<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="fr-fr" xmlns="http://www.w3.org/2005/Atom"><title>jtremesay.org - last articles</title><link href="https://jtremesay.org/atom.xml" rel="alternate"/><link href="http://testserver/atom.xml" rel="self"/><id>https://jtremesay.org/atom.xml</id><updated>2024-11-28T20:00:00+02:00</updated><entry><title>JSSG django app</title><link href="https://jtremesay.org/posts/jssg-django-app.html" rel="alternate"/><published>2024-11-28T20:00:00+02:00</published><id>https://jtremesay.org/posts/jssg-django-app.html</id><summary type="html">&lt;p&gt;Disclaimer: article écrit avec l'assistance de GitHub Copilot.&lt;/p&gt;

&lt;p&gt;TL;DR: &lt;a href="https://github.com/jtremesay/jssg"&gt;JSSG&lt;/a&gt; le générateur de site statique est maintenant disponible sous forme d'app django.&lt;/p&gt;

&lt;h2&gt;#MyLife&lt;/h2&gt;

&lt;p&gt;Vu que ça fait plusieurs mois que je n'ai pas eu à toucher à JSSG (Jtremesay Static Site Generator) alors que dans l'interval j'ai fait plusieurs trucs rigolo sur le site, c'est un signe qu'il était plutôt stable. Je vais donc faire un point dessus.&lt;/p&gt;

&lt;p&gt;Donc, JSSG, c'est qu'est-ce que c'est que quoi ?&lt;/p&gt;

&lt;p&gt;C'est mon générateur de site statique à moi parce que j'ai des besoins de gros relous et que j'ai un gros syndrome de Not Invented Here.&lt;/p&gt;

&lt;p&gt;Mes besoins de gros relous :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;le résultat est un site statique, facilement délivrable par un serveur web lambda et deployable virtuellement partout; y compris en utilisant un bon vieux ftp sur un hébergeur gratuit si il le faut !&lt;/li&gt;
&lt;li&gt;j'aime pas écrire du html ou me répéter, me faudra un truc qui me permette de prendre du contenu quelque soit son origine, généralement markdown. Mais qui n'aime pas exécuter un flow compliqué orchestré par make pour générer une page spécifique du site parce que l'automatisation a ses raisons que la raison ignore ? (oui, fut un temps, ce &lt;a href="/posts/chaines-youtubes.html"&gt;post&lt;/a&gt; était généré automatiquement à partir d'un export OPML de mon aggrégateur RSS via des scripts et un makefile :D)&lt;/li&gt;
&lt;li&gt;maintenant que j'ai découvert les joies de la génération procédurale 2D/3D en typescript, j'veux pouvoir facilement intégrer du front moderne dans le site. donc faut supporter &lt;code&gt;vite&lt;/code&gt; ou whatever est le truc à la mode pour compiler du typescript en js; mais pas de single page application. Le JS doit être chargé par page au besoin. &lt;/li&gt;
&lt;li&gt;doit avoir une gestion des fichiers statiques (images, css, js, etc.). Par gestion, j'entends collection des statics, transformations nécessaires, et intégration dans le site généré&lt;/li&gt;
&lt;li&gt;gestion des liens internes et détection des liens morts: le générateur doit détecter au build time les liens cassés pointant vers des pages ou fichiers inexistants, ainsi que les medias, les scripts et autres css&lt;/li&gt;
&lt;li&gt;je veux organiser mon contenu comme je veux, pas comme le générateur le veut. Donc je veux pouvoir mettre mes fichiers markdown où je veux, avec la structure de répertoires que je veux, et que le générateur s'adapte à ça&lt;/li&gt;
&lt;li&gt;je veux que ça soit facile à utiliser en local : je tape mon contenu, je sauvegarde, je rafraîchis la page (ou pas d'ailleurs, c'est bien aussi le live reload), et pouf, je vois mes changements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JSSG a connus plusieurs itérations. Tel le &lt;a href="https://fr.wikipedia.org/wiki/Bateau_de_Th%C3%A9s%C3%A9e"&gt;bateau de Thésée&lt;/a&gt;, il a d'ailleurs était réécrit from scratch plusieurs fois.&lt;/p&gt;

&lt;p&gt;Ce fut originellement une app flask. La partie utilisation était bien; mais j'ai pas réussi à en faire un générateur static. &lt;/p&gt;

&lt;p&gt;Puis ce fut un assemblage créatif de python, de jinja et de makefile. Ça faisait un contenu static comme je le voulais, mais c'était chiants à utiliser en local.&lt;/p&gt;

&lt;p&gt;Au &lt;a href="https://sereema.com"&gt;taf&lt;/a&gt;, on utilise Django. Et après 4 ans à bosser intensivement avec, j'aime beaucoup. c'est vraiment ma plateforme préféré pour faire dev du web en local. Y'a tout ce qu'il faut : du templating, du live reload, de l'extensibilité, des outils de dev, etc. Sauf que Django c'est pas fait pour générer du contenu statique. Qu'à cela ne tienne. J'ai donc bricolé un bout de code pour collecter les .md des pages et posts, ainsi qu'une commande Django qui exécute les vues des pages et des posts avec tous les .md trouvé et enregistre le résultat dans un répertoire de sortie. Django s'occupe comme un grand des statict avec &lt;code&gt;./manage.py collectstatic&lt;/code&gt;. Ainsi, on se retrouve avec toute la puissance et flexibilité de Django pour développer son site, et un site static tout simple en sortie !&lt;/p&gt;

&lt;p&gt;En plus, grace à Django, vous pouvez faire des trucs débilement compliqué tel qu'utiliser une base de données. Wait, une base de données avec un site statique ? Non ! Une base de données utilisé pendant le dev pour générer des pages dynamiques qui seront ensuite capturés au build time :D&lt;/p&gt;

&lt;p&gt;Donc si c'est votre truc, vous pouvez tout a fait envisager de transformer JSSG en un CMS pour usage local en profitant des possibilités du backoffice de Django et de la persistance du contenu dans une base de données. Puis utiliser cette DB pour générer la version statique de votre site.&lt;/p&gt;

&lt;p&gt;Puis j'ai découvert &lt;a href="https://django-distill.com/"&gt;Distill&lt;/a&gt; qui permet de générer des pages statiques à partir de vues Django. C'est sans aucun scrupule que je leur ai délégué la génération des pages statiques. J'ai donc pu me concentrer sur la partie ingestion du contenu.&lt;/p&gt;

&lt;p&gt;Depuis, quelqu'un a manifesté son intérêt pour utiliser JSSG. Mais jusque là le code de JSSG était lié de manière trop incestueuse à celui de jtremesay.org. J'ai donc décidé de le séparer dans un repo à part. Mais c'était un peu relou d'avoir jssg aussi mêlé au contenu et devoir maintenir les deux versions, la vanilla et la mienne.&lt;/p&gt;

&lt;p&gt;Ce qui nous mene à la version actuelle de JSSG et aussi un changement de paradigme. JSSG N'EST PAS un générateur de site statique. C'est un composant (app) Django à ajouter à votre projet pour simplifier la statification de ce dernier. Ou vu autrement, c'est une plateforme de développement web moderne permettant la création de stite statique. C'est vous qui voyez :shrug:&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;JSSG est installable via pip:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;git+https://github.com/jtremesay/jssg.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Maintenant, il vous faut ajouter &lt;code&gt;jssg&lt;/code&gt; à votre &lt;code&gt;INSTALLED_APPS&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;jssg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Vous aurez aussi besoin de définir les variables suivantes dans votre &lt;code&gt;settings.py&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;JSSG_RSS_LINK: str&lt;/code&gt;: L'url de votre site, utilisé dans le flux RSS&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JSSG_RSS_TITLE: str&lt;/code&gt;: la description de votre site, utilisé dans le flux RSS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mettez à jour votre &lt;code&gt;urls.py&lt;/code&gt; pour déclarer les urls de JSSG:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jssg.utils&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jssg_urls&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Automatically declare urls for pages, posts and rss feed&lt;/span&gt;
&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;jssg_urls&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Fonctionnement&lt;/h2&gt;

&lt;p&gt;JSSG va chercher un dossier &lt;code&gt;content&lt;/code&gt; dans les applications de votre projet. De cette manière, vous pouvez facilement organiser votre contenu comme vous le souhaitez, en plusieurs applications si cela est nécessaire pour s'adapter à votre complexité. Par exemple pour définir des sous-sites.&lt;/p&gt;

&lt;p&gt;Dans ce dossier &lt;code&gt;content&lt;/code&gt;, vous devez avoir un dossier &lt;code&gt;posts&lt;/code&gt; et un dossier &lt;code&gt;pages&lt;/code&gt;. Les fichiers markdown de ces dossiers seront traités pour générer les pages et les posts de votre site.&lt;/p&gt;

&lt;p&gt;Exemple d'application utilisant JSSG :&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;yousapp/
├── apps.py
├── content
│   ├── pages
│   │   ├── doom.md
│   │   └── index.md
│   └── posts
│       ├── 20230531_helloworld.md
│       └── 20250231_DOOM.md
├── front
│   ├── doom
│   │   ├── engine.ts
│   │   ├── lumps.ts
│   │   ├── main.ts
│   │   ├── readers.ts
│   │   ├── types.ts
│   │   ├── view2d.ts
│   │   └── view3d.ts
│   ├── main
│   │   ├── doom.ts
│   │   └── mytsapp.ts
│   └── vite-env.d.ts
├── __init__.py
├── migrations
│   └── __init__.py
├── static
│   └── yousapp
│       ├── doom
│       │   └── doom1.wad
│       ├── files
│       │   └── my_file.txt
│       ├── images
│       │   └── myimage.jpg
│       ├── css
│       │   └── mycss.css
│       ├── theme
│       │   └── favicon.ico
├── storage.py
└── templates
    ├── base.html
    ├── blocks
    │   ├── footer.html
    │   └── header.html
    ├── page.html
    └── post.html
 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Chaque fichier markdown trouvé sera rendu accessible par Django via une vue et des routes. Ainsi, vous pouvez accéder à votre contenu en local en utilisant les urls générées par Django. JSSG génère aussi une route pour un flux RSS de votre contenu.&lt;/p&gt;

&lt;p&gt;Si vous souhaitez gérez vous même les urls de votre contenu, vous pouvez désactiver l'autodiscovery et déclarer les routes à la main tout en profitant des vues de JSSG pour générer le contenu.&lt;/p&gt;

&lt;p&gt;Vous pouvez même directement utiliser &lt;a href="https://django-distill.com/"&gt;Distill&lt;/a&gt; pour déclarer les routes de vos propres vues pour générer des pages statiques à partir de celles-ci.&lt;/p&gt;

&lt;p&gt;Le contenu des fichiers markdown est traité par le moteur de template de Django avant d'être rendu en html. Vous pouvez donc utiliser toute la puissance du &lt;a href="https://docs.djangoproject.com/en/5.1/ref/templates/language/"&gt;langage de template&lt;/a&gt; de Django.&lt;/p&gt;

&lt;p&gt;Vous pouvez utiliser &lt;code&gt;{% static %}&lt;/code&gt; et &lt;code&gt;{% url %}&lt;/code&gt; pour référencer vos fichiers statiques et vos urls Django dans le contenu.&lt;/p&gt;

&lt;h2&gt;Templates&lt;/h2&gt;

&lt;p&gt;Si vous utilisez les vues de JSSG, vous devez fournir les templates &lt;code&gt;post.html&lt;/code&gt; et &lt;code&gt;page.html&lt;/code&gt;. Ils doivent être dans le dossier &lt;code&gt;templates&lt;/code&gt; d'une de vos applications, n'importe laquelle.&lt;/p&gt;

&lt;p&gt;Dans ces template, vous aurez accès à la variable &lt;code&gt;object&lt;/code&gt; qui contiendra respectivement un objet &lt;code&gt;Post&lt;/code&gt; ou &lt;code&gt;Page&lt;/code&gt; selon le type de contenu à afficher.&lt;/p&gt;

&lt;p&gt;Exemple de template &lt;code&gt;post.html&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% extends &amp;quot;base.html&amp;quot; %}

{% block &amp;quot;content&amp;quot; %}
Publié le {{ object.timestamp|date:&amp;quot;Y-m-d&amp;quot; }}.

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ object.title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{{ object.content_md|safe }}
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Exemple de template &lt;code&gt;page.html&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% extends &amp;quot;base.html&amp;quot; %}

{% block &amp;quot;content&amp;quot; %}
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ object.title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{{ object.content_md|safe }}
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Comme ces vos propres templates, vous êtes libres de faire absolument tous ce que vous voulez.&lt;/p&gt;

&lt;h2&gt;Contenu&lt;/h2&gt;

&lt;p&gt;Pour l'instant, JSSG ne supporte que le markdown. Il reprend la convention "front matter" et cherche un header en entête du fichier markdown. Ce header doit être délimité par &lt;code&gt;---&lt;/code&gt; et contenir des clés/valeurs au format &lt;code&gt;key: value&lt;/code&gt;. Les clés supportées dépendent du type de contenue. Après le header, le contenu du fichier est traité comme du un template django contenant du markdown.&lt;/p&gt;

&lt;h3&gt;Document&lt;/h3&gt;

&lt;p&gt;C'est une abstraction représentant un contenu et ses metadata et destiné à être utilisé pour construire une page. Aucun à priori n'est fait sur le type de contenu ou ses metadata.&lt;/p&gt;

&lt;p&gt;Dans le contenu, vous avez accès à l'objet &lt;code&gt;posts&lt;/code&gt; contenant la list des &lt;code&gt;Post&lt;/code&gt; ordonnés par date de publication décroissante. Principalement utile pour générer des liens vers d'autres posts, tel que sur la page d'accueil.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;---
foo: bar
&lt;span class="gu"&gt;iiii: jjjj&lt;/span&gt;
&lt;span class="gu"&gt;---&lt;/span&gt;
blabla
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Vous pouvez charger un document de la manière suivante:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jssg.models&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Document&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;path/to/file.md&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# relatif à n&amp;#39;importe quel dossier `content`&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# le chemin du fichier, utilisé pour générer l&amp;#39;url. N&amp;#39;a pas besoin d&amp;#39;être un vrai fichier existant réellement. Utile pour créer dynamiquement des pages&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# les metadata du document (le contenu du header)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# le contenu du document tel que lu depuis le fichier&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# le contenu du document après traitement par le moteur de template de Django et markdown2html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3&gt;Page&lt;/h3&gt;

&lt;p&gt;Une page est un document (la classe &lt;code&gt;Page&lt;/code&gt; hérite de &lt;code&gt;Document&lt;/code&gt;) qui représente une page du site. Si l'autodiscovery est activé, JSSG cherche les fichiers markdown dans le dossier &lt;code&gt;content/pages&lt;/code&gt; de chaque application de votre projet.&lt;/p&gt;

&lt;p&gt;Header:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;title: str&lt;/code&gt;: le titre de la page, obligatoire&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slug: str&lt;/code&gt;: le slug de la page, utilisé pour générer l'url. Déterminé automatiquement si non fourni&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chaque page est accessible via la route &lt;code&gt;/pages/&amp;lt;slug&amp;gt;.html&lt;/code&gt; nommée &lt;code&gt;page&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Exemple de page :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;---
title: Hello, world!
&lt;span class="gu"&gt;slug: hello&lt;/span&gt;
&lt;span class="gu"&gt;---&lt;/span&gt;
A [&lt;span class="nt"&gt;link  to a page&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;page&amp;#39; &amp;#39;other-page&amp;#39; }&lt;/span&gt;).

List of posts: 

{% for post in posts %}
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;{{ post.title }}&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;post&amp;#39; post.slug %}&lt;/span&gt;)
{% endfor %}

An image:
![&lt;span class="nt"&gt;A cat&lt;/span&gt;](&lt;span class="na"&gt;{% static &amp;#39;yourapp/images/cat.jpg&amp;#39; %}&lt;/span&gt;)

A js app:
&amp;lt;div id=&amp;quot;myjsapp&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;script src=&amp;quot;{% static &amp;#39;yourapp/js/myjsapp.js&amp;#39; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;

A table generated from a csv:
{% csv_table &amp;#39;yourapp/files/data.csv&amp;#39; %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Exemple d'objet &lt;code&gt;Page&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jssg.models&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Page&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.urls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;

&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;path/to/file.md&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# relatif à n&amp;#39;importe quel dossier `content/pages`&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# &amp;quot;Hello, world!&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# &amp;quot;hello&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;page&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="c1"&gt;# &amp;quot;/pages/path/to/hello.html&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3&gt;Post&lt;/h3&gt;

&lt;p&gt;Un post est une page (la classe &lt;code&gt;Post&lt;/code&gt; hérite de &lt;code&gt;Page&lt;/code&gt;) qui représente un article du site. Un flux RSS est généré automatiquement pour les posts. Si l'autodiscovery est activé, JSSG cherche les fichiers markdown dans le dossier &lt;code&gt;content/posts&lt;/code&gt; de chaque application de votre projet.&lt;/p&gt;

&lt;p&gt;Header :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;title: str&lt;/code&gt;: le titre du post, obligatoire&lt;/li&gt;
&lt;li&gt;&lt;code&gt;date: str&lt;/code&gt;: la date de publication, obligatoire. Format ISO 8601&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slug: str&lt;/code&gt;: le slug du post, utilisé pour générer l'url. Déterminé automatiquement si non fourni&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chaque post est accessible via la route &lt;code&gt;/posts/&amp;lt;slug&amp;gt;.html&lt;/code&gt; nommée &lt;code&gt;post&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Exemple de post :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;---
title: Hello, world
&lt;span class="gu"&gt;date: 2023-05-31T20:00+02:00&lt;/span&gt;
&lt;span class="gu"&gt;---&lt;/span&gt;
bla bla bla
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Exemple d'objet &lt;code&gt;Post&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jssg.models&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django.urls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;

&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;path/to/file.md&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# relatif à n&amp;#39;importe quel dossier `content/posts`&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# &amp;quot;Hello, world!&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# &amp;quot;hello-world&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;page&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="c1"&gt;# &amp;quot;/posts/path/to/hello-world.html&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Vues&lt;/h2&gt;

&lt;p&gt;JSSG fournit des vues pour afficher les pages et les posts. Vous pouvez les utiliser telles quelles ou les étendre pour ajouter des fonctionnalités.&lt;/p&gt;

&lt;h2&gt;Fichiers statiques&lt;/h2&gt;

&lt;p&gt;C'est du django standard. Vous mettez vos fichiers statiques dans un dossier &lt;code&gt;static&lt;/code&gt; à la racine de vos applications et vous les référencez dans vos templates avec &lt;code&gt;{% static %}&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Liens internes&lt;/h2&gt;

&lt;p&gt;Comme JSSG permet d'utiliser le système d'url de Django, y compris dans le contenu, il est facile de faire des liens internes. JSSG détecte les liens cassés au build time et vous prévient si un lien pointe vers un fichier ou une page qui n'existe pas.&lt;/p&gt;

&lt;h2&gt;Génaration du site statique&lt;/h2&gt;

&lt;p&gt;Pour générer le site statique, vous pouvez utiliser la commande &lt;code&gt;distill-local&lt;/code&gt; fournie par distill. Elle va collecter les fichiers statiques et générer les pages statiques dans un dossier de sortie. Vous pouvez ensuite servir ce dossier avec n'importe quel serveur web.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;./manage.py&lt;span class="w"&gt; &lt;/span&gt;distill-local&lt;span class="w"&gt; &lt;/span&gt;--collectstatic&lt;span class="w"&gt; &lt;/span&gt;dist
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Extra: typescript&lt;/h2&gt;

&lt;p&gt;Ce n'est pas intégré à JSSG, mais grace à &lt;a href="https://pypi.org/project/django-vite/"&gt;django-vite&lt;/a&gt;, il est très facile d'intégrer typescript dans votre projet. &lt;/p&gt;

&lt;p&gt;Pour cela, vous devez installer &lt;code&gt;django-vite&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;django-vite
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Puis ajouter &lt;code&gt;vite&lt;/code&gt; à votre &lt;code&gt;INSTALLED_APPS&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;django_vite&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;jssg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Puis définir la variable &lt;code&gt;DJANGO_VITE&lt;/code&gt; dans votre &lt;code&gt;settings.py&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;DJANGO_VITE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dev_mode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Vous pouvez utilisez ce &lt;code&gt;vite.config.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;defineConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;vite&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;globSync&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;glob&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/static/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;manifest.json&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;outDir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;./static&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;rollupOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;globSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;*/front/main/*.ts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;À partir de là, &lt;code&gt;vite&lt;/code&gt; va traiter chaque fichier &lt;code&gt;.ts&lt;/code&gt; dans les dossiers &lt;code&gt;front/main&lt;/code&gt; de vos applications et les compiler en &lt;code&gt;.js&lt;/code&gt; dans le dossier &lt;code&gt;static&lt;/code&gt; de votre projet. De cette manière, vous pouvez profiter du dev web moderne (imports, gestion des dépendances par pinage, vérification du code par typescript, transpilation vers du vieux js pour supporter un max de navigateur…) sans avoir à subir la bullshiterie du SPA et intégrer mes js à l'ancienne page par page comme le gros boomer que je suis.&lt;/p&gt;

&lt;p&gt;Ex pour charger votre app typescript top moumoute dans une page:&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;---
title: Hello, typescript!
&lt;span class="gu"&gt;slug: hello-ts&lt;/span&gt;
&lt;span class="gu"&gt;---&lt;/span&gt;
{% load django_vite %}

{% vite_asset &amp;#39;yourapp/front/main/mytsapp.ts&amp;#39; %}
&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Un petit coup de &lt;code&gt;npm run dev&lt;/code&gt; &amp;amp;&amp;amp; &lt;code&gt;./manage.py runserver&lt;/code&gt;, un petit tour sur &lt;code&gt;http://localhost:8000/pages/hello-ts.html&lt;/code&gt; et tadam; Vous pouvez faire vos délire front les plus créatifs, dans du markdown, servi par django, et destiné à finir dans un site statique. C'est pas beau ça ? #kamoulox&lt;/p&gt;

&lt;p&gt;Et pour le site final ? &lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;build
$&lt;span class="w"&gt; &lt;/span&gt;./manage.py&lt;span class="w"&gt; &lt;/span&gt;distill-local&lt;span class="w"&gt; &lt;/span&gt;--collectstatic&lt;span class="w"&gt; &lt;/span&gt;dist
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Simple et efficace.&lt;/p&gt;

&lt;h2&gt;Extra: Docker&lt;/h2&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:current-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;front&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;package.json&lt;span class="w"&gt; &lt;/span&gt;package-lock.json&lt;span class="w"&gt; &lt;/span&gt;./
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;install
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tsconfig.json&lt;span class="w"&gt; &lt;/span&gt;vite.config.ts&lt;span class="w"&gt; &lt;/span&gt;./
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;myapp/front/&lt;span class="w"&gt; &lt;/span&gt;myapp/front/
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;site&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;requirements.txt&lt;span class="w"&gt; &lt;/span&gt;./
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-Ur&lt;span class="w"&gt; &lt;/span&gt;requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;./
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;proj/&lt;span class="w"&gt; &lt;/span&gt;proj/
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;myapp/&lt;span class="w"&gt; &lt;/span&gt;myapp/
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--from&lt;span class="o"&gt;=&lt;/span&gt;front&lt;span class="w"&gt; &lt;/span&gt;/code/static/&lt;span class="w"&gt; &lt;/span&gt;static/
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./manage.py&lt;span class="w"&gt; &lt;/span&gt;distill-local&lt;span class="w"&gt; &lt;/span&gt;--collectstatic&lt;span class="w"&gt; &lt;/span&gt;--force&lt;span class="w"&gt; &lt;/span&gt;dist

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;nginx:mainline-alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--from&lt;span class="o"&gt;=&lt;/span&gt;site&lt;span class="w"&gt; &lt;/span&gt;/code/dist/&lt;span class="w"&gt; &lt;/span&gt;/usr/share/nginx/html/
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;mysite&lt;span class="w"&gt; &lt;/span&gt;.
$&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;:80&lt;span class="w"&gt; &lt;/span&gt;mysite
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Extra: Docker-compose pour déployement avec docker swarm + traefik&lt;/h2&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;3.8&amp;quot;&lt;/span&gt;
&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;jtremesay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;killruana/jtremesay.org:main&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;8003:80&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik_public&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.enable=true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.middlewares.jtremesay-compress.compress=true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.entrypoints=websecure&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.middlewares=jtremesay-compress&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.rule=Host(`jtremesay.org`)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Host(`slaanesh.org`)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.service=jtremesay&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.tls.certresolver=leresolver&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.services.jtremesay.loadbalancer.server.port=80&amp;quot;&lt;/span&gt;

&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;traefik_public&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;external&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Je vends le script de CI/CD Github Actions et le docker-compose de traefik pour une giraffe de bière.&lt;/p&gt;

&lt;h2&gt;Évolutions futures&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;le rendre plus configurable et flexible. Il est actuellemnent très lié à mes besoins et mon organisation de contenu&lt;/li&gt;
&lt;li&gt;intégrer le contenu avec l'orm django (aka faire hérieter &lt;code&gt;Document&lt;/code&gt; de &lt;code&gt;models.Model&lt;/code&gt;) pour donner la possibilité JSSG en tant que CMS plus traditionnel. Y'a plein d'endroit où ça serait cool de pouvoir utiliser un queryset pour lister des pages ou charger un post&lt;/li&gt;
&lt;li&gt;fournir un nouveau type de &lt;a href="https://docs.djangoproject.com/en/5.1/ref/databases/#subclassing-the-built-in-database-backends"&gt;db backend&lt;/a&gt; pour django qui permettrait de rendre directement accessible à l'orm les trucs dans le dossier &lt;code&gt;content&lt;/code&gt; pour une utilisation plus traditionnelle de django sans avoir à charger les posts dans une db. Vous pouvez utiliser l'ORM de Django ET avoir un contenu toujours à jour parce que directement lu depuis le disque :D. &lt;code&gt;Post.objects.filter(title__icontains="hello")&lt;/code&gt; directement mappé sur &lt;code&gt;Path("content/posts").rglob("*.md")&lt;/code&gt; :D &lt;code&gt;post.save()&lt;/code&gt; ? Paf ! &lt;code&gt;content/posts/bla.md&lt;/code&gt; est crée !&lt;/li&gt;
&lt;li&gt;intégrer django-vite directement dans JSSG ? C'est quand meme un truc un peu relou qui a plus de sens à être appliqué sur le projet qui utilise jssg&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Pour &lt;em&gt;mon&lt;/em&gt; usage, JSSG fait le taf. Comme dit en intro, j'ai pas eu à y toucher depuis plusieurs mois. malgré les ajouts de conneries sur le site. Et ce fut un régal de faire ces conneries (&lt;a href="/pages/textnodes.html"&gt;text 2 particules !&lt;/a&gt; ! &lt;a href="/pages/doom.html"&gt;Doom&lt;/a&gt; ! &lt;a href="/pages/crankshaft.html"&gt;Crankshaft&lt;/a&gt; !)&lt;/p&gt;

&lt;p&gt;Ça vous permet de transformer simplement votre projet Django tout moderne, complexe et hackable en bon vieux site statique ennuyeux qui juste marche. Et c'est disponible sous licence GNU AGPL dans toutes les bonnes cremeries libristes. Que demander de plus ?&lt;/p&gt;
</summary></entry><entry><title>JSSG + django-distill</title><link href="https://jtremesay.org/posts/jssg-django-distill.html" rel="alternate"/><published>2024-01-30T20:00:00+02:00</published><id>https://jtremesay.org/posts/jssg-django-distill.html</id><summary type="html">&lt;p&gt;Petite mise à jour de JSSG.&lt;/p&gt;

&lt;p&gt;Depuis l'&lt;a href="/posts/jssg-django.html"&gt;épisode précédent&lt;/a&gt;, j'ai découvert &lt;a href="https://django-distill.com/"&gt;django-distill&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;django-distill is an open source, MIT licenced static site generator library for the Django web development framework and is designed to integrate into existing Django projects to allow some, or all of the content in the site to be written out as static pages. Integration is simple and the level of static site generation is up to the project. django-distill integrates with Django, rather than requiring projects to be written for django-distill.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;L'intégration fut très facile. Essentiellement mettre à jour le &lt;code&gt;urls.py&lt;/code&gt; :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django_distill&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;distill_path&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jssg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jssg.models&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_pages&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;slug&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_glob&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;slug&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_glob&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;


&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;distill_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IndexView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distill_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;index.html&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;distill_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;atom.xml&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostFeedsView&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;atom_feed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;distill_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;pages/&amp;lt;slug:slug&amp;gt;.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;page&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;distill_func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;get_pages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;distill_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;posts/&amp;lt;slug:slug&amp;gt;.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;distill_func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;get_posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Un petit coup de &lt;code&gt;./manage.py distill-local --collectstatic dist&lt;/code&gt; et pouf, vos assets sont collectés, et les pages html et le flux atom généré.&lt;/p&gt;

&lt;p&gt;Ça m'a permis de supprimer pas mal de code :D&lt;/p&gt;

&lt;p&gt;De plus, c'est aussi utilisable avec un django en prod pour avoir une partie du site en static et le reste en dynamique. Je n'en ai pas l'usage immédiat, mais c'est toujours bon à savoir :)&lt;/p&gt;
</summary></entry><entry><title>JSSG Django</title><link href="https://jtremesay.org/posts/jssg-django.html" rel="alternate"/><published>2024-01-05T12:30:00+02:00</published><id>https://jtremesay.org/posts/jssg-django.html</id><summary type="html">&lt;p&gt;Encore une nouvelle itération de JSSG, mon générateur de sites statiques propulsant ce site afin de mieux répondre à mes besoins.&lt;/p&gt;

&lt;p&gt;Pour rappel, mes besoins étaient :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;je veux un site statique en plain old html&lt;/li&gt;
&lt;li&gt;je ne veux pas écrire de html&lt;/li&gt;
&lt;li&gt;je veux que le déployement soit en mode "fire &amp;amp; forget" (CI/CD on push)&lt;/li&gt;
&lt;li&gt;je veux toujours faire des trucs tordus qui ne rentrent pas dans les cases des solutions clés en main existantes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dans l'épisode &lt;a href="/posts/jssg.html"&gt;précédent&lt;/a&gt;, j'avais bricolé un &lt;a href="https://getpelican.com/"&gt;pelican&lt;/a&gt; du pauvre à truc à base de python + restructuredtext + jinja pour gérer le contenue et vite + typescript pour le front.&lt;/p&gt;

&lt;p&gt;Ça marche super bien pour générer la version finale du site. Mais c'était super désagréable d'écrire du contenu ou de travailler sur les animations en typescript. La faute à l'absence d'un auto-rebuild à la volée pour simplifier la vie. Mais c'est super relou à coder ça :P&lt;/p&gt;

&lt;p&gt;Retour à la planche à dessin, avec cette contrainte en plus :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;il faut que ça soit très facile de travailler sur le contenu du site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Après quelques PoC infructueux, dont un basé sur &lt;a href="https://ninja-build.org/"&gt;ninja&lt;/a&gt; parce que pourquoi pas, j'eu une épiphanie : il me fallait &lt;a href="https://www.djangoproject.com/"&gt;django&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En effet, django est absolument génial en ce qui concerne le développement web :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;./manage.py&lt;span class="w"&gt; &lt;/span&gt;runserver
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Et pouf, j'ai un environnement de dev très confortable, l'accès à plein de modules et bibliothèques, de l'autoreload automatique, et l'intégration de vite/typescript via le plugin &lt;a href="https://github.com/protibimbok/django-vite-plugin"&gt;django-vite-plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sauf que Django sert à propulser des webapps WSGI/ASGI, pas à générer des sites statiques.&lt;/p&gt;

&lt;p&gt;Qu'à cela ne tienne. Y'a qu'à ajouter une commande qui parcoure toutes les routes de l'application et appellent les vues sous-jacentes pour générer le HTML et l'enregistrer dans des fichiers. C'est moche mais c'est pas stupide si ça marche.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;build
&lt;span class="gp"&gt;$ &lt;/span&gt;./manage.py&lt;span class="w"&gt; &lt;/span&gt;collectstatic&lt;span class="w"&gt; &lt;/span&gt;--no-input
&lt;span class="gp"&gt;$ &lt;/span&gt;./manage.py&lt;span class="w"&gt; &lt;/span&gt;gensite
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Et pouf, j'ai mon front transpilé, mes statics traités, et mon html généré.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="go"&gt;dist/&lt;/span&gt;
&lt;span class="go"&gt;├── atom.xml&lt;/span&gt;
&lt;span class="go"&gt;├── index.html&lt;/span&gt;
&lt;span class="go"&gt;├── pages&lt;/span&gt;
&lt;span class="go"&gt;│   ├── a-propos.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── cgi.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── crankshaft.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── cv.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── pi-monte-carlo.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── raycaster.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── scadaplayer.html&lt;/span&gt;
&lt;span class="go"&gt;│   └── tris-animes.html&lt;/span&gt;
&lt;span class="go"&gt;├── posts&lt;/span&gt;
&lt;span class="go"&gt;│   ├── chaines-youtubes.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── django-vuejs-multipage-application.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── hello-world.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── jssg-django.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── jssg.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── migration-de-gandi-mail-vers-mailo.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── news.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── pelican.html&lt;/span&gt;
&lt;span class="go"&gt;│   ├── terraform-oracle-cloud.html&lt;/span&gt;
&lt;span class="go"&gt;│   └── tris-animes.html&lt;/span&gt;
&lt;span class="go"&gt;└── static&lt;/span&gt;
&lt;span class="go"&gt;    ├── assets&lt;/span&gt;
&lt;span class="go"&gt;    │   ├── cgi-d89f4043.js&lt;/span&gt;
&lt;span class="go"&gt;    │   ├── crankshaft-e50e7a32.js&lt;/span&gt;
&lt;span class="go"&gt;    │   ├── pimontecarlo-ecd5d6d5.js&lt;/span&gt;
&lt;span class="go"&gt;    │   ├── raycaster-d4ad5a5e.js&lt;/span&gt;
&lt;span class="go"&gt;    │   ├── scadaplayer-f0555500.js&lt;/span&gt;
&lt;span class="go"&gt;    │   └── sorts-02a45577.js&lt;/span&gt;
&lt;span class="go"&gt;    ├── jssg&lt;/span&gt;
&lt;span class="go"&gt;    │   ├── files&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── jonathan.tremesaygues_at_slaanesh.org.pub.043beb42ea7d.asc&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── jonathan.tremesaygues_at_slaanesh.org.pub.043beb42ea7d.asc.br&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── jonathan.tremesaygues_at_slaanesh.org.pub.043beb42ea7d.asc.gz&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── jonathan.tremesaygues_at_slaanesh.org.pub.asc&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── jonathan.tremesaygues_at_slaanesh.org.pub.asc.br&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── jonathan.tremesaygues_at_slaanesh.org.pub.asc.gz&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── youtube_channels.csv&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── youtube_channels.csv.br&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── youtube_channels.csv.gz&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── youtube_channels.d4eefe1fdb68.csv&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── youtube_channels.d4eefe1fdb68.csv.br&lt;/span&gt;
&lt;span class="go"&gt;    │   │   └── youtube_channels.d4eefe1fdb68.csv.gz&lt;/span&gt;
&lt;span class="go"&gt;    │   ├── images&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── mailo_access_to_spaces.704ca18ad68e.jpg&lt;/span&gt;
&lt;span class="go"&gt;    │   │   └── mailo_access_to_spaces.jpg&lt;/span&gt;
&lt;span class="go"&gt;    │   ├── pygments&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── monokai.569e3254f732.css&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── monokai.569e3254f732.css.br&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── monokai.569e3254f732.css.gz&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── monokai.css&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── monokai.css.br&lt;/span&gt;
&lt;span class="go"&gt;    │   │   └── monokai.css.gz&lt;/span&gt;
&lt;span class="go"&gt;    │   ├── theme&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── cc_byncsa.44c7d2e04342.png&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── cc_byncsa.png&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── favicon-16x16.e4b7b2c44b28.png&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── favicon-16x16.png&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── favicon-32x32.57254c655e62.png&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── favicon-32x32.png&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── favicon.fac9193e2b71.ico&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── favicon.fac9193e2b71.ico.br&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── favicon.fac9193e2b71.ico.gz&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── favicon.ico&lt;/span&gt;
&lt;span class="go"&gt;    │   │   ├── favicon.ico.br&lt;/span&gt;
&lt;span class="go"&gt;    │   │   └── favicon.ico.gz&lt;/span&gt;
&lt;span class="go"&gt;    │   └── tol&lt;/span&gt;
&lt;span class="go"&gt;    │       ├── analyse_grammaticale_francais_vs_pros_large.daa0891c22db.jpg&lt;/span&gt;
&lt;span class="go"&gt;    │       ├── analyse_grammaticale_francais_vs_pros_large.jpg&lt;/span&gt;
&lt;span class="go"&gt;    │       ├── analyse_grammaticale_francais_vs_pros_small.4e91758daeea.jpg&lt;/span&gt;
&lt;span class="go"&gt;    │       └── analyse_grammaticale_francais_vs_pros_small.jpg&lt;/span&gt;
&lt;span class="go"&gt;    ├── manifest.json&lt;/span&gt;
&lt;span class="go"&gt;    └── staticfiles.json&lt;/span&gt;

&lt;span class="go"&gt;11 directories, 64 files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Le contenu&lt;/h2&gt;

&lt;p&gt;Le contenu (pages et posts) est maintenant écrit en markdown. La précédente version de JSSG utilisait restructuredtext, mais je me suis rendu compte à l'usage que je détestais :D&lt;/p&gt;

&lt;p&gt;Chaque document commence par un block de méta-data encadrés par des &lt;code&gt;---&lt;/code&gt;. Il me sert notamment à renseigner le nom ou le slug de la page, et la date de publication des posts.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;---
&lt;span class="gu"&gt;title: Ma super page!&lt;/span&gt;
&lt;span class="gu"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## Un titre&lt;/span&gt;

Bla bla bla
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;J'ai aussi accès à toute la puissance du django, ce qui permet de faire des trucs rigolos.&lt;/p&gt;

&lt;p&gt;Les urls internes sont gérés par django. Comme ça, tous lien mort est automatiquement détecté à la génération, permettant d'avoir un semblant de cohérence du contenu au cours du temps.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;Images générés par ordinateur&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;page&amp;#39; &amp;#39;cgi&amp;#39; %}&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Génération de la liste des articles :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% for post in posts %}
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;{{ post.timestamp|date:&amp;quot;Y-m-d&amp;quot; }}&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;post&amp;#39; post.slug %}&lt;/span&gt;): {{ post.title }}{% endfor %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Inclusion d'une web app :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% vite_asset &amp;#39;jtremesay/front/main/crankshaft.ts&amp;#39; %}
{% static &amp;#39;jtremesay/jss/css/crankshaft.css&amp;#39; %}
&amp;lt;div id=&amp;quot;crankshaft-app&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Pour un exemple plus complet, voila la page d’accueil :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;---
title: Bienvenue sur mon site !
&lt;span class="gu"&gt;slug: index&lt;/span&gt;
&lt;span class="gu"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## Pages&lt;/span&gt;

&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;Images générés par ordinateur&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;page&amp;#39; &amp;#39;cgi&amp;#39; %}&lt;/span&gt;)
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;Un raycaster façon Wolfenstein3D en pur typescript&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;page&amp;#39; &amp;#39;raycaster&amp;#39; %}&lt;/span&gt;)
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;Algorithmes de tris animés&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;page&amp;#39; &amp;#39;tris-animes&amp;#39; %}&lt;/span&gt;)
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;Calculer Pi au casino&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;page&amp;#39; &amp;#39;pi-monte-carlo&amp;#39; %}&lt;/span&gt;)
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;Scada player&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;page&amp;#39; &amp;#39;scadaplayer&amp;#39; %}&lt;/span&gt;) : un outil pour visualiser les données issues d&amp;#39;une éolienne
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;Crankshaft&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;page&amp;#39; &amp;#39;crankshaft&amp;#39; %}&lt;/span&gt;) : Simulation de systèmes vilebrequin / bielle / piston

&lt;span class="gu"&gt;## Projets&lt;/span&gt;

Quelqu&amp;#39;uns de mes projets :

&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;Raytracer&lt;/span&gt;](&lt;span class="na"&gt;https://github.com/jtremesay/raytracer&lt;/span&gt;) : Itération suivante, un raytraceur en rust.
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;Mathsworld&lt;/span&gt;](&lt;span class="na"&gt;https://mathsworld.jtremesay.org/&lt;/span&gt;) ([sources](https://github.com/jtremesay/mathsworld)) : Le next level du projet précédent : un générateur de shader WebGL raytraçant une scène décrite en s-expression
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;kFPGA&lt;/span&gt;](&lt;span class="na"&gt;https://github.com/jtremesay/kfpga&lt;/span&gt;) : une architecture FPGA opensource (openhardware?).
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;MPS&lt;/span&gt;](&lt;span class="na"&gt;https://github.com/jtremesay/mpssim&lt;/span&gt;) : Un processor MIPS 8 bits.

&lt;span class="gu"&gt;## Articles&lt;/span&gt;

{% for post in posts %}
&lt;span class="k"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="nt"&gt;{{ post.timestamp|date:&amp;quot;Y-m-d&amp;quot; }}&lt;/span&gt;](&lt;span class="na"&gt;{% url &amp;#39;post&amp;#39; post.slug %}&lt;/span&gt;): {{ post.title }}{% endfor %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Les vues&lt;/h2&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;PageView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TemplateView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;template_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;page.html&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;page_cls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Page&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_context_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;slug&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_context_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;object&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_page_with_slug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;PostView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PageView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;template_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;post.html&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;page_cls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;J'aime Django, simple et efficace &amp;lt;3&lt;/p&gt;

&lt;p&gt;Comme vous vous en doutez à la vue de mon site, les templates sont ultra minimalistes.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% extends &amp;quot;base.html&amp;quot; %}

{% block &amp;quot;content&amp;quot; %}
Publié le {{ object.timestamp|date:&amp;quot;Y-m-d&amp;quot; }}.

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ object.title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{{ object.content_md|safe }}
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;La propriété &lt;code&gt;Page.content_md&lt;/code&gt; s'occupe de générer à la volée le HTML à partir du markdown, après que ce dernier soit passé par le moteur de templating.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# snip&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;content_md&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;markdown2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="s2"&gt;&amp;quot;posts&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_glob&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;extras&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fenced-code-blocks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Le flux de syndication atom est généré par django.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;PostFeedsView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;jtremesay - derniers articles&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;feed_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Atom1Feed&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_glob&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;item_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;item_description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_md&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;item_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;item_pubdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Les routes&lt;/h2&gt;

&lt;p&gt;Encore une fois, rien de bien compliqué.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RedirectView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/pages/index.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;atom.xml&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostFeedsView&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;atom_feed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pages/&amp;lt;slug:slug&amp;gt;.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;page&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;posts/&amp;lt;slug:slug&amp;gt;.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;La génération statique&lt;/h2&gt;

&lt;p&gt;La partie "rigolote" du projet, la commande qui s'occupe de générer le HTML. Encore une fois, rien de bien compliqué.&lt;/p&gt;

&lt;p&gt;On se contente de visiter toutes les pages du site et d'enregistrer le résultat.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseCommand&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;get_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;index.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;get_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;atom_feed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_glob&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;get_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;page&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,)))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_glob&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;get_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;La récupération proprement dite, on crée une requête HTTP et on laisse la vue faire son travail :)&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;META&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;HTTP_HOST&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;jtremesay.org&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;get&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;https&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;302&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DIST_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DIST_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Dockerization&lt;/h2&gt;

&lt;p&gt;On installe le bordel nécessaire, on copie le code source, on build le front, on traite les statics, on génère le site, et on génère le site. Enfin on copie le résultat dans une nouvelle image nginx.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;site&lt;/span&gt;

&lt;span class="c"&gt;# Update packages and install needed stuff&lt;/span&gt;
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;dist-upgrade&lt;span class="w"&gt; &lt;/span&gt;-y
&lt;span class="c"&gt;# I hate modern way of doing things&lt;/span&gt;
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-fsSL&lt;span class="w"&gt; &lt;/span&gt;https://deb.nodesource.com/setup_20.x&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;nodejs
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;setuptools&lt;span class="w"&gt; &lt;/span&gt;wheel

&lt;span class="c"&gt;# Install python &amp;amp; node deps&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;requirements.txt&lt;span class="w"&gt; &lt;/span&gt;./
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-Ur&lt;span class="w"&gt; &lt;/span&gt;requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;package.json&lt;span class="w"&gt; &lt;/span&gt;package-lock.json&lt;span class="w"&gt; &lt;/span&gt;./
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;install

&lt;span class="c"&gt;# Copy source dir&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;tsconfig.json&lt;span class="w"&gt; &lt;/span&gt;vite.config.ts&lt;span class="w"&gt; &lt;/span&gt;./
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jssg/&lt;span class="w"&gt; &lt;/span&gt;jssg/
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;content/&lt;span class="w"&gt; &lt;/span&gt;content/
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;front/&lt;span class="w"&gt; &lt;/span&gt;front/

&lt;span class="c"&gt;# Build&lt;/span&gt;
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;build
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;collectstatic&lt;span class="w"&gt; &lt;/span&gt;--no-input
&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;gensite

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--from&lt;span class="o"&gt;=&lt;/span&gt;site&lt;span class="w"&gt; &lt;/span&gt;/code/dist/&lt;span class="w"&gt; &lt;/span&gt;/usr/share/nginx/html/
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Cloudification&lt;/h2&gt;

&lt;p&gt;Ça tourne dans un cluster docker swarm avec traefik en frontal.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;3.8&amp;quot;&lt;/span&gt;
&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;jtremesay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;killruana/jtremesay.org:main&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;8003:80&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik_public&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.enable=true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.middlewares.jtremesay-compress.compress=true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.entrypoints=websecure&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.middlewares=jtremesay-compress&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.rule=Host(`jtremesay.org`,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;`slaanesh.org`)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.service=jtremesay&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.jtremesay.tls.certresolver=zerossl&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.services.jtremesay.loadbalancer.server.port=80&amp;quot;&lt;/span&gt;

&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;traefik_public&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;external&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;CI/CD&lt;/h2&gt;

&lt;p&gt;Github Actions.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;CI/CD&lt;/span&gt;

&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-latest&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Docker meta&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;meta&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;docker/metadata-action@v4&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;killruana/jtremesay.org&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;type=schedule&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;type=ref,event=branch&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;type=ref,event=pr&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;type=sha&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Set up Docker Buildx&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;docker/setup-buildx-action@v2&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Login to Docker Hub&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;docker/login-action@v2&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;killruana&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;${{ secrets.DOCKERHUB_TOKEN }}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Build and push&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;docker/build-push-action@v3&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;${{ steps.meta.outputs.tags }}&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;cache-from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;type=registry,ref=killruana/jtremesay.org:buildcache&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;cache-to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;type=registry,ref=killruana/jtremesay.org:buildcache,mode=max&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-latest&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;needs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;build&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;github.ref == &amp;#39;refs/heads/main&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Deploy&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;distributhor/workflow-webhook@v3&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;${{ secrets.WEBHOOK_URL }}&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;verify_ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;site static ? check&lt;/li&gt;
&lt;li&gt;chouette environement de dev ? check&lt;/li&gt;
&lt;li&gt;integration de vite/typescript ? check&lt;/li&gt;
&lt;li&gt;écriture du contenu en pas html ? check&lt;/li&gt;
&lt;li&gt;transpilation du front ? check&lt;/li&gt;
&lt;li&gt;gestion des static ? check&lt;/li&gt;
&lt;li&gt;customizable ? check&lt;/li&gt;
&lt;li&gt;simple ? check&lt;/li&gt;
&lt;li&gt;fire &amp;amp; forget ? check&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code&gt;$ cloc jssg/
      12 text files.
      10 unique files.
      14 files ignored.

github.com/AlDanial/cloc v 1.98  T=0.01 s (1127.0 files/s, 71340.1 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Python                          10            125            223            285
-------------------------------------------------------------------------------
SUM:                            10            125            223            285
-------------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;C'est fou ce qu'on peut faire avec moins de 300 lignes de python !&lt;/p&gt;
</summary></entry><entry><title>News</title><link href="https://jtremesay.org/posts/news.html" rel="alternate"/><published>2024-01-04T22:30:00+02:00</published><id>https://jtremesay.org/posts/news.html</id><summary type="html">&lt;p&gt;Bonne années !&lt;/p&gt;

&lt;p&gt;Dans les ajouts récents sur le site :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="/pages/pi-monte-carlo.html"&gt;Comment calculer pi en lançant des fléchettes sur une cible&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/pages/scadaplayer.html"&gt;Scadaplayer&lt;/a&gt;, un truc muche animé pour visualiser des données produites par une éolienne&lt;/li&gt;
&lt;li&gt;&lt;a href="/pages/crankshaft.html"&gt;Crankshaft&lt;/a&gt;, une simulation d'un système vilebrequin/bielle/piston&lt;/li&gt;
&lt;/ul&gt;
</summary></entry><entry><title>Tris animés</title><link href="https://jtremesay.org/posts/tris-animes.html" rel="alternate"/><published>2023-08-18T12:30:00+02:00</published><id>https://jtremesay.org/posts/tris-animes.html</id><summary type="html">&lt;p&gt;J'ai fait un truc muche pour visualiser les différents algorithmes de tris. C'est disponible &lt;a href="/pages/tris-animes.html"&gt;ici&lt;/a&gt;.&lt;/p&gt;
</summary></entry><entry><title>Terraform + Oracle Cloud</title><link href="https://jtremesay.org/posts/terraform-oracle-cloud.html" rel="alternate"/><published>2023-08-01T23:00:00+02:00</published><id>https://jtremesay.org/posts/terraform-oracle-cloud.html</id><summary type="html">&lt;p&gt;Ce soir, j'ai été amené à découvrir &lt;a href="https://cloud.oracle.com&amp;gt;"&gt;Oracle Cloud&lt;/a&gt; pour aider l'amie &lt;a href="https://dataswamp.org/~solene/"&gt;Solène'%&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Je ne pensais pas ça possible, mais j'ai trouvé l'expérience encore plus désagréable que celle offerte par &lt;a href="https://aws.amazon.com/"&gt;AWS&lt;/a&gt; :D&lt;/p&gt;

&lt;p&gt;Enfin bref. Après près d'une heure passée à essayer de créer le compte et se connecter à l'interface (oui, sérieusement), et 2 heures à potasser la doc, voila le &lt;code&gt;main.tf&lt;/code&gt; que j'accoucha.&lt;/p&gt;

&lt;p&gt;Il permet de provisionner une VM sous Oracle Linux 9.2, ainsi que tout le bazar autour (VNC, subnet, internet gateway, route table, …).&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Go to https://cloud.oracle.com/identity/compartments/&lt;/span&gt;
&lt;span class="c1"&gt;# to get your own compartment id&lt;/span&gt;
&lt;span class="kr"&gt;variable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;compartment_id&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ocid1.tenancy.oc1..&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# eu-marseille-1&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm&lt;/span&gt;
&lt;span class="kr"&gt;variable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;availability_domain&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;nLTo:EU-MARSEILLE-1-AD-1&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# Oracle Linux 9.2, eu-marseille-1&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.oracle.com/en-us/iaas/images/image/ba32bce1-c03a-48be-8dba-4436b1b190d3/&lt;/span&gt;
&lt;span class="kr"&gt;variable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;image_id&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ocid1.image.oc1.eu-marseille-1.aaaaaaaa5mugakrcfhb73itd7uvclgorricrb5n4g7ydgykxyzejqcdocqda&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# SSH public key&lt;/span&gt;
&lt;span class="kr"&gt;variable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;ssh_public_key&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ssh-rsa ... id_rsa&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nb"&gt;terraform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nb"&gt;required_providers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;oci&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;oracle/oci&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;5.6.0&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# https://registry.terraform.io/providers/oracle/oci/latest/docs&lt;/span&gt;
&lt;span class="kr"&gt;provider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;oci&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;    # Configuration options&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/identity_compartment&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.oracle.com/en-us/iaas/Content/Identity/Tasks/managingcompartments.htm&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;&amp;quot;oci_identity_compartment&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;sandbox&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;compartment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;var.compartment_id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sandbox&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;My first compartment!&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_vcn&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/managingVCNs.htm&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;&amp;quot;oci_core_vcn&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;sandbox&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;compartment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_identity_compartment.sandbox.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;display_name&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sandbox&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;cidr_blocks&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;10.0.0.0/16&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/core_internet_gateways&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/managingIGs.htm&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;&amp;quot;oci_core_internet_gateway&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;sandbox&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;compartment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_identity_compartment.sandbox.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;vcn_id&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_core_vcn.sandbox.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;display_name&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sandbox&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_route_table&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/managingroutetables.htm&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;&amp;quot;oci_core_route_table&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;sandbox_igw&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;compartment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_identity_compartment.sandbox.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;vcn_id&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_core_vcn.sandbox.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;display_name&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sandbox-igw&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;route_rules&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;network_entity_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_core_internet_gateway.sandbox.id&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;0.0.0.0/0&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_subnet&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.oracle.com/en-us/iaas/Content/Network/Tasks/managingVCNs.htm&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;&amp;quot;oci_core_subnet&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;sanbox_public&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;cidr_block&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;10.0.0.0/24&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;compartment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_identity_compartment.sandbox.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;vcn_id&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_core_vcn.sandbox.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;display_name&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sandbox-public&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_route_table_attachment&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;&amp;quot;oci_core_route_table_attachment&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;sandbox_public_igw&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;subnet_id&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_core_subnet.sanbox_public.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;route_table_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_core_route_table.sandbox_igw.id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_instance&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.oracle.com/en-us/iaas/Content/Compute/Concepts/computeoverview.htm&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;&amp;quot;oci_core_instance&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;sandbox&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;compartment_id&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_identity_compartment.sandbox.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;availability_domain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;var.availability_domain&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;shape&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;VM.Standard.E2.1.Micro&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;source_details&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;source_id&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;var.image_id&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;source_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;image&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;display_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sandbox&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;create_vnic_details&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;assign_public_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;subnet_id&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_core_subnet.sanbox_public.id&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;metadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;ssh_authorized_keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;var.ssh_public_key&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="kr"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;&amp;quot;vm_ip&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;oci_core_instance.sandbox.public_ip&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Provisionning et connection en ssh :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;terraform&lt;span class="w"&gt; &lt;/span&gt;apply&lt;span class="w"&gt; &lt;/span&gt;-auto-approve
Terraform&lt;span class="w"&gt; &lt;/span&gt;used&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;selected&lt;span class="w"&gt; &lt;/span&gt;providers&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;generate&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;following&lt;span class="w"&gt; &lt;/span&gt;execution&lt;span class="w"&gt; &lt;/span&gt;plan.&lt;span class="w"&gt; &lt;/span&gt;Resource&lt;span class="w"&gt; &lt;/span&gt;actions&lt;span class="w"&gt; &lt;/span&gt;are&lt;span class="w"&gt; &lt;/span&gt;indicated&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;following
symbols:
+&lt;span class="w"&gt; &lt;/span&gt;create

Terraform&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;perform&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;following&lt;span class="w"&gt; &lt;/span&gt;actions:

&lt;span class="o"&gt;(&lt;/span&gt;…&lt;span class="o"&gt;)&lt;/span&gt;


Plan:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;add,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;change,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;destroy.

Changes&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;Outputs:
+&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;vm_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;known&lt;span class="w"&gt; &lt;/span&gt;after&lt;span class="w"&gt; &lt;/span&gt;apply&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;(&lt;/span&gt;…&lt;span class="o"&gt;)&lt;/span&gt;

Apply&lt;span class="w"&gt; &lt;/span&gt;complete!&lt;span class="w"&gt; &lt;/span&gt;Resources:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;added,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;changed,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;destroyed.

Outputs:

&lt;span class="nv"&gt;vm_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;144.24.203.47&amp;quot;&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;opc@144.24.203.47
The&lt;span class="w"&gt; &lt;/span&gt;authenticity&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;host&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;144.24.203.47 (144.24.203.47)&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;can&lt;span class="s1"&gt;&amp;#39;t be established.&lt;/span&gt;
&lt;span class="s1"&gt;ED25519 key fingerprint is SHA256:2fDt7TNdNlHf1gprtRThKOM4mtay4Aj5XpRM5QEHgpQ.&lt;/span&gt;
&lt;span class="s1"&gt;This key is not known by any other names.&lt;/span&gt;
&lt;span class="s1"&gt;Are you sure you want to continue connecting (yes/no/[fingerprint])? yes&lt;/span&gt;
&lt;span class="s1"&gt;Warning: Permanently added &amp;#39;&lt;/span&gt;&lt;span class="m"&gt;144&lt;/span&gt;.24.203.47&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ED25519&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;known&lt;span class="w"&gt; &lt;/span&gt;hosts.
&lt;span class="o"&gt;[&lt;/span&gt;opc@sandbox&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="nb"&gt;logout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</summary></entry><entry><title>JSSG: jtremesay's static site generator</title><link href="https://jtremesay.org/posts/jssg.html" rel="alternate"/><published>2023-07-29T17:33:00+02:00</published><id>https://jtremesay.org/posts/jssg.html</id><summary type="html">&lt;p&gt;Bon ben finalement je suis parti sur mon propre générateur, &lt;a href="https://github.com/jtremesay/jtremesay.org/tree/2861e2ba6e17902614f85dc712b450b2daf0a286"&gt;&lt;code&gt;jssg&lt;/code&gt;&lt;/a&gt;. C'est minimaliste, ça gère rien, c'est parfait pour mes besoins.&lt;/p&gt;

&lt;p&gt;C'est juste un bricolage autour de python et jinja.&lt;/p&gt;

&lt;p&gt;On verra comment il évolue :)&lt;/p&gt;
</summary></entry><entry><title>Migration de Gandi Mail vers Mailo</title><link href="https://jtremesay.org/posts/migration-de-gandi-mail-vers-mailo.html" rel="alternate"/><published>2023-06-18T16:00:00+02:00</published><id>https://jtremesay.org/posts/migration-de-gandi-mail-vers-mailo.html</id><summary type="html">&lt;h2&gt;Context&lt;/h2&gt;

&lt;p&gt;Récemment, Gandi a &lt;a href="https://linuxfr.org/users/acatton/journaux/gandi-passe-de-no-bullshit-a-bait-and-switch&amp;gt;"&gt;annoncé&lt;/a&gt; une augmentation de ses prix, dont notamment celui des boites mail. Actuellement, Je profite de 3 boites mails gratuites incluses avec mon nom de domaine. À partir de juillet, elles passeraient à presque &lt;a href="https://www.gandi.net/static/documents/2023-july-eur-renew-price-increase.pdf"&gt;5€/mois&lt;/a&gt; chacune. Un peu excessif, surtout pour mes 200Mo d'archives toutes boites mails confondues.&lt;/p&gt;

&lt;h2&gt;Le besoin&lt;/h2&gt;

&lt;p&gt;Héberger 3 boites mails, en utilisant mon propre nom de domaine, chez un hébergeur idéalement associatif ou coopératif, situé en Europe, et n'ayant pas son business basé sur la collecte des données. Le tout à un tarif acceptable.&lt;/p&gt;

&lt;h2&gt;Les challengers&lt;/h2&gt;

&lt;p&gt;J'ai rapidement regardé du côté des &lt;a href="https://www.chatons.org/search/by-service?service_type_target_id=112&amp;field_alternatives_aux_services_target_id=All&amp;field_software_target_id=All&amp;field_is_shared_value=All&amp;title=&amp;gt;"&gt;chatons&lt;/a&gt;. Rien ne fit mon bonheur même si je note l'existence de l'association l'&lt;a href="https://www.lautre.net/"&gt;Autre Net&lt;/a&gt; pour un éventuel usage futur.&lt;/p&gt;

&lt;p&gt;J'ai regardé du côté de [ProtonMail] (https://proton.me). Ils sont chers (5€/mois) pour plusieurs boites mails. Et il faut obligatoirement installer leur &lt;a href="https://proton.me/mail/bridge"&gt;middleware&lt;/a&gt; pour profiter de IMAP :(.&lt;/p&gt;

&lt;p&gt;Pis &lt;a href="https://www.mailo.com/"&gt;Mailo&lt;/a&gt;. C'est des frenchies, ils &lt;a href="https://www.mailo.com/mailo/fr/qui-sommes-nous.php"&gt;prétendent&lt;/a&gt; offrir « une messagerie 100 % européenne, garantissant aux utilisateurs de retrouver la maîtrise de leurs données et les accompagnant dans une utilisation éthique et responsable du Web ». Leur &lt;a href="https://www.mailo.com/mailo/fr/regles-de-confidentialite.php"&gt;politique de confidentialité&lt;/a&gt; indique que « (…) Mailo ne communique aucune de vos données personnelles à des tiers. En particulier, Mailo ne transmet aucune information à ses régies publicitaires partenaires. ». L'offre gratuite permet d'utiliser son propre domaine et est financée par la &lt;a href="https://ublockorigin.com/"&gt;pub&lt;/a&gt;. L'offre payante est à 1€/mois.&lt;/p&gt;

&lt;p&gt;Ils semblaient cocher toutes les cases, je décida de partir avec eux.&lt;/p&gt;

&lt;p&gt;Creation du compte. Les formulaires sont simples et clairs. Les seules informations personnelles demandées sont nom, prénom et date de naissance. Je pars sur l'offre gratuite (je suis s/radin/économe/). Me voila l'heureux propriétaire d'une nouvelle adresse éponyme en &lt;code&gt;@mailo.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sauf que mon but est d'avoir des adresses en &lt;code&gt;@slaanesh.org&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;La &lt;a href="https://www.mailo.com/mailo/fr/noms-de-domaine.php&amp;gt;"&gt;doc&lt;/a&gt; n'est pas super claire sur la procédure :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Votre nom de domaine avec Mailo&lt;/p&gt;
  
  &lt;p&gt;Vous pouvez acheter un nom de domaine à partir de 15 euros TTC par an.
   - (…)
   - Si vous avez déjà acheté un nom de domaine ailleurs, vous pouvez aussi le déclarer pour l'utiliser avec les services Mailo.&lt;/p&gt;
  
  &lt;p&gt;Pour cela, vous devez disposer d'un espace Mailo gratuit :
   - Vous devez disposer d'un compte Mailo.
   - Dans votre compte Mailo, choisissez "Espaces Mailo" dans le menu.
   - Créez un espace Mailo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Après quelques tâtonnement, voila comment que j'ai fait.&lt;/p&gt;

&lt;h2&gt;Utiliser son propre domaine avec Mailo&lt;/h2&gt;

&lt;p&gt;Connectez vous avec votre adresse en &lt;code&gt;@mailo.com&lt;/code&gt;. C'est la seule fois ou vous vous en servirez. Par la suite, vous utiliserez les comptes crées sous votre domaine perso.&lt;/p&gt;

&lt;p&gt;Accéder à « Mailo Space » par l’icône aux trois bonhommes dans la barre en haut à droite.&lt;/p&gt;

&lt;p&gt;&lt;img src="/static/jtremesay/images/mailo_access_to_spaces.704ca18ad68e.jpg" alt="là où il faut cliquer pour trouver les Spaces" /&gt;&lt;/p&gt;

&lt;p&gt;Créez un nouvel espace. J'ai choisi un espace familial mais j'aurai probablement du prendre un espace pro ou asso. Tant pis. Ça n'a pas l'air de changer grand choses, et comme j'ai la flemme de me retaper la config, ça va rester ainsi.&lt;/p&gt;

&lt;p&gt;Edit: en fait si, ça change des trucs. L'espace familiale coûte 2€/mois et offre 5 comptes premiums (+nombre illimités de comptes gratuits).&lt;/p&gt;

&lt;p&gt;Une fois l'espace créer, aller dans &lt;code&gt;Management -&amp;gt; Declare a domain name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Mailo va vous donner un champs &lt;code&gt;TXT&lt;/code&gt; à ajouter à votre domaine pour prouver que vous en êtes le propriétaire.&lt;/p&gt;

&lt;p&gt;Une fois votre domaine authentifié, sélectionner le dans la liste puis allez dans &lt;code&gt;DNS configuration&lt;/code&gt;. À la section &lt;code&gt;Mail configuration&lt;/code&gt;, il vous donnera les champs &lt;code&gt;MX&lt;/code&gt; et &lt;code&gt;TXT&lt;/code&gt; que vous devez ajouter votre domaine.&lt;/p&gt;

&lt;p&gt;En attendant que les DNS se propagent, vous pouvez créer votre compte. Retourner à l'écran Management, sélectionner &lt;code&gt;Create an account&lt;/code&gt; et rentrez toutes les informations demandées. Assurez vous de bien sélectionner votre domaine dans la liste des domaines disponibles.&lt;/p&gt;

&lt;p&gt;Une fois le compte créer, allez dans &lt;code&gt;Management -&amp;gt; Managers&lt;/code&gt; pour ajouter les droits administratifs à votre nouveau compte.&lt;/p&gt;

&lt;p&gt;Vous pouvez maintenant vous déconnecter et vous reconnecter avec votre nouveau compte pour vérifier que tous marche.&lt;/p&gt;

&lt;p&gt;Pour créer de nouveau comptes, il suffit de répéter la procédure précédente :P&lt;/p&gt;

&lt;p&gt;Pour les alias, c'est un peu le bordel.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pour un alias avec votre domaine perso, &lt;code&gt;Mailo spaces -&amp;gt; Management -&amp;gt; List of accounts -&amp;gt; Manage the aliases&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;pour un alias avec un des domaines de Mailo, &lt;code&gt;Settingt -&amp;gt; Aliases -&amp;gt; Create an alias&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;les alias dynamiques (fonctionnalité de l'offre premium) : les mails envoyés à &lt;code&gt;&amp;lt;username&amp;gt;++&amp;lt;tag&amp;gt;@&lt;/code&gt; sont redirigés vers &lt;code&gt;&amp;lt;username&amp;gt;@&lt;/code&gt;. (Obligatoirement &lt;code&gt;++&lt;/code&gt;, rip toutes mes adresses avec un seul &lt;code&gt;+&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;imapsync&lt;/h2&gt;

&lt;p&gt;Mailo a l'air de fournir tout ce qu'il faut pour migrer les anciens mails depuis les serveurs IMAP de Gandi, mais j'ai préféré jouer avec &lt;code&gt;imapsync&lt;/code&gt; pour tout copier.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;imapsync&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--host1&lt;span class="w"&gt; &lt;/span&gt;mail.gandi.net&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--user1&lt;span class="w"&gt; &lt;/span&gt;camille@slaanesh.org&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--password1&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;secret1&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--host2&lt;span class="w"&gt; &lt;/span&gt;mail.mailo.com&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--user2&lt;span class="w"&gt;  &lt;/span&gt;camille@slaanesh.org&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--password2&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;secret2&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Premier retour&lt;/h2&gt;

&lt;p&gt;Bah, ça juste marche. Ça juste marche tellement bien que j'ai pris un an d'abonnement premium (12€), copié tout ce qui avait à être copié et supprimé mes boites Gandi.&lt;/p&gt;

&lt;p&gt;La webapp est meh, j'ai eu un peu de mal à trouver comment enregistrer mes domaines ou créer les alias. Mais une fois le bon menu trouvé, c'est simple et clair.&lt;/p&gt;

&lt;p&gt;Ils ont l'air de proposer d'autres trucs tel que caldav, carddav, stockage et partage de fichiers, mais j'ai déjà mon nextcloud pour ça.&lt;/p&gt;

&lt;h2&gt;Retours des 6 mois&lt;/h2&gt;

&lt;p&gt;Ouais ben ça juste marche. Je n'ai jamais eu à me reconnecter à l'interface d'adminstration ou au webmail. J'ai pas eu de problèmes de mails non délivrés ou de spam. Je continue de recommender.&lt;/p&gt;
</summary></entry><entry><title>Django + VueJS - MultiPage Application</title><link href="https://jtremesay.org/posts/django-vuejs-multipage-application.html" rel="alternate"/><published>2023-06-14T20:00:00+02:00</published><id>https://jtremesay.org/posts/django-vuejs-multipage-application.html</id><summary type="html">&lt;h2&gt;Préambule&lt;/h2&gt;

&lt;p&gt;Parce que j'ai passé beaucoup trop de temps à faire tomber ça en marche, je partage ici le résultat de mes expérimentations.&lt;/p&gt;

&lt;p&gt;Contexte: j'ai fait un peu de dev front au début des années 2010. À l'époque, c'était simple. Jquery, quelques fichiers javascript inclus dans la page, et roulez jeunesse.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;tree&lt;span class="w"&gt; &lt;/span&gt;oldschool/
oldschool/
├──&lt;span class="w"&gt; &lt;/span&gt;js
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;common.js
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;index.js
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;jquery.js
│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;ma_page.js
├──&lt;span class="w"&gt; &lt;/span&gt;index.html
└──&lt;span class="w"&gt; &lt;/span&gt;ma_page.html

$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;oldschool/ma_page.html
&amp;lt;!DOCTYPE&lt;span class="w"&gt; &lt;/span&gt;html&amp;gt;
&amp;lt;html&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&amp;gt;

&amp;lt;head&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;meta&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UTF-8&amp;quot;&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;title&amp;gt;Mon&lt;span class="w"&gt; &lt;/span&gt;Site&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Ma&lt;span class="w"&gt; &lt;/span&gt;Page&amp;lt;/title&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;script&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/js/jquery.js&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;script&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/js/common.js&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;script&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/js/ma_page.js&amp;quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Ça fini immanquablement par devenir du code spaghetti, mais le coût d'entrée est pour ainsi dire nul. C'est à la portée du premier crétin venu. Cf moi.&lt;/p&gt;

&lt;h2&gt;La problématique&lt;/h2&gt;

&lt;p&gt;Nous voila en 2023. Au cours de cette décennie écoulée, j'ai fait assez peu de dev web, et je n'ai pas DU TOUT suivi les évolutions du domaine. Au taf, on a une webapp django dont le frontend est géré à l'ancienne : HTML généré par les templates Django et Vanilla JS inclut à l'ancienne.&lt;/p&gt;

&lt;p&gt;Ça commence à devenir assez gros et bordélique. Je voudrais faire du ménage dans tout ça, et moderniser. Au moins ajouter TypeScript pour profiter du typage statique et de la compilation pour détecter et corriger aux plus tôt les petites erreurs d'inattention à la con et de profiter de l'intelligence de l'IDE. NPM serait aussi un plus pour simplifier les mises à jours de nos dépendances. Puis peut-être expérimenter avec des framework UI tel que vuejs pour voir si ça peut nous simplifier la vie pour certain éléments de l'interface.&lt;/p&gt;

&lt;p&gt;Bref, le cahier des charges est simple : transitionner vers du front moderne, tout en continuant à le gérer à l'ancienne, et intégration avec l'existant parce qu'on ne va pas tout remplacer.&lt;/p&gt;

&lt;p&gt;Ben il s'avère que l'écosystème moderne n'est ABSOLUMENT pas pensé pour être utilisé comme ça.&lt;/p&gt;

&lt;p&gt;Non, maintenant le front est censé être pensé comme une application à part entière et indépendante du backend et causer avec ce dernier via une api REST.&lt;/p&gt;

&lt;p&gt;Sauf que cette manière de faire arrange pas du tout quand il faut intégrer avec l'existant. Surtout quand tu veux juste quelque composants par ci par là et pas une application complète.&lt;/p&gt;

&lt;p&gt;Après beaucoup trop de prises de tête, de crises de nerfs, d'expérimetations, et d'épluchage de documentations, j'ai fini par faire fonctionner vuejs+typescript et django pour que ça marche à l'ancienne.&lt;/p&gt;

&lt;p&gt;La solution est d'utiliser vitejs en mode MultiPage Application, de découvrir dynamiquement vos différents points d'entrées, et demander à Django de servir en tant que static le dossier contenant le résultat de la compilation.&lt;/p&gt;

&lt;h2&gt;Le front&lt;/h2&gt;

&lt;p&gt;Intéressons nous tout d'abord à la partie front.&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;tree&lt;span class="w"&gt; &lt;/span&gt;djangovuejs
djangovuejs
├──&lt;span class="w"&gt; &lt;/span&gt;front
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;main
│&lt;span class="w"&gt;   &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;index.ts
│&lt;span class="w"&gt;   &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;ma_page.ts
│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;src
│&lt;span class="w"&gt;       &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;common.ts
│&lt;span class="w"&gt;       &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;Index.vue
│&lt;span class="w"&gt;       &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;MaPage.vue
├──&lt;span class="w"&gt; &lt;/span&gt;package.json
├──&lt;span class="w"&gt; &lt;/span&gt;tsconfig.json
└──&lt;span class="w"&gt; &lt;/span&gt;vite.config.ts

$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;djangovuejs/package.json
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;djangovuejs&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;private&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;0.0.0&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;module&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dev&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;vite build --watch -m dev&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;build&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tsc &amp;amp;&amp;amp; vite build&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dependencies&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;devDependencies&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;@vitejs/plugin-vue&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;^4.1.0&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;typescript&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;^5.0.2&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;vite&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;^4.3.9&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;glob&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;^10.2.6&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;djangovuejs/tsconfig.json
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;compilerOptions&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;target&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ES2020&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;useDefineForClassFields&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;module&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ESNext&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;lib&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ES2020&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DOM&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DOM.Iterable&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;skipLibCheck&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;        &lt;/span&gt;/*&lt;span class="w"&gt; &lt;/span&gt;Bundler&lt;span class="w"&gt; &lt;/span&gt;mode&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;moduleResolution&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bundler&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;allowImportingTsExtensions&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;resolveJsonModule&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;isolatedModules&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;noEmit&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;jsx&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;preserve&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;/*&lt;span class="w"&gt; &lt;/span&gt;Linting&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;strict&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;noUnusedLocals&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;noUnusedParameters&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;true,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;noFallthroughCasesInSwitch&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;include&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;front/**/*.ts&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;front/**/*.d.ts&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;front/**/*.vue&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
&lt;span class="o"&gt;}&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;djangovuejs/vite.config.ts
import&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;globSync&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;glob&amp;#39;&lt;/span&gt;
import&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;defineConfig&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;vite&amp;#39;&lt;/span&gt;
import&lt;span class="w"&gt; &lt;/span&gt;vue&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@vitejs/plugin-vue&amp;#39;&lt;/span&gt;

//&lt;span class="w"&gt; &lt;/span&gt;https://vitejs.dev/config/
import&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;globSync&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;glob&amp;#39;&lt;/span&gt;
import&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;defineConfig&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;vite&amp;#39;&lt;/span&gt;
import&lt;span class="w"&gt; &lt;/span&gt;vue&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@vitejs/plugin-vue&amp;#39;&lt;/span&gt;

//&lt;span class="w"&gt; &lt;/span&gt;https://vitejs.dev/config/
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;default&lt;span class="w"&gt; &lt;/span&gt;defineConfig&lt;span class="o"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;plugins:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;vue&lt;span class="o"&gt;()]&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;build:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;rollupOptions:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;input:&lt;span class="w"&gt; &lt;/span&gt;globSync&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;front/main/**/*.ts&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;output:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;dir:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;out/front/&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;            &lt;/span&gt;entryFileNames:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[name].js&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;            &lt;/span&gt;assetFileNames:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;assets/[name].[ext]&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;            &lt;/span&gt;chunkFileNames:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;chunks/[name].js&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;})&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;front/main/ma_page.ts
import&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;createApp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;vue&amp;#39;&lt;/span&gt;
import&lt;span class="w"&gt; &lt;/span&gt;App&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;../src/MaPage.vue&amp;#39;&lt;/span&gt;

createApp&lt;span class="o"&gt;(&lt;/span&gt;App&lt;span class="o"&gt;)&lt;/span&gt;.mount&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#app&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Le dossier &lt;code&gt;front/main/&lt;/code&gt; contient nos différents point d'entrées qui seront découverts dynamiquement par vitejs. &lt;code&gt;front/src/&lt;/code&gt; contient le reste du code relatif au front.&lt;/p&gt;

&lt;p&gt;Pour plus de détail, la doc est votre amie :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;https://vitejs.dev/config/&lt;/li&gt;
&lt;li&gt;https://rollupjs.org/configuration-options/&lt;/li&gt;
&lt;li&gt;https://vuejs.org/guide/introduction.html&lt;/li&gt;
&lt;li&gt;https://www.typescriptlang.org/docs/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nous pouvons maintenant installer les dépendances et compiler le différents points d'entrées :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;djangovuejs/

$&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;install

added&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;67&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;packages,&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;audited&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;68&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;packages&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;853ms

&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;packages&lt;span class="w"&gt; &lt;/span&gt;are&lt;span class="w"&gt; &lt;/span&gt;looking&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;funding
run&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;fund&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;details

found&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;vulnerabilities

$&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;build

&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;djangovuejs@0.0.0&lt;span class="w"&gt; &lt;/span&gt;build
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;vite&lt;span class="w"&gt; &lt;/span&gt;build

vite&lt;span class="w"&gt; &lt;/span&gt;v4.3.9&lt;span class="w"&gt; &lt;/span&gt;building&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;production...
✓&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;modules&lt;span class="w"&gt; &lt;/span&gt;transformed.
out/front/assets/ma_page.css&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.04&lt;span class="w"&gt; &lt;/span&gt;kB&lt;span class="w"&gt; &lt;/span&gt;│&lt;span class="w"&gt; &lt;/span&gt;gzip:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.06&lt;span class="w"&gt; &lt;/span&gt;kB
out/front/assets/index.css&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.04&lt;span class="w"&gt; &lt;/span&gt;kB&lt;span class="w"&gt; &lt;/span&gt;│&lt;span class="w"&gt; &lt;/span&gt;gzip:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.06&lt;span class="w"&gt; &lt;/span&gt;kB
out/front/index.js&lt;span class="w"&gt;                              &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.33&lt;span class="w"&gt; &lt;/span&gt;kB&lt;span class="w"&gt; &lt;/span&gt;│&lt;span class="w"&gt; &lt;/span&gt;gzip:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.26&lt;span class="w"&gt; &lt;/span&gt;kB
out/front/ma_page.js&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.34&lt;span class="w"&gt; &lt;/span&gt;kB&lt;span class="w"&gt; &lt;/span&gt;│&lt;span class="w"&gt; &lt;/span&gt;gzip:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.27&lt;span class="w"&gt; &lt;/span&gt;kB
out/front/chunks/_plugin-vue_export-helper.js&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;49&lt;/span&gt;.54&lt;span class="w"&gt; &lt;/span&gt;kB&lt;span class="w"&gt; &lt;/span&gt;│&lt;span class="w"&gt; &lt;/span&gt;gzip:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;.01&lt;span class="w"&gt; &lt;/span&gt;kB
✓&lt;span class="w"&gt; &lt;/span&gt;built&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;684ms

$&lt;span class="w"&gt; &lt;/span&gt;tree&lt;span class="w"&gt; &lt;/span&gt;out/
out/
└──&lt;span class="w"&gt; &lt;/span&gt;front
&lt;span class="w"&gt;    &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;assets
&lt;span class="w"&gt;    &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;index.css
&lt;span class="w"&gt;    &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;ma_page.css
&lt;span class="w"&gt;    &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;chunks
&lt;span class="w"&gt;    &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;_plugin-vue_export-helper.js
&lt;span class="w"&gt;    &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;index.js
&lt;span class="w"&gt;    &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;ma_page.js

&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;directories,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;files
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Le sous dossier &lt;code&gt;chunks&lt;/code&gt; contient les morceaux de codes qui sont communs à vos points d'entrées afin de minimiser la quantité de javascript téléchargée par le client. Le sous dossier &lt;code&gt;assets&lt;/code&gt; contient les css générés à partir des composants vuejs et les assets (images et cie) référencés dans ces derniers.&lt;/p&gt;

&lt;p&gt;Vous pouvez aussi compiler le code à la demande quand vous éditer un fichier avec la commande suivante :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt;  &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;dev

&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;djangovuejs@0.0.0&lt;span class="w"&gt; &lt;/span&gt;dev
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;vite&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;--watch&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;dev

vite&lt;span class="w"&gt; &lt;/span&gt;v4.3.9&lt;span class="w"&gt; &lt;/span&gt;building&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dev...

watching&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;changes...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Django&lt;/h2&gt;

&lt;p&gt;Les modifications à appliquer à Django sont minimales. Il vous suffit d'abord de définir la variable &lt;code&gt;STATICFILES_DIRS&lt;/code&gt; de votre &lt;code&gt;proj/settings.py&lt;/code&gt; :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;STATICFILES_DIRS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;front&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BASE_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;out&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;front&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Vous pouvez maintenant importer le javascript dans votre template :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% load static %}
{% extends &amp;quot;myapp/base.html&amp;quot; %}

{% block content %}
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{% static &amp;#39;jtremesay/front/ma_page.js&amp;#39; %}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;module&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{% static &amp;#39;jtremesay/front/assets/ma_page.css&amp;#39; %}&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ma_page&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-api-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{% url &amp;#39;app:api.json&amp;#39; %}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Le dev web s'est quand même sacrément complexifié aux cours des 10 dernières années :-/&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;tree&lt;span class="w"&gt; &lt;/span&gt;oldschool
oldschool
├──&lt;span class="w"&gt; &lt;/span&gt;js
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;common.js
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;index.js
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;jquery.js
│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;ma_page.js
├──&lt;span class="w"&gt; &lt;/span&gt;index.html
└──&lt;span class="w"&gt; &lt;/span&gt;ma_page.html

&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;directories,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;files
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;djangovuejs
├──&lt;span class="w"&gt; &lt;/span&gt;front
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;main
│&lt;span class="w"&gt;   &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;index.ts
│&lt;span class="w"&gt;   &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;ma_page.ts
│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;src
│&lt;span class="w"&gt;       &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;common.ts
│&lt;span class="w"&gt;       &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;Index.vue
│&lt;span class="w"&gt;       &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;MaPage.vue
├──&lt;span class="w"&gt; &lt;/span&gt;manage.py
├──&lt;span class="w"&gt; &lt;/span&gt;myapp
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;admin.py
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;apps.py
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;__init__.py
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;migrations
│&lt;span class="w"&gt;   &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;__init__.py
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;models.py
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;templates
│&lt;span class="w"&gt;   &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;myapp
│&lt;span class="w"&gt;   &lt;/span&gt;│&lt;span class="w"&gt;       &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;base.html
│&lt;span class="w"&gt;   &lt;/span&gt;│&lt;span class="w"&gt;       &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;index.html
│&lt;span class="w"&gt;   &lt;/span&gt;│&lt;span class="w"&gt;       &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;ma_page.html
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;tests.py
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;urls.py
│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;views.py
├──&lt;span class="w"&gt; &lt;/span&gt;package.json
├──&lt;span class="w"&gt; &lt;/span&gt;package-lock.json
├──&lt;span class="w"&gt; &lt;/span&gt;proj
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;asgi.py
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;__init__.py
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;settings.py
│&lt;span class="w"&gt;   &lt;/span&gt;├──&lt;span class="w"&gt; &lt;/span&gt;urls.py
│&lt;span class="w"&gt;   &lt;/span&gt;└──&lt;span class="w"&gt; &lt;/span&gt;wsgi.py
├──&lt;span class="w"&gt; &lt;/span&gt;requirements.txt
├──&lt;span class="w"&gt; &lt;/span&gt;tsconfig.json
└──&lt;span class="w"&gt; &lt;/span&gt;vite.config.ts

&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;directories,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;27&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;files
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</summary></entry><entry><title>Pelican</title><link href="https://jtremesay.org/posts/pelican.html" rel="alternate"/><published>2023-06-13T19:00:00+02:00</published><id>https://jtremesay.org/posts/pelican.html</id><summary type="html">&lt;p&gt;Il y a &lt;a href="/posts/hello-world.html"&gt;2 semaines&lt;/a&gt;, ce blog était une webapp flask minimaliste. Quelques jours de travail plus tard, le voila transformé en générateur de sites statiques ultra simple utilisant de manière assez créative le moteur de templates Jinja et des Makefiles. Pis quand j'ai commencé à réfléchir à comment intégrer Markdown ou restructuredText pour taper les posts dans un truc moins relou que HTML, j'ai réalisé que ce projet commençais sérieusement à ressembler à &lt;a href="https://getpelican.com/&amp;gt;"&gt;Pelican&lt;/a&gt; : un générateur de site statique qui utilise les templates Jinja.&lt;/p&gt;

&lt;p&gt;Bon ben voila, le site est maintenant gé(né)ré par Pelican.&lt;/p&gt;

&lt;p&gt;Le seul truc qui me manque vraiment est la possibilité d'utiliser le templating Jinja DANS le contenu plutôt que simplement pour le design.&lt;/p&gt;

&lt;p&gt;Ça me serait utile pour notamment générer dynamiquement la table des chaines Youtube &lt;a href="/posts/chaines-youtubes.html"&gt;ici&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Là, j'ai fait crados. J'ai un &lt;a href="https://github.com/jtremesay/jtremesay.org/blob/b6f30f31376b1171a9653b38c341e8ca4b6d5a09/fragments/Makefile#L6"&gt;Makefile&lt;/a&gt; qui s'occupe de générer un fragment HTML à partir du CSV de données, et ce fragment est inclus dans l'&lt;a href="https://github.com/jtremesay/jtremesay.org/blob/b6f30f31376b1171a9653b38c341e8ca4b6d5a09/content/20230606_chaines_youtube.rst?plain=1#L8"&gt;article&lt;/a&gt;. Ça a le mérite de respecter le cahier des charges et d'être simple et rapide à implémenter.&lt;/p&gt;

&lt;p&gt;Pis là, je découvre l'existence du plugin &lt;a href="https://github.com/pelican-plugins/jinja2content"&gt;jinja2content&lt;/a&gt; qui, je cite, "allows the use of Jinja2 directives inside your Pelican articles and pages"…&lt;/p&gt;

&lt;p&gt;Bon ben je sais quoi faire ce soir…&lt;/p&gt;
</summary></entry><entry><title>Chaînes YouTubes</title><link href="https://jtremesay.org/posts/chaines-youtubes.html" rel="alternate"/><published>2023-06-06T20:00:00+02:00</published><id>https://jtremesay.org/posts/chaines-youtubes.html</id><summary type="html">&lt;p&gt;Une sélection de chaînes Youtube dumpées de mon agrégateur de flux. Généré automatiquement depuis &lt;a href="/static/jtremesay/files/youtube_channels.ec717b693bda.csv"&gt;youtube_channels.csv&lt;/a&gt; :&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
  &lt;th&gt;title&lt;/th&gt;
  &lt;th&gt;url&lt;/th&gt;
  &lt;th&gt;description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;td&gt;3Blue1Brown&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCYO&lt;em&gt;jab&lt;/em&gt;esuFRV4b17AJtAw&lt;/td&gt;
  &lt;td&gt;3Blue1Brown, by Grant Sanderson, is some combination of math and entertainment, depending on your disposition. The goal is for explanations to be driven by a...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;12 Parsecs&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCkMsSGlEMPRxhqvGj6qeucQ&lt;/td&gt;
  &lt;td&gt;Bienvenue sur la chaine de l&amp;#x27;à peu près...Parce que tout ce que je raconte, on va dire que c&amp;#x27;est à peu près ça...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Ahoy&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCE1jXbVAGJQEORz9nZqb5bQ&lt;/td&gt;
  &lt;td&gt;Insightful gaming videos.&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;ANALEPSE&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCDB2qWL_20CgNC22fgPz00w&lt;/td&gt;
  &lt;td&gt;Analepse vous propose de revenir sur un personnage, un évènement, un contexte historique au travers d&amp;#x27;œuvres artistiques et culturelles.Romans, bandes dessin...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Asclépios&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCDqEttzOpPbDoeC05HRPPDQ&lt;/td&gt;
  &lt;td&gt;La Médecine à l&amp;#x27;ancienne, genre... Vraiment à l&amp;#x27;ancienne&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;AstronoGeek&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC5X4e8ScZI2AFd_vkjSoyoQ&lt;/td&gt;
  &lt;td&gt;AstronoGeek, c&amp;#x27;est une chaîne dédiée à la vulgarisation de l&amp;#x27;astronomie. Bordel de merde.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Ave&amp;#x27;Roes&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCMGzPGJMKKDjnproamSIC1A&lt;/td&gt;
  &lt;td&gt;Romain, alias Ave’Roes.5 ans à étudier l’histoire (surtout médiévale !).Je vous aide à comprendre réellement l’islam et l’histoire arabo-musulmane… au-delà d...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Bazar du Grenier&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCCMxHHciWRBBouzk-PGzmtQ&lt;/td&gt;
  &lt;td&gt;La chaîne &amp;quot;fourre tout&amp;quot; du grenier ! - Cinéma- Jeux de rôles- Let&amp;#x27;s play- Reviews de jeux !&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Bismuth&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCQ9STd0zeHrrQGJQEuvhuTw&lt;/td&gt;
  &lt;td&gt;World record and tool-assisted speedrun explanations&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Bolchegeek&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCEfpbmshpf-G90fGLla73kA&lt;/td&gt;
  &lt;td&gt;La chaîne Bolchegeek aborde la culture populaire sous des angles artistiques, sociaux et politiques.Cinoche, BD, séries, bouquins ou jeux vidéo : on s&amp;#x27;interr...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Calmos&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCYBr8enT5x4-JIxcPY6y-_A&lt;/td&gt;
  &lt;td&gt;Calmos c’est une chaîne YouTube, un podcast, et une foule d’autres petites choses pour les gens bien comme vous qui aimez le cinéma.Ça s&amp;#x27;appelle Calmos parce...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;CGP Grey&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC2C_jShtL725hvbm1arSV9w&lt;/td&gt;
  &lt;td&gt;Share your videos with friends, family, and the world&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Chat Sceptique&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCOuIgj0CYCXCvjWywjDbauw&lt;/td&gt;
  &lt;td&gt;Chat Sceptique est un projet belge de vulgarisation scientifique sur YouTube axé sur les stats, les maths et l&amp;#x27;esprit critique... avec en vedette des petits ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Cody&amp;#x27;sLab&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCu6mSoMNzHQiBIOCkHUa2Aw&lt;/td&gt;
  &lt;td&gt;Aright everyone Cody here!Welcome to my channel! I simply post videos with science, bees, and anything else that I am working on. Enjoy!If you need to get in...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Confessions d&amp;#x27;Histoire&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCzj9-ZfpJ74vYv1RZzAWTVg&lt;/td&gt;
  &lt;td&gt;Enfin, il était temps de donner directement la parole aux grands personnages historiques pour qu&amp;#x27;ils nous donnent leur point de vue sur les événements marqua...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Curiosity Catalyst&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCg47pIhBRJbz52-ABnm0uxw&lt;/td&gt;
  &lt;td&gt;Un regard sceptique sur le monde.Membre du Café des sciences .Toutes les vidéos de la chaîne Curiosity Catalyst sont sous licence Créative Common : CC-BY-NC-SA&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Data Gueule&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCm5wThREh298TkK8hCT9HuA&lt;/td&gt;
  &lt;td&gt;Chaque jour, nous sommes bombardés par des milliers de molécules d&amp;#x27;information. Des faits, des noms, des chiffres qui s&amp;#x27;empilent et se percutent sans que, po...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Deerstalker Pictures&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCEqT&lt;em&gt;Q5&lt;/em&gt;ijLOcVv930jSimg&lt;/td&gt;
  &lt;td&gt;New videos every Wednesday! Making 1 For All, an original fantasy comedy webseries. Check out our live-action cosplay short films and skits from popular vide...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Deus Ex Silicium&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCH6ppHEvV3_WIXEwmhv9HEg&lt;/td&gt;
  &lt;td&gt;DEUS EX SILICIUM est une chaine francophone dont la thématique principale est l&amp;#x27;électronique numérique, et plus particulièrement autour des semiconducteurs e...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Diable Positif&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCI_FnSJPNGd3Y3IL0PwD-NQ&lt;/td&gt;
  &lt;td&gt;Découvrons ensemble comment rendre la vie sur Terre encore plus pourrie qu&amp;#x27;elle ne l&amp;#x27;est déjà voulez-vous ?Ensemble, nous allons étudier différents procédés ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;driving 4 answers&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCwosUnVH6AINmxtqkNJ3Fbg&lt;/td&gt;
  &lt;td&gt;Let&amp;#x27;s learn, build and explore the amazing world of mechanics, engines, cars and bikes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;e-penser&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCcziTK2NKeWtWQ6kB5tmQ8Q&lt;/td&gt;
  &lt;td&gt;Ici, je parle de ce dont j&amp;#x27;ai envie de parler quoi qu&amp;#x27;en pense la plate-forme :)Retrouvez ici les nouvelles vidéos e-penser (à partir de 2022),  ainsi que le...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;ElectroBOOM&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCJ0-OtVpF0wOKEqT2Z1HEtA&lt;/td&gt;
  &lt;td&gt;Want to subconsciously learn while being entertained? Then subscribe!!  My videos are also available on Odysee in the link below, in case YouTube decides to ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;El Jj&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCgkhWgBGRp0sdFy2MHDWfSg&lt;/td&gt;
  &lt;td&gt;Mais si ! C&amp;#x27;est la chaîne avec des vidéos de maths ! Des fois, ça va un peu vite, du coup, j&amp;#x27;ai pas tout bien compris.contact : contact.eljj@gmail.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Enfants de l&amp;#x27;Est&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCfqYDo0XQC3tgkt7rs1-NqQ&lt;/td&gt;
  &lt;td&gt;Une balalaïka dans une main, une bouteille de vodka dans l&amp;#x27;autre - c&amp;#x27;est ainsi que chez les Enfants de l&amp;#x27;Est nous combattons les clichés.Rejoignez notre chal...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Esquive la Boule de Feu&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCYPwq5uafGQrr4PW8GQRxew&lt;/td&gt;
  &lt;td&gt;Chaîne d&amp;#x27;analyse de game design sur des jeux divers et variés, avec des touches d&amp;#x27;humour et parfois de commentaire politique quand le sujet s&amp;#x27;y prête. Le for...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Et tout le monde s&amp;#x27;en fout&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC-2EkisRV8h9KsHpslQ1gXA&lt;/td&gt;
  &lt;td&gt;Notre monde est stupéfiant et mal barré, et ça nous fait saigner du nez. Et si on arrêtait de s&amp;#x27;en foutre ?Web série bi-mensuelle alternant toutes les deux s...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Fabien Campaner&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCn2odfT6D4WPg4y-zRIgU5g&lt;/td&gt;
  &lt;td&gt;Diverses émissions sur le cinéma dans lesquelles j&amp;#x27;analyse des films qui m&amp;#x27;ont marqués pour de bonnes ou de mauvaises raisons, avec de l&amp;#x27;humour dedans :- Le ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Fouloscopie&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCLXDNUOO3EQ80VmD9nQBHPg&lt;/td&gt;
  &lt;td&gt;Découvrez la fouloscopie – la science du comportement des foules : paniques, mouvement de foule, propagation de rumeurs, réseaux sociaux, intelligence collec...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;François Descraques&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCpJWP5_DNPdIhrJIpRkZPoA&lt;/td&gt;
  &lt;td&gt;Réalisateur / Scénariste. J&amp;#x27;ai créé cette chaîne principalement pour mon feuilleton audio &amp;quot;Mystères à St-Jacut&amp;quot;.Mes faits d&amp;#x27;armes : Le Visiteur du Futur, la ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Half as Interesting&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCuCkxoKLYO&lt;em&gt;EQ2GeFtbM&lt;/em&gt;bw&lt;/td&gt;
  &lt;td&gt;Education-y explainer videos that are almost good enough to watch.&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Herodot&amp;#x27;com&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCWWzB99AURYo2KLzCReWqmA&lt;/td&gt;
  &lt;td&gt;Salut Camarades!Laissez-moi vous montrer l&amp;#x27;Histoire de fond en comble, avec de gros bouquins, des vannes moisies, de grosses bibliographies, des références l...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Heu?reka&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC7sXGI8p8PvKosLWagkK9wQ&lt;/td&gt;
  &lt;td&gt;Vous ne comprenez rien à la finance et à l&amp;#x27;économie ? Vous êtes au bon endroit !Les épisodes sont écrits comme des dialogues entre deux personnages afin de p...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Histoire Appliquée&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCcT7B4zCzrfywO2Q19OJIzA&lt;/td&gt;
  &lt;td&gt;Une chaîne où l&amp;#x27;on teste l&amp;#x27;Histoire, et on la rend vivante ! Parce que si l&amp;#x27;Histoire se raconte elle peut aussi s&amp;#x27;expérimenter et se vivre... Alors on va en ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;horizon-gull&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCGeFgMJfWclTWuPw8Ok5FUQ&lt;/td&gt;
  &lt;td&gt;Chaîne officielle du site &amp;quot;Hacking social&amp;quot; ( https://www.hacking-social.com/ )Pour nous soutenir : https://fr.tipeee.com/hacking-socialhttps://liberapay.com/...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Hygiène Mentale&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCMFcMhePnH4onVHt2-ItPZw&lt;/td&gt;
  &lt;td&gt;&amp;quot;Si on avait un vrai système éducatif dans ce pays, on y donnerait des cours d&amp;#x27;Autodéfense-Intellectuelle&amp;quot;Zététique , Scepticisme, Etude scientifique des phé...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Hypallage&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCsZ1CqyrMoadedIBuKl1nyw&lt;/td&gt;
  &lt;td&gt;J&amp;#x27;analyse des œuvres de fictions diverses, ou plutôt je partage ce que j&amp;#x27;ai compris de leur construction et ce qui m&amp;#x27;a fait les aimer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;J&amp;#x27;m&amp;#x27;énerve pas, j&amp;#x27;explique&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC1Ue7TuX3iH4y8-Qrjj-hyg&lt;/td&gt;
  &lt;td&gt;Physique, Histoire des sciences et quasi-blagounettes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Joueur Du Grenier&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC_yP2DpIgs5Y1uWC0T03Chw&lt;/td&gt;
  &lt;td&gt;Test de jeux à la con !&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Kurzgesagt – In a Nutshell&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q&lt;/td&gt;
  &lt;td&gt;Animation videos explaining things with optimistic nihilism since 12,013.We’re a team of illustrators, animators, number crunchers and one dog who aim to spa...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;L&amp;#x27;Histoire par les cartes&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCQ3ZULRPs2T-hlCBd9-1vtA&lt;/td&gt;
  &lt;td&gt;Je réalise de animations cartographiques. L’objectif de mes vidéos est de donner de courtes introductions (+/- 5minutes) sur divers sujets historiques. Vous ...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;L&amp;#x27;Histoire trouve toujours un chemin&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UChD5UkL8LtcLk_D0-obwW8w&lt;/td&gt;
  &lt;td&gt;Ici on parle d&amp;#x27;Histoire, on est sérieux sur les faits, moins sur le reste.Membre du label Hérodote. https://label-herodote.com/Disclaimer : L&amp;#x27;objectivité tot...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;La Manie Du Cinéma&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCJVtE8BwsjVxt6W-lTA-8sA&lt;/td&gt;
  &lt;td&gt;How about I show you behind the scenes of the movie world?Anecdotes, small and big stories of the 7th Art, hidden references, let’s sneak behind the big scre...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Lanterne Cosmique&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCWYxQt9Y3y2CDzXLf2K7xCA&lt;/td&gt;
  &lt;td&gt;De l&amp;#x27;hypercuriosité, des questions et de la Science. Notre Univers est badass.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Le Fossoyeur de Films&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCwbV8cTR4yBgFdfa_BXV2OA&lt;/td&gt;
  &lt;td&gt;Bienvenue sur cette chaîne où l&amp;#x27;on va explorer les mystères du cinéma, voyager et ne pas oublier de prendre un bon café au passage.Le visuel du bandeau est s...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;LegalEagle&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCpa-Zb0ZcQjTCPP1Dx_1M8Q&lt;/td&gt;
  &lt;td&gt;Do you want to know how our legal system works?  You’ve come to the right place.  LegalEagle is all about giving you an insider’s view to the legal system.  ...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Le monde en cartes&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCYA_ElxMgkJlvcKa4SM0dOg&lt;/td&gt;
  &lt;td&gt;Cette chaîne a pour but de traiter différents sujets par le prisme de la cartographie.&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Le Point Genius&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCXO8f8SAVP02zSAxnSvtYHw&lt;/td&gt;
  &lt;td&gt;&amp;quot;L&amp;#x27;intelligence vient du latin intellegere, relier les choses entre elles. L&amp;#x27;idiot dit qu&amp;#x27;il voit pas le rapport….&amp;quot;Chaîne consacrée aux génies en tout genre ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Le Réveilleur&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC1EacOJoqsKaYxaDomTCTEQ&lt;/td&gt;
  &lt;td&gt;Les éléments scientifiques disponibles m&amp;#x27;ont convaincu que les problèmes environnementaux (changement climatique, érosion de la biodiversité... etc) vont con...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Les Revues du Monde&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCnf0fDz1vTYW-sl36wbVMbg&lt;/td&gt;
  &lt;td&gt;Les Revues du Monde - Émission culturelle sur l&amp;#x27;archéologie, l&amp;#x27;Histoire, l&amp;#x27;Anthropologie, les découvertes.Je suis Charlie Danger, je réalise, écris et présen...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;le Stagirite&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCtyONQLs6htK-wWiCAk4wVw&lt;/td&gt;
  &lt;td&gt;Ma chaîne elle est bien.Les messages par ici : lestagirite.topoi(at)gmail.com&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Le Vortex&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCZxLew-WXWm5dhRZBgEFl-Q&lt;/td&gt;
  &lt;td&gt;Bienvenue dans le Vortex !Une colocation pour les mélanger tous, une colocation pour les écouter tous, une colocation pour apprendre de tous et dans le Vorte...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Linguisticae&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCofQxJWd4qkqc7ZgaLkZfcw&lt;/td&gt;
  &lt;td&gt;Une expression qui te turlupine? Un mot étrange dont tu ne connais pas l&amp;#x27;origine? Tu t&amp;#x27;intéresses aux Sciences du Langage mais les déclinaisons ne sont pas t...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;LinksTheSun&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCfXXAQ-mp1uUcvSpvMcAAtw&lt;/td&gt;
  &lt;td&gt;Bienvenue sur la chaîne de LinksTheSun, des Points Culture, des critiques de paroles de chansons, des résumés de film, des chansons, des parodies et plein d&amp;#x27;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Mathieu Sommet&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCCpwMG0qZkr62FNZktfcvYg&lt;/td&gt;
  &lt;td&gt;Vidéaste indépendant depuis 2011. En ce moment, je bosse sur ma nouvelle émission : « OVNI »&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Mediapart&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCdnaDhU-LDQrIEEmSIfq0-Q&lt;/td&gt;
  &lt;td&gt;Journal d&amp;#x27;information numérique, indépendant et participatif. Seuls nos lecteurs peuvent nous acheter.Depuis 2008, Mediapart s’est fait connaître par ses inf...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Meeea&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCEEi1rclmzywXicV7JfPjWw&lt;/td&gt;
  &lt;td&gt;On y cause adaptations, Comics, Cinéma et Jeux vidéo.En plus c&amp;#x27;est gratos.Seal Of Quality SINCE 2010&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;META&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCUOgJaic4p3kwuN7LDJ5ukA&lt;/td&gt;
  &lt;td&gt;META est une chaîne de philosophie, animée par Jim Gabaret et Céline Marty, dont le but est de populariser les grandes thèses classiques et contemporaines de...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Mickaël Launay&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC4PasDd25MXqlXBogBw9CAg&lt;/td&gt;
  &lt;td&gt;La chaîne Micmaths vous propose diverses vidéos autour des mathématiques. Vous y trouverez des cours de maths, des jeux de logique, des chroniques ou encore ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;minutephysics&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCUHW94eEFW7hkUMVaZz4eDg&lt;/td&gt;
  &lt;td&gt;Simply put: cool physics and other sweet science.&amp;quot;If you can&amp;#x27;t explain it simply, you don&amp;#x27;t understand it well enough.&amp;quot;~Rutherford via Einstein? (wikiquote)C...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;MisterJDay&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC2uPHgyyLv2rnkm4CfIgWcw&lt;/td&gt;
  &lt;td&gt;Analyses de clips / Analyses de pubs / Culture Tube / Vidéos randomBy Gael K. / M. Connard / JDay / Kama / Hardisk / Paniac&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Modern Vintage Gamer&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCjFaPUcJU1vwk193mnW_w1w&lt;/td&gt;
  &lt;td&gt;I love Modern, I love Retro.Tech, Hardware, Gaming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;monsieur bidouille&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCSULDz1yaHLVQWHpm4g_GHA&lt;/td&gt;
  &lt;td&gt;Vidéaste spécialisé vulgarisation technique, DIY, réseau et énergie.Pour tous contact, veuillez passer par le formulaire sur mon site : http://monsieurbidoui...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Monsieur Phi&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCqA8H22FwgBVcF3GJpp0MQw&lt;/td&gt;
  &lt;td&gt;Docteur en philosophie, ce qui ne soigne pas grand chose. 📚 Mon livre &amp;quot;Curiosités philosophiques, de Platon à Russell&amp;quot; : https://amzn.to/3hIshrw 🎧 La versi...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Mr. Sam - Point d&amp;#x27;interrogation&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCh2YBKhYIy-_LtfCIn2Jycg&lt;/td&gt;
  &lt;td&gt;Mr.Sam - Prenons soin de la MÉTHODE avec laquelle on se met des trucs dans le crâne.LES ÉMISSIONSFake ? • Que penser d&amp;#x27;un sujet lorsqu&amp;#x27;on y applique les méth...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;NEXUS VI&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC8-UThnwzBI5ApzVG4MY7VQ&lt;/td&gt;
  &lt;td&gt;Embarquez à bord du Nexus VI et suivez le Capitaine ainsi que son équipage aux quatre coins de la Galaxie pour votre chronique spécialisée dans la Science-Fi...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;NileRed&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCFhXFikryT4aFcLkLw2LBLA&lt;/td&gt;
  &lt;td&gt;Capturing the natural beauty of chemistryI find that chemistry is often taught poorly or without a purpose. Because of this, people tend to lose interest and...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Officiel DEFAKATOR&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCU0FhLr6fr7U9GOn6OiQHpQ&lt;/td&gt;
  &lt;td&gt;On défake sur les fakes. Et que l&amp;#x27;obscurantisme retourne à l&amp;#x27;obscurité !Defakator est un super héros. Du coup il porte un costume, avec un masque et une cape...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;OLYDRI&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCdHXMcWxwZr2qQpdCCOcSkw&lt;/td&gt;
  &lt;td&gt;NOOB est un univers étendu sur le thème du MMORPG. Il s&amp;#x27;agit d&amp;#x27;une licence transmédia existant en web-série, bande dessinée, roman, light novel, manga, jeu v...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;OverSimplified&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCNIuvl7V8zACPpTmmNIqP2A&lt;/td&gt;
  &lt;td&gt;Explaining things in an OverSimplified way&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;pannenkoek2012&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCMm211NGh4Ls5SAMZJF7E8A&lt;/td&gt;
  &lt;td&gt;I make videos about Super Mario 64.&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Passe-Science&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCsz9DiwPtgDvxJ-njWnieZw&lt;/td&gt;
  &lt;td&gt;Chaîne de vulgarisation scientifique: physique, mathématiques et informatique. Un niveau de vulgarisation avancé pour approfondir ce que vous aurez pu déjà e...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Passé sauvage&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCLhOJJbPciPdocXTaAk2SdA&lt;/td&gt;
  &lt;td&gt;Archéologie I Anthropologie I HistoireSérie documentaire Odyssée sauvage.&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Primitive Technology&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCAL3JXZSzSm8AlZyD3nQdBA&lt;/td&gt;
  &lt;td&gt;Primitive technology is a hobby where you build things in the wild completely from scratch using no modern tools or materials. These are the strict rules: If...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Primum Non Nocere&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC9BnGZLT4iPaJtDOXYwQuHQ&lt;/td&gt;
  &lt;td&gt;Salut à toi internaute !Bienvenu sur Primum Non NocereJe suis Primum et accompagné de mon Doppelganger Doppel, on va essayer de te parler de médecine autreme...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Questions d&amp;#x27;Histoire&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCCGRtSqLfljpX9mzCYDsQIg&lt;/td&gt;
  &lt;td&gt;Questions d’Histoire est une chaine de vulgarisation historique traitant de nombreuses thématiques et problématiques. Plusieurs formats sont développés.Histo...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;ratelrock&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCaVWxVObKsthKR92x6bz7zA&lt;/td&gt;
  &lt;td&gt;Une tasse à café en colère qui parle de films, souvent de mauvais films, mais de films avant tout!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Real Engineering&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCR1IuLEqb6UEA_zQ81kwXfg&lt;/td&gt;
  &lt;td&gt;Interesting answers to simple questions.&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;RealLifeLore&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCP5tjEmvPItGyLhmjdwP7Ww&lt;/td&gt;
  &lt;td&gt;Answers to questions that you&amp;#x27;ve never asked. Welcome to the RealLifeLore communityPlease subscribe and ring the bellBusiness Email: reallifelore@standard.tv&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Retro Game Mechanics Explained&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCwRqWnW5ZkVaP_lZF7caZ-g&lt;/td&gt;
  &lt;td&gt;A YouTube channel about video games &amp;amp; programming. For inquiries, please visit inquiries.rgmechex.com.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;ScienceClic&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCV6uM3y-8TeO7JlCtcEN-Bg&lt;/td&gt;
  &lt;td&gt;D&amp;#x27;où venons-nous ? Dans quel monde vivons nous ? En quelques minutes, découvrez et comprenez les théories scientifiques les plus complexes, à la base du cosm...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Science de comptoir&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCE_LWXcsdp7cWbo4DR4ZA-A&lt;/td&gt;
  &lt;td&gt;⚒ De la géologie (n&amp;#x27;)en veux-tu (pas) en voilà (quand même) !Vous êtes-vous déjà demandé pourquoi lécher un caillou ? Comment se forme le sable ? Comment per...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;ScienceEtonnante&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCaNlbnghtwlsGF-KzAFThqA&lt;/td&gt;
  &lt;td&gt;Des vidéos pour raconter la science étonnante, amusante, passionnante et stupéfiante !Retrouvez moi aussi sur mon blog &amp;quot;Science étonnante&amp;quot; http://sciencetonn...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Simplex Paléo&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCej7tmvtZ_ex9dZE-ZYS0EQ&lt;/td&gt;
  &lt;td&gt;L&amp;#x27;évolution des espèces à travers les périodes géologiques...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Spirit Of The Law&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UChzLZJo-SxuPHz-oYKAIC_g&lt;/td&gt;
  &lt;td&gt;Age of Empires 2 Youtuber&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;StudioUha&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCkjJda7HO1WVVhUsMrDmYuA&lt;/td&gt;
  &lt;td&gt;Studio Uha!, ce sont deux fiers larrons shootés à la vidéo qui vont vous botter le cul à coups de micro-séries chatoyantes.Retrouvez-nous sur www.facebook.co...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Stupid Economics&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCyJDHgrsUKuWLe05GvC2lng&lt;/td&gt;
  &lt;td&gt;Stupid Economics is a French-speaking YouTube channel of journalism and economic popularization, interpreted by Arnaud and produced by Valentin.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Sur le champ&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCiWrr_SnP8eK-_9yJWFzbEA&lt;/td&gt;
  &lt;td&gt;Le chaîne Sur le Champ est une chaîne de vulgarisation de l&amp;#x27;histoire de la guerre. A travers des batailles ou des campagnes historiques dont nous explorons l...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Techniques Spatiales&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCwf-gTeemly9VOVALdBXiTw&lt;/td&gt;
  &lt;td&gt;Chaine de curiosité scientifique sur les technologies spatiales.&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Technology Connections&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCy0tKL1T7wFoYcxCe0xjN6Q&lt;/td&gt;
  &lt;td&gt;Share your videos with friends, family, and the world&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;The 8-Bit Guy&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC8uT9cgJorJPWu7ITLGo9Ww&lt;/td&gt;
  &lt;td&gt;A computer video series focusing mainly on retro technology from the 80s, 90s and 2000s. However, once or twice a year I&amp;#x27;ll create something totally &amp;quot;out the...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;The Sciencoder&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC1zVvHsRsvxInUMX4dfIyAQ&lt;/td&gt;
  &lt;td&gt;The Unbolter. Bientôt.Mail pro : thesciencoder@harydprod.com&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;The Urban Doctor&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC33WWpCRHft51iEV2hVcRzQ&lt;/td&gt;
  &lt;td&gt;Ever wonder why people daydream of European towns? Or why your city&amp;#x27;s core isn&amp;#x27;t bustling with energy like New York? The answers probably have something to d...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Tzitzimitl - Esprit Critique&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC0yPCUmdMZIGtnxSnx5_ifA&lt;/td&gt;
  &lt;td&gt;Esprit Critique est une chaîne pour développer bah... votre esprit critique.On y fait de la vulgarisation/réfléxion politico-philosophico-historico-scientifi...&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;UnDropDansLaMare&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCbq4WwLfbhRxvyVYOTYU25g&lt;/td&gt;
  &lt;td&gt;Le site UnDropDansLaMare est l&amp;#x27;interlocuteur de ceux qui souhaitent parler du jeu vidéo sérieusement. Nous vous proposons nos réflexions et nos réponses, vou...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Une bière et Jivay&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCwzxu1j19ICnfW5zrBvUw-w&lt;/td&gt;
  &lt;td&gt;La bière, comme vous ne l&amp;#x27;avez jamais bue !&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Veritasium&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCHnyfMqiRRG1u-2MsSQLbXA&lt;/td&gt;
  &lt;td&gt;An element of truth - videos about science, education, and anything else I find interesting.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Vous Avez Le Droit&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UCynBu7PDfJsCXeCSqT1QUQw&lt;/td&gt;
  &lt;td&gt;La justice est quelque chose de trop sérieux pour la laisser aux juristes !Je ne dis pas que des conneries, j&amp;#x27;en filme également : des formats courts qui vul...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Wendover Productions&lt;/td&gt;
  &lt;td&gt;https://www.youtube.com/channel/UC9RM-iSvTu1uPJb8X5yp3EQ&lt;/td&gt;
  &lt;td&gt;Wendover Productions is all about explaining how our world works. From travel, to economics, to geography, to marketing and more, every video will leave you ...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</summary></entry><entry><title>Hello, world</title><link href="https://jtremesay.org/posts/hello-world.html" rel="alternate"/><published>2023-05-31T20:00:00+02:00</published><id>https://jtremesay.org/posts/hello-world.html</id><summary type="html">&lt;p&gt;Encore un nouveau Hello world, sur une nouvelle itération du blog, qui va encore une fois rester désespérément vide :D&lt;/p&gt;

&lt;p&gt;Je veux un truc simple. J'écris mon contenu dans mon éditeur de texte préféré, je commit, je push, et ka-boom, le site est mis à jour. Idéalement, je voudrais un site complètement statique. Du bon vieux HTML servi par un bon vieux nginx. Malheureusement, je n'ai pas trouvé mon bonheur dans les trucs existants (principalement Pelican, Hugo et Jekyll) (disclaimer: j'ai pas non plus trop cherché). Pis y'a nécessairement où je voudrais faire des trucs pas prévus pour. Genre des expérimentations en JS pour illustrer certains articles. Mais voila, j'ai la flemme d'écrire du bon vieux HTML. Pis j'aime beaucoup la méta-programmation et les trucs qui génèrent des bidules.&lt;/p&gt;

&lt;p&gt;Du coup, voila ma solution, pas du tout &lt;code&gt;sur-ingénieurée &amp;lt;https://xeiaso.net/talks/how-my-website-works&amp;gt;&lt;/code&gt;_. Le contenu écrit sous forme de templates Jinja, et servi par une app Flask maison. Comme tout est sous forme de template, je peux écrire mes articles depuis le comfort de mon éditeur de texte, tout en ayant une bonne séparations entre la partie contenu et la partie présentation.&lt;/p&gt;

&lt;p&gt;Un post ressemble à ça :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% extends &amp;quot;base.html&amp;quot; %}

{% block title %}Hello, world{% endblock %}

{% block content %}
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Mon super article
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;On a déjà vu plus simple. Certes. Mais on a aussi pire :shrug:&lt;/p&gt;

&lt;p&gt;La vue flask s'occupant d'afficher ce post :&lt;/p&gt;

&lt;div class="codehilite"&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;lt;int:year&amp;gt;/&amp;lt;int:month&amp;gt;/&amp;lt;int:day&amp;gt;/&amp;lt;slug&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;view_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;blog/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;04&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;02&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;02&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TemplateNotFound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Simple et efficace. Y'a que la partie découverte des posts pour générer l'index qui est «créative» et assez insatisfaisante. Mébon, si j'attends d'être satisfait de la solution avant de publier du contenu, on est pas arrivés :D&lt;/p&gt;

&lt;p&gt;Fin bref, bienvenue dans mon antre :)&lt;/p&gt;
</summary></entry><entry><title>kFPGA et DEL qui clignotent</title><link href="https://jtremesay.org/posts/kfpga-et-del-qui-clignotent.html" rel="alternate"/><published>2019-11-27T00:00:00+02:00</published><id>https://jtremesay.org/posts/kfpga-et-del-qui-clignotent.html</id><summary type="html">&lt;p&gt;(Billet que j'avais publié sur un précédent blog et que je pensais irrémédiablement perdu jusqu'à ce que j'en trouve une copie sur la &lt;a href="https://web.archive.org/web/20200423094849/https://blog.slaanesh.org/kfpga-et-del-qui-clignotent.html#kfpga-et-del-qui-clignotent"&gt;Wayback Machine&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="/static/jtremesay/images/qmtech_board_running_adder2_implemented_on_k1g4.726a8366bf14.jpg"&gt;&lt;img src="/static/jtremesay/images/qmtech_board_running_adder2_implemented_on_k1g4_thumb.7a6f08ff3568.jpg" alt="Un FPGA branché à des LEDS montrant la fonctionnalité d'additionneur" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lors de l'&lt;a href="https://linuxfr.org/users/killruana/journaux/k1g1-le-premier-fpga-libre"&gt;épisode précédent&lt;/a&gt;, j'avais annoncé avoir validé le fonctionnement de l'architecture kFPGA - ou du moins celui de son plus petit cœur possible - par simulation. Comme son nom l'indique, la simulation consiste à faire simuler le circuit par l'ordinateur. C'est donc un moyen facile de voir son circuit fonctionner. Et avec l'usage de banc de tests, il est possible d'automatiser l'exécution du circuit afin de s'assurer qu'il fonctionne comme attendu malgré. C'est un peu nos tests unitaires à nous. La simulation est donc un outil important dans la conception d'un circuit intégré. Mais je trouve que la simulation possède 2 problèmes majeurs : elle est imbitable et abstraite. Pour le côté imbitable, je pense qu'une image vaut mieux que milles mots.
Simulation d'un Adder 2 bits mappé dans k1g4&lt;/p&gt;

&lt;p&gt;&lt;a href="/static/jtremesay/images/kfpga_k1g4_adder_2_simulation.03630e383108.png"&gt;&lt;img src="/static/jtremesay/images/kfpga_k1g4_adder_2_simulation_thumb.2ea1d88e7443.png" alt="Simulation d'un Adder 2 bits mappé dans k1g4" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Là, c'est le résultat de l'exécution d'un banc de test validant le fonctionnement d'un additionneur deux bits mappé dans un cœur kFPGA. Quand j'avais dit que c'était imbitable…&lt;/p&gt;

&lt;p&gt;Pour le côté abstrait, c'est plus un truc personnel. Voir des vagues de couleurs et des lignes de log sur un écran, ce n'est pas assez concret pour moi. J'ai &lt;strong&gt;besoin&lt;/strong&gt; de toucher le truc (c'est là que je me dis que l'informatique n'était peut-être pas le meilleur choix de carrière pour moi…). Et comment fait-on pour exécuter réellement un circuit intégré sans l'envoyer en fonderie ? On le met dans un FPGA ! #FPGAception. Mais pour éviter des histoires d'œuf et de poule, j'ai choisi de me tourner vers une carte de développement QMTech basé sur un FPGA &lt;a href="https://www.xilinx.com/products/silicon-devices/fpga/artix-7.html"&gt;Xilinx Artix 7&lt;/a&gt; 100T. J'ai choisi cette carte parce qu'elle intègre un gros FPGA (100k logic cells !) et beaucoup d'I/O pour vraiment pas cher ($60)&lt;/p&gt;

&lt;p&gt;&lt;a href="/static/jtremesay/images/QMTECH-Xilinx-FPGA-Artix7-Artix-7-XC7A100T-Core-Board.webp"&gt;&lt;img src="/static/jtremesay/images/QMTECH-Xilinx-FPGA-Artix7-Artix-7-XC7A100T-Core-Board_thumb.jpg" alt="Photo d'une carte Xilinx Artix-7 XCA7A100T de QMTECH" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seul bémol, il a fallu que je soude les connecteurs d'IO moi même. 120 putains de pins à souder. J'ai jamais était très bon pour souder des composants et j'avais rien pour me faire la main. Du coup, pas le choix, c'est avec beaucoup d'appréhension j'ai appris sur le tas. Comme attendu, les premières soudures étaient franchement catastrophique. Mais sur la fin, ça commençait à être acceptables. Du coup, je suis repassé sur toute les pins. Ce qui nous fait plus de 200 points de soudure. Youhou.&lt;/p&gt;

&lt;p&gt;Je vous passe les galères pour faire reconnaitre le FPGA par le programmeur (spoiler alert, ça marche mieux quand on installe les pilotes), la prise en main du dit programmeur, les premiers tests (youhou, j'arrive à faire clignoter des DEL o/).&lt;/p&gt;

&lt;p&gt;Fun fact : Malgré mes 4 ans dans une startup proposant des softs &amp;amp; hard IP de eFPGA, c'était la première fois de ma vie que j'utilisais un FGPA.&lt;/p&gt;

&lt;p&gt;Une fois cette prise en main terminée, il a fallut faire rentrer mon kFPGA dans le Xilinx. Puis aussi de quoi le programmer. Et une ROM pour contenir le bitstream. Et un générateur de signaux parce que j'aime bien quand ça fait des trucs tout seul. Et un diviseur de clock parce que sinon la STA n'était pas validée. Au final, je me suis retrouvé avec ce design :&lt;/p&gt;

&lt;p&gt;&lt;a href="/static/jtremesay/images/kfpga_k1g4_adder_2_vivado_schematic.9f359fabf658.png"&gt;&lt;img src="/static/jtremesay/images/kfpga_k1g4_adder_2_vivado_schematic_thumb.053dc029067c.png" alt="Schematic du kFPGA prêt à être implémenté dans le FPGA Xilinx" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bref, après un peu de bidouillage, j'ai fini par avoir un cœur kFPGA implémenté sur un Xilinx qui réalise des additions 2 bits et dont on peut visualiser le fonctionnement avec des DEL. Le résultat est visible en vidéo ici.&lt;/p&gt;

&lt;p&gt;Je suis content :)&lt;/p&gt;
</summary></entry></feed>