<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Metal3d</title>
    <link>https://www.metal3d.org/</link>
    <description>Recent content on Metal3d</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>fr-fr</language>
    <copyright>Patrice Ferlet, tout droits réservés</copyright>
    <lastBuildDate>Sun, 28 Sep 2025 09:27:35 +0200</lastBuildDate><atom:link href="https://www.metal3d.org/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Oui, python peut rivaliser avec C et Rust</title>
      <link>https://www.metal3d.org/blog/2025/python-peut-rivaliser-avec-c-et-rust/</link>
      <pubDate>Sun, 28 Sep 2025 09:27:35 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2025/python-peut-rivaliser-avec-c-et-rust/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2025/python-peut-rivaliser-avec-c-et-rust/cover.png&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Je ne sais pas si c&amp;rsquo;est par frustration ou par snobisme, mais Python n&amp;rsquo;a jamais été aussi critiqué pour sa &amp;ldquo;lenteur&amp;rdquo;
que depuis qu&amp;rsquo;il est le langage le plus utilisé au monde.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;On ne choisit pas Python pour sa vitesse d&amp;rsquo;exécution&lt;/strong&gt;, mais pour sa vitesse de production.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Cependant, la plupart des détracteurs de Python oubli une chose importante, c&amp;rsquo;est que Python est utilisé comme un langage d&amp;rsquo;interface à des modules développés dans d&amp;rsquo;autres langages.&lt;/p&gt;
&lt;p&gt;Python est très utilisé dans le domaine de l&amp;rsquo;IA. Vous êtes conscient que s&amp;rsquo;il y a bien un domaine dans lequel il est nécessaire d&amp;rsquo;avoir de la vitesse, c&amp;rsquo;est bien celui-là !  Pourtant, il reste le langage favori des chercheurs en data-science. &lt;strong&gt;C&amp;rsquo;est tout simplement parce que TensorFlow, PyTorch, NumPy et autres bibliothèques de
calcul intensif ne sont pas directement développés en Python.&lt;/strong&gt; Python nous donne accès à des trucs codés en C, C++, Rust, etc.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors, sommes-nous limité à devoir développer des modules en C/C++/Rust pour faire du calcul intensif ? &lt;strong&gt;Pas
forcément.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Depuis 2007, une série d&amp;rsquo;outils dont vous avez certainement entendu parler, permettent de compiler du code Python et
accéder à des performances proches de celles de C/C++/Rust. Et son nom, vous l&amp;rsquo;avez certainement déjà croisé, c&amp;rsquo;est
&lt;a href=&#34;https://cython.org/&#34;&gt;Cython&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Importez &lt;code&gt;from cython import int, ccall&lt;/code&gt; dans votre module Python, ajoutez &lt;code&gt;@ccall&lt;/code&gt; au-dessus de vos fonctions. Importez
aussi les types que vous voulez utiliser (&lt;code&gt;float, double, char&lt;/code&gt;, etc) depuis &lt;code&gt;cython&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ajoutez les annotations de type aux arguments et aux retours de vos fonctions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from cython import int, float, ccall

@ccall
def votre_fonction(arg1: int, arg2: float) -&amp;gt; float:
    # votre code ici
    pass
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compilez votre module avec la commande :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cythonize -i -3 votre_module.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fini&amp;hellip; Votre module rivalise avec C et Rust.&lt;/p&gt;
&lt;p&gt;Maintenant, si vous voulez comprendre ce qu&amp;rsquo;il se passe, lisez la suite.&lt;/p&gt;
&lt;h2 id=&#34;cython&#34;&gt;Cython&lt;/h2&gt;
&lt;p&gt;À ne pas confondre avec CPython qui est le nom
que l&amp;rsquo;on donne à Python pour le différencier des autres implémentations (Jython, PyPY, mypy&amp;hellip;). Cython, j&amp;rsquo;en avais
déjà parlé dans un vieil article, mais il traitait essentiellement d&amp;rsquo;accéder à des librairies partagées.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Là, on va vraiment faire du code, et mesurer.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Cython peut faire plusieurs choses :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compiler du code Python en passant par du C&lt;/li&gt;
&lt;li&gt;Utiliser un langage adapté, le Pyrex, qui permet d&amp;rsquo;utiliser des types statiques&lt;/li&gt;
&lt;li&gt;S&amp;rsquo;intégrer dans un module pour proposer des types et des décorateurs pour améliorer la compilation&lt;/li&gt;
&lt;li&gt;Et pas mal de choses&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;aime beaucoup Pyrex, mais je vais vous montrer une chose que j&amp;rsquo;ai pris l&amp;rsquo;habitude de faire pour gagner du temps.&lt;/p&gt;
&lt;p&gt;Si vous avez pris l&amp;rsquo;habitude d&amp;rsquo;annoter vos fonctions (avec des types que Python n&amp;rsquo;utilise pas), alors ça va être ultra-simple.&lt;/p&gt;
&lt;p&gt;Prenons l&amp;rsquo;exemple d&amp;rsquo;une suite de Fibonacci. C&amp;rsquo;est un classique, mais c&amp;rsquo;est un bon exemple pour faire un test de
performances.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# fib.py
def fib(n: int) -&amp;gt; int:
    if n &amp;lt;= 1:
        return n
    return fib(n - 1) + fib(n - 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce bout de code, si je l&amp;rsquo;appelle avec &amp;ldquo;n=40&amp;rdquo;, sur ma machine, il va prendre 10 secondes.&lt;/p&gt;
&lt;p&gt;Pour les mesures, je vais l&amp;rsquo;appeler avec ce bout de code :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import sys
from fib import fib

iteration= sys.argv[1] if len(sys.argv) &amp;gt; 1 else 1
for i in range(int(iteration)):
  fib(40)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si je le fait tourner plusieurs fois, c&amp;rsquo;est pour éviter d&amp;rsquo;avoir un biais dû au &amp;ldquo;warm up&amp;rdquo;. Parce que si je ne l&amp;rsquo;appelle
qu&amp;rsquo;une seule fois, Python bat Rust à pleines coutures. Cette hérésie me vaudrait un bon vieux shitstorm et j&amp;rsquo;ai pas envie
de passer mon temps à bannir des trolls.&lt;/p&gt;
&lt;p&gt;En pur Python : &lt;code&gt;pythnon main.py&lt;/code&gt; ne va appeler &lt;code&gt;fib(40)&lt;/code&gt; qu&amp;rsquo;une seule fois et va prendre 10 secondes. &lt;strong&gt;10 secondes,
c&amp;rsquo;est énorme&lt;/strong&gt;. Et là, oui, Python est lent. Plus lent que la majorité des langages compilés.&lt;/p&gt;
&lt;h2 id=&#34;première-compilation-à-la-bourrin&#34;&gt;Première compilation, à la bourrin&lt;/h2&gt;
&lt;p&gt;On peut déjà compiler le code tel quel, sans rien changer. Le gain ne sera pas énorme, mais vous allez voir que ça a déjà
un impact.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cythonize -i -3 fib.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela produit un fichier &amp;ldquo;&lt;code&gt;.so&lt;/code&gt;&amp;rdquo; (sur Linux) et Python est assez malin pour savoir qu&amp;rsquo;il doit importer la librairie à la
place de &amp;ldquo;&lt;code&gt;fib.py&lt;/code&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Testons le temps d&amp;rsquo;exécution.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;time python main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette fois, le temps d&amp;rsquo;exécution de &lt;code&gt;fib(40)&lt;/code&gt; tombe à 7 secondes. Donc, un gain de 30%. Pas mal, mais on n&amp;rsquo;est toujours
pas proche de ce que je vois en C, en Rust, ou Java ou en Go.&lt;/p&gt;
&lt;p&gt;On va donc passer aux choses sérieuses.&lt;/p&gt;
&lt;h2 id=&#34;le-coup-de-pouce&#34;&gt;Le coup de pouce&lt;/h2&gt;
&lt;p&gt;La documentation est vraiment claire. Soit, vous refaites votre code en Pyrex, soit vous utilisez un peu de magie. Et
voici ce que je vous propose : &lt;strong&gt;ne pas toucher au code Python à part forcer les types et décorer la fonction.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Donc on ajoute juste deux lignes :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from cython import int, ccall

@ccall
def fib(n: int) -&amp;gt; int:
    if n &amp;lt;= 1:
        return n
    return fib(n - 1) + fib(n - 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le fait d&amp;rsquo;avoir annoté les types a été une bonne chose, car maintenant, le type &amp;ldquo;&lt;code&gt;int&lt;/code&gt;&amp;rdquo; n&amp;rsquo;est plus le &amp;ldquo;type natif&amp;rdquo; de
Python, mais un &lt;code&gt;int&lt;/code&gt; qui sera vu tel quel dans le code C que Cython va produire.&lt;/p&gt;
&lt;p&gt;Et, &lt;code&gt;ccal&lt;/code&gt;, à lire &amp;ldquo;C Call&amp;rdquo; (appel en C), va faire en sorte de produire une fonction en C et une fonction accessible depuis Python
lors de la compilation.&lt;/p&gt;
&lt;p&gt;On va compiler le module avec la commande suivante :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cythonize -i -3 fib.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là, le temps d&amp;rsquo;exécution de &lt;code&gt;fib(40)&lt;/code&gt; tombe à&amp;hellip; 0.14 seconde. Oui, vous avez bien lu, &lt;strong&gt;0.14 seconde&lt;/strong&gt;. Soit un
facteur de réduction de 100 !&lt;/p&gt;
&lt;h2 id=&#34;non-mais-attend-cest-quoi-tout-ça-&#34;&gt;Non mais attend, c&amp;rsquo;est quoi tout ça ?&lt;/h2&gt;
&lt;p&gt;Je vous laisse aller lire les documentations, mais je vais essayer de vous résumer ce qu&amp;rsquo;il se passe.&lt;/p&gt;
&lt;p&gt;Cython, à la base, permet de compiler du code Python en C. À l&amp;rsquo;origine, il fallait utiliser un langage dédié, le Pyrex,
très proche de Python. Pyrex propose des définitions spécifiques pour définir les types, les fonctions et les classes.
Et en soit, on aurait pu réécrire la fonction en Pyrex :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# fichier fib.pyx

cdef _fib(int n):
  # sera compilé en C
  if n &amp;lt;= 1:
      return n
  return fib(n - 1) + fib(n - 2)

def fib(int n):
  # enveloppe l&#39;appel C pour la rendre accessible depuis Python
  return _fib(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La définition &lt;code&gt;cdef&lt;/code&gt; permet de définir une fonction qui ne sera pas accessible depuis Python. C&amp;rsquo;est une fonction qui
sera traduite en C.&lt;/p&gt;
&lt;p&gt;Pyrex accepte aussi &lt;code&gt;def&lt;/code&gt; qui permet de définir une fonction qui sera accessible depuis Python. Et donc non compilée.
C&amp;rsquo;est pour cela que je fais, ici, une fonction qui permet d&amp;rsquo;envelopper l&amp;rsquo;appel C.&lt;/p&gt;
&lt;p&gt;Cython propose aussi &lt;code&gt;cpdef&lt;/code&gt; qui permet de faire les deux en même temps. C&amp;rsquo;est-à-dire, qu&amp;rsquo;il fait pour nous la fonction
en C et l&amp;rsquo;enveloppe Python.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;cpdef fib(int n):
  if n &amp;lt;= 1:
      return n
  return fib(n - 1) + fib(n - 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pratique, mais on reste dans un langage dédié.&lt;/p&gt;
&lt;p&gt;Mais depuis quelques années, nous avons pris l&amp;rsquo;habitude d&amp;rsquo;annoter nos fonctions Python avec des types. Python se fout de
ses annotations, c&amp;rsquo;est surtout pratique pour les IDE et les outils de vérification de type comme &lt;code&gt;mypy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Sauf que les auteurs de Cython ont eu la bonne idée de proposer de les gérer. A condition de bien définir qu&amp;rsquo;un &amp;ldquo;int&amp;rdquo;
est un &amp;ldquo;int&amp;rdquo; du langage C. C&amp;rsquo;est pour cela que j&amp;rsquo;importe &lt;code&gt;int&lt;/code&gt; depuis &lt;code&gt;cython&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Et, &lt;code&gt;@ccall&lt;/code&gt; permet de dire que cette fonction doit être compilée en C et qu&amp;rsquo;une enveloppe Python doit être créée, tout
comme on le ferait avec &lt;code&gt;cpdef&lt;/code&gt; dans un script Pyrex.&lt;/p&gt;
&lt;p&gt;Cela rend vraiment la vie plus simple. On peut garder son code Python, et juste ajouter des annotations de type et des
décorateurs. Sans compilation, le code Python est exactement le même et fonctionne de la même manière. Un coup de
&lt;code&gt;cythonize&lt;/code&gt; et nous avons toutes les optimisations de Cython.&lt;/p&gt;
&lt;h2 id=&#34;dans-un-projet&#34;&gt;Dans un projet&lt;/h2&gt;
&lt;p&gt;On ne va pas s&amp;rsquo;amuser à taper des commandes &lt;code&gt;cythonize&lt;/code&gt; à la main. Ce que l&amp;rsquo;on va faire, c&amp;rsquo;est adapter notre fichier
&lt;code&gt;pyproject.toml&lt;/code&gt; pour définir un module d&amp;rsquo;extension.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-toml&#34;&gt;[build-system]
requires = [&amp;quot;setuptools&amp;quot;, &amp;quot;cython&amp;quot;]

[tool.setuptools]
packages = [&amp;quot;example&amp;quot;]

[tool.setuptools.ext_modules]
fib = { sources = [&amp;quot;example/fib.py&amp;quot;], language = &amp;quot;c&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et quand votre projet sera installé, le module sera compilé automatiquement.&lt;/p&gt;
&lt;h2 id=&#34;on-compare-avec-c&#34;&gt;On compare avec C&lt;/h2&gt;
&lt;p&gt;Alors, on pourrait se demander si en C, on ferait mieux, et bien tentons :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

uint fib(int n) {
    if (n &amp;lt;= 1) {
        return n;
    }
    return fib(n - 1) + fib(n - 2);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, &amp;quot;Usage: %s &amp;lt;n&amp;gt;\n&amp;quot;, argv[0]);
        return 1;
    }
    int n = atoi(argv[1]);
    printf(&amp;quot;%u\n&amp;quot;, fib(n));
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si je compile avec :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gcc -Ofast -march=native -o fib fib.c
time ./fib 40
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et que je lance &lt;code&gt;./fib 40&lt;/code&gt;, j&amp;rsquo;obtiens un temps d&amp;rsquo;exécution de 0.10 seconde. Donc, oui, c&amp;rsquo;est plus rapide, mais pas de beaucoup.&lt;/p&gt;
&lt;p&gt;Avec Clang :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;clang -O3 -march=native -ffast-math -o fib fib.c
time ./fib 40
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette fois, on est à 0.04 seconde. Donc, on est 3.5 fois plus rapide que Cython.&lt;/p&gt;
&lt;p&gt;Oui, Clang est vraiment un bon compilateur. Et sur des tâches mathématiques, il est souvent plus rapide que GCC.&lt;/p&gt;
&lt;h2 id=&#34;benchmark&#34;&gt;Benchmark&lt;/h2&gt;
&lt;p&gt;Pour ne pas se faire avoir par le &amp;ldquo;warm up&amp;rdquo;, il faut appeler plusieurs fois la fonction dans le même processus. Sinon,
vous allez avoir un biais monstre. Je me suis amusé à coder la fonction en C, Go, en Rust, en Java, en pur Python avec
et sans compilation &lt;code&gt;cythonize&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pour C, je tente avec deux compilateurs : &lt;code&gt;clang&lt;/code&gt; et &lt;code&gt;gcc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Je n&amp;rsquo;ai pas trouvé comment bien utiliser &lt;code&gt;clang&lt;/code&gt; avec &lt;code&gt;cythonize&lt;/code&gt;, tous mes tests avaient un temps catastrophique. Je suis donc resté sur GCC.&lt;/li&gt;
&lt;li&gt;J&amp;rsquo;utilise OpenJDK 21.0.8 pour la version Java&lt;/li&gt;
&lt;li&gt;Go 1.25&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les options de compilation sont prévues pour optimiser au mieux :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rustc&lt;/code&gt; prend les options &lt;code&gt;-C opt-level=3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clang&lt;/code&gt; prend les options &lt;code&gt;-O3 -march=native -ffast-math&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gcc&lt;/code&gt; utilise les options &lt;code&gt;-Ofast -march=native&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il y a certainement de meilleures options, mais le but ici est de montrer que Cython donne un vrai gain.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est sans appel&amp;hellip;&lt;/p&gt;
&lt;p style=&#34;min-width: 90%; text-align: center; margin-left: auto; margin-right: auto&#34;&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/python-peut-rivaliser-avec-c-et-rust/bench-no-log.png&#34; alt=&#34;Benchmark no log&#34;&gt;&lt;/p&gt;
&lt;p&gt;Clairement, Python &amp;ldquo;pur&amp;rdquo; est complètement à la ramasse. &lt;strong&gt;Cython fait une énorme différence.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On peut, bien entendu, mettre le graphique en échelle logarithmique pour mieux voir les différences entre les langages
compilés.&lt;/p&gt;
&lt;p style=&#34;min-width: 90%; text-align: center; margin-left: auto; margin-right: auto&#34;&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/python-peut-rivaliser-avec-c-et-rust/bench-log.png&#34; alt=&#34;Benchmark log avec Python&#34;&gt;&lt;/p&gt;
&lt;p&gt;Si je supprime Python, on voit mieux les différences entre les langages compilés.&lt;/p&gt;
&lt;p style=&#34;min-width: 90%; text-align: center; margin-left: auto; margin-right: auto&#34;&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/python-peut-rivaliser-avec-c-et-rust/bench1.png&#34; alt=&#34;Benchmark&#34;&gt;&lt;/p&gt;
&lt;p&gt;Bien entendu que Rust est plus rapide (et encore heureux, c&amp;rsquo;est son seul atout&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&amp;hellip;). Go semble très lent, mais je
vous rappelle que Go n&amp;rsquo;utilise pas d&amp;rsquo;optimisation agressive comme Rust ou C/C++. Go est un langage qui compile vite, qui
a des performances honorables, mais qui n&amp;rsquo;est pas fait pour faire du calcul intensif.&lt;/p&gt;
&lt;p&gt;Java est dans la moyenne, mais il faut savoir que la JVM a un &amp;ldquo;warm up&amp;rdquo; important. Encore une fois, le but de Java n&amp;rsquo;est
pas de faire du calcul intensif, mais d&amp;rsquo;avoir des performances honorables dans un environnement spécifique.&lt;/p&gt;
&lt;p&gt;On reviendra sur le sujet dans quelques instants, mais j&amp;rsquo;insiste : on ne choisit pas un langage seulement pour sa
vitesse d&amp;rsquo;exécution. On le choisit pour ses qualités d&amp;rsquo;intégration, de structuration, de maintenabilité etc. Sinon, on
ferait tous de l&amp;rsquo;assembleur&amp;hellip;&lt;/p&gt;
&lt;p&gt;On remarque aussi que CLang est particulièrement bon. Je l&amp;rsquo;utilise plus souvent que GCC depuis quelques années, et je ne
m&amp;rsquo;étais pas vraiment penché sur ses qualités d&amp;rsquo;optimisation. Il n&amp;rsquo;est clairement pas mauvais le bougre.&lt;/p&gt;
&lt;h2 id=&#34;attention&#34;&gt;Attention&lt;/h2&gt;
&lt;p&gt;Il ne faut pas faire de déduction hâtive. Le code de Fibonacci est récursif et n&amp;rsquo;utilise pas de structures de données.
C&amp;rsquo;est un cas particulier. Mais il montre que Cython peut faire une grosse différence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Soyez sympas de ne pas utiliser cet article de manière fallacieuse.&lt;/strong&gt; Oui, Rust est super rapide, oui Go est largement
plus lent que Rust ou C.&lt;/p&gt;
&lt;p&gt;Si je choisis très souvent le Go, c&amp;rsquo;est parce qu&amp;rsquo;il est largement assez rapide, que son langage est simple pour traiter des
cas complexes d&amp;rsquo;asynchronisme, que son écosystème est riche. Je choisis Python pour traiter des données hétérogènes,
accéder aux modules d&amp;rsquo;IA de manière simple, et comme je suis près à attendre 100 ms pour qu&amp;rsquo;une réponse HTTP arrive.&lt;/p&gt;
&lt;p&gt;Je choisis rarement Rust, même si je suis parfaitement conscient de ses qualités. Mais sa syntaxe est lourde pour mon
domaine d&amp;rsquo;activité.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai arrêté le Java puisqu&amp;rsquo;il ne m&amp;rsquo;apporte absolument rien par rapport à Go, Python ou Rust.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Est-ce que vous devez faire pareil que moi ? &lt;strong&gt;NON !&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Par contre, je donne un conseil général, apprenez plusieurs langages. Cela vous permet d&amp;rsquo;appréhender plusieurs
méthodologies, plusieurs concepts que vous pouvez adapter à d&amp;rsquo;autres langages. Cela vous permet d&amp;rsquo;aller plus vite sur
certaines tâches.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Par exemple, j&amp;rsquo;ai souvent besoin de &amp;ldquo;post-process&amp;rdquo; avec Helm, pour modifier du YAML à la volée. Croyez-moi, je ne le
ferai jamais en Rust ou en C. Python est parfait pour ça.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Je vais le répéter, mais &lt;strong&gt;le but d&amp;rsquo;un langage n&amp;rsquo;est pas nécessairement la vitesse d&amp;rsquo;exécution.&lt;/strong&gt; Python est
exceptionnel pour travailler vite, traiter des données complexes sans avoir à passer des heures sur l&amp;rsquo;implémentation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rust a ses qualités, Go en a d&amp;rsquo;autres, Java aussi. Et dans les faits, tous les langages sont valables, tous ont une
qualité que les autres n&amp;rsquo;ont pas.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ce que je voulais vous montrer ici, c&amp;rsquo;est que pour Python, il existe un moyen de le faire calculer très vite, bien plus
vite que si on le laissait en &amp;ldquo;pur Python&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Je le répète, on passe de 10 secondes à 0.14 seconde !&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;le-code-pour-les-benchmarks&#34;&gt;Le code pour les benchmarks&lt;/h2&gt;
&lt;p&gt;Le code utilisé est le suivant, pour chaque langage :&lt;/p&gt;


&lt;div class=&#34;splitted&#34;&gt;
  &lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;use std::env::args;

fn fibo(n: u32) -&amp;gt; u32 {
    if n &amp;lt;= 1 {
        return n;
    }
    fibo(n - 1) + fibo(n - 2)
}

fn main() {
    let iteration: i32 = args()
        .collect::&amp;lt;Vec&amp;lt;_&amp;gt;&amp;gt;()
        .get(1)
        .expect(&amp;quot;Please provide the iteration argument&amp;quot;)
        .parse()
        .expect(&amp;quot;Iteration must be an integer&amp;quot;);


    let n = 40;
    for _ in 0..iteration {
        _ = fibo(n);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
	&amp;quot;os&amp;quot;
	&amp;quot;strconv&amp;quot;
)

func fibo(n uint) uint {
	if n &amp;lt;= 1 {
		return n
	}
	return fibo(n-2) + fibo(n-1)
}

func main() {
	iteration := os.Args[1]
	nbIteration, err := strconv.Atoi(iteration)
	if err != nil {
		panic(err)
	}

	for range nbIteration {
		_ = fibo(40)
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public class Main {
  public static void main(String[] args) {
    int iteration = Integer.parseInt(args[0]);
    for (int i = 0; i &amp;lt; iteration; i++) {
      fibonacci(40);
    }
  }

  public static int fibonacci(int n) {
    if (n &amp;lt;= 1) {
      return n;
    } else {
      return fibonacci(n - 1) + fibonacci(n - 2);
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-c&#34;&gt;#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

uint32_t fibo(uint32_t n) {
  if (n &amp;lt;= 1)
    return n;

  return fibo(n - 1) + fibo(n - 2);
}

int main(int argc, char **argv) {
  if (argc != 2)
    return EXIT_FAILURE;

  char *count = argv[1];
  int n = atoi(count);

  // launch
  for (int i = 0; i &amp;lt; n; i++)
    fibo(40);

  return EXIT_SUCCESS;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Pour Python, le code du module est celui donné dans l&amp;rsquo;article, et le script de lancement est le suivant :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import sys

from fibo import fibo

if __name__ == &amp;quot;__main__&amp;quot;:
    iteration = int(sys.argv[1]) if len(sys.argv) &amp;gt; 1 else 1
    for _ in range(iteration):
        fibo(40)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Toutes les importations sont appelées pour lancer 10 fois le calcul de &lt;code&gt;fibo(40)&lt;/code&gt;. Il m&amp;rsquo;a suffi de prendre le temps total
et le diviser par 10 pour avoir le temps moyen.&lt;/p&gt;
&lt;h2 id=&#34;au-final&#34;&gt;Au final&lt;/h2&gt;
&lt;p&gt;Bien entendu, Python est un langage interprété, et il ne sera jamais aussi rapide que C, Rust ou Go. Je ne cesserai de le
rappeler, mais on ne choisit pas un langage pour sa vitesse d&amp;rsquo;exécution. On le choisit pour sa capacité à nous
permettre de travailler dans de bonnes conditions.&lt;/p&gt;
&lt;p&gt;Cython apporte de la compilation à Python, et permet de faire du calcul intensif. Ce n&amp;rsquo;est pas la panacée, mais c&amp;rsquo;est
une vraie solution pour améliorer les performances de certains modules.&lt;/p&gt;
&lt;p&gt;Si les performances sont un critère important, alors il faut savoir que Python peut faire du calcul intensif. Au prix
d&amp;rsquo;un peu de configuration, on peut obtenir des performances très honorables, proche du C, voire de Rust.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais, si la vitesse est le premier critère, alors il faut se tourner vers des langages compilés, adaptés à la tâche.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Je ne choisis Rust que dans les cas où j&amp;rsquo;ai besoin de performances extrêmes, et où je ne peux pas me permettre d&amp;rsquo;attendre
100 ms pour une réponse. Dans ce cas je suis prêt à passer du temps à écrire du code plus lourd et moins productif. Oui,
vous l&amp;rsquo;avez compris, je ne suis pas fan de Rust&amp;hellip;&lt;/p&gt;
&lt;p&gt;Go propose un bon compromis entre vitesse, simplicité et productivité. C&amp;rsquo;est pour cela que je l&amp;rsquo;utilise très souvent.&lt;/p&gt;
&lt;p&gt;Cependant, j&amp;rsquo;utilise énormément Python, parce que c&amp;rsquo;est un langage qui me permet de faire plein de choses rapidement. Si la
vitesse me pose un souci (ce qui est rare), alors j&amp;rsquo;utilise Cython. Si je pense qu&amp;rsquo;un autre langage est plus approprié,
je l&amp;rsquo;utilise. Je neme fige pas, j&amp;rsquo;ai appris une quinzaine de langages justement pour être à l&amp;rsquo;aise dans un maximum de
situations.&lt;/p&gt;
&lt;p&gt;Chaque langage a sa place.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;oui je trolle un peu, j&amp;rsquo;ai le droit, c&amp;rsquo;est mon blog.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>PDM &#43; uv, duo de choc pour vos projets Python</title>
      <link>https://www.metal3d.org/blog/2025/pdm-&#43;-uv-duo-de-choc-pour-python/</link>
      <pubDate>Sat, 20 Sep 2025 13:39:46 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2025/pdm-&#43;-uv-duo-de-choc-pour-python/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2025/pdm-&#43;-uv-duo-de-choc-pour-python/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Si vous êtes un développeur Python, et que vous travailez avec des environnements virtuels et la gestion
des dépendances, vous avez probablement entendu parler de pipenv et poetry.&lt;/p&gt;
&lt;p&gt;Le principe reste le même : un outil pour gérer les dépendances, créer des environnements virtuels, et faciliter la
gestion d&amp;rsquo;un projet Python. Aussi, ces outils permettent de construire et pousser les paquets sur PyPI. Moins de galère
qu&amp;rsquo;un &lt;code&gt;setup.py&lt;/code&gt;, plus sécurisant en terme de &amp;ldquo;lock&amp;rdquo; de version. Tout le monde est content. Ou pas&amp;hellip;&lt;/p&gt;
&lt;p&gt;Vous allez vite comprendre de quoi je parle, et je vais vous faire un topo des trucs lourdingues avant de vous parler de
&lt;code&gt;uv&lt;/code&gt; et de &lt;code&gt;pdm&lt;/code&gt;. Mais si vous êtes pressé, un &amp;ldquo;too long; didn&amp;rsquo;t read&amp;rdquo; ça fait gagner du temps.&lt;/p&gt;
&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Ce que vous allez gagner :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gain de place &lt;strong&gt;drastique&lt;/strong&gt; sur le disque, résolution de dépendances très rapide grâce à &lt;code&gt;uv&lt;/code&gt; et un excellent support
pour les CI (build, test) et CD (publish avec gestion OIDC vers Gitlab par exemple)&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Installez &amp;ldquo;uv&amp;rdquo; et &amp;ldquo;pdm&amp;rdquo;, personnellement j&amp;rsquo;utilise &lt;code&gt;homebrew&lt;/code&gt; sur Linux:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;brew install uv pdm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sinon, &lt;code&gt;uv&lt;/code&gt; est dans les paquets de Fedora. Pour pdm, &lt;a href=&#34;https://pdm-project.org/en/latest/#other-installation-methods&#34;&gt;allez voir la doc d&amp;rsquo;installation&lt;/a&gt; pour toutes les options (pipx, pip, usr, etc.).&lt;/p&gt;
&lt;p&gt;Puis :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pdm config use_uv true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Créez votre projet avec :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pdm init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et vous pouvez installer les dépendances avec :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pdm add package1 package2  # pour les dépendances normales
pdm add --group devel package3  # pour les dépendances de dev
pdm run python script.py # pour lancer un script dans l&#39;env virtuel
pdm build # crée un paquet python
pdm publish # pousse le paquet sur PyPI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous aurez un cache d&amp;rsquo;installation globale, donc aucune réplication de paquets, les paquets sont liés avec des liens
hard (hard links). C&amp;rsquo;est super rapide, et la création de paquets est très simple.&lt;/p&gt;
&lt;p&gt;PDM permet de pousser vos paquets sur PyPI, et gère les scripts, les dépendances de développement, etc. C&amp;rsquo;est &lt;code&gt;poetry&lt;/code&gt;
mais dopé comme un coureur du tour de France.&lt;/p&gt;
&lt;h2 id=&#34;pip-puis-poetry&#34;&gt;pip, puis poetry&lt;/h2&gt;
&lt;p&gt;Qu&amp;rsquo;on se le dise, &amp;ldquo;&lt;code&gt;pip&lt;/code&gt;&amp;rdquo; est l&amp;rsquo;outil &amp;ldquo;legacy&amp;rdquo;, le vieux pépère qui accompagne Python pour installer des paquets. Il
fonctionne, il fait le job, et il sera toujours présent. Sauf que voilà&amp;hellip; très vite vous vous prenez au jeu, vous
faites des environnements virtuels, vous gérez des dépendances, et là, &amp;ldquo;&lt;code&gt;pip&lt;/code&gt;&amp;rdquo; devient vite limité. Si si, soyon
honnêtes, avoir un fichier de &amp;ldquo;requirements.txt&amp;rdquo; ça devient vite l&amp;rsquo;enfer. Outre la gestion de versions, quand vous
voulez séaprer les dépendances de développement, de production, etc. Vous avez l&amp;rsquo;impression de bricoler.&lt;/p&gt;
&lt;p&gt;Avant, on faisait ce genre de sorcellerie :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;python -m venv .venv
source .venv/bin/activate
pip install flask
pip freeze &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et on était content hein, on donnait ça à manger à nos collègues, et tout le monde tapait &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;. Et ils oubliaient de lancer &lt;code&gt;source .venv/bin/activate&lt;/code&gt; et hop ça mettait les lib dans le répertoire
utilisateur, et ça foutait le bazar. Bref, on se prenait les pieds dans le tapis.&lt;/p&gt;
&lt;p&gt;Alors sont nés des outils comme &lt;code&gt;pipenv&lt;/code&gt; et &lt;code&gt;poetry&lt;/code&gt; pour nous simplifier la vie. Et franchement, ils font le job. J&amp;rsquo;ai
clairement préféré &lt;code&gt;poetry&lt;/code&gt; à &lt;code&gt;pipenv&lt;/code&gt;, pour la simple et bonne raison qu&amp;rsquo;il utilise un fichier &lt;code&gt;pyproject.toml&lt;/code&gt; qui est
respectueux des standards Python.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Voir &lt;a href=&#34;https://peps.python.org/pep-0518/&#34;&gt;PEP 518&lt;/a&gt; pour plus de détails.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Tout devient plus clair, on passe par la commande &amp;ldquo;&lt;code&gt;poetry&lt;/code&gt;&amp;rdquo; pour tout. Démarrer le projet, ajouter des dépendances,
grouper les packages de développement, etc. C&amp;rsquo;est vraiment top.&lt;/p&gt;
&lt;p&gt;Mais&amp;hellip; mais&amp;hellip; je suis développeur Go, et un truc me chagrinait avec Python&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;la-centralisation&#34;&gt;La centralisation&lt;/h2&gt;
&lt;p&gt;Quand on fait du Go, on utilise la commande &amp;ldquo;go get&amp;rdquo; pour récupérer des dépendances. Et tout est centralisé dans un seul
et même répertoire, le fameux &lt;code&gt;mod cache&lt;/code&gt;. Ça veut dire que si vous avez 10 projets Go, et que vous utilisez la même
librairie et la même version, elle n&amp;rsquo;est téléchargée qu&amp;rsquo;une seule fois. Et, comme Go utilise un compilateur, il fait le
lien entre la version qui se trouve dans le cache.&lt;/p&gt;
&lt;p&gt;En clair, vous n&amp;rsquo;avez pas réellement d&amp;rsquo;environnement virtuel. Vous avez un cache de dépendances, et Go s&amp;rsquo;occupe de tout.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Beaucoup d&amp;rsquo;autres langages savent faire cela, par exemple Java avec Maven, Nodejs avec &amp;ldquo;&lt;code&gt;pnpm&lt;/code&gt;&amp;rdquo;, etc.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Il y a des gens bien sur LinkedIn, et quand j&amp;rsquo;ai posé la question, un certain Thomas PEDOT me dit simplement &amp;ldquo;bah
utilise UV !&amp;rdquo;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;avais déjà regardé ce projet, construit par les mêmes auteurs que &amp;ldquo;ruff&amp;rdquo; (un excellent linter pour Python), mais je
n&amp;rsquo;avais pas compris à quel point c&amp;rsquo;était génial.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.astral.sh/uv/guides/install-python/&#34;&gt;uv&lt;/a&gt; est un gestionnaire de paquets et d&amp;rsquo;environnements virtuels pour
Python qui a l&amp;rsquo;idée géniale d&amp;rsquo;utiliser des &amp;ldquo;hard links&amp;rdquo; pour faire des liens entre les paquets dans le cache et
l&amp;rsquo;environnement. &lt;strong&gt;Mais ce n&amp;rsquo;est pas tout !&lt;/strong&gt; Uv gère la résolution de dépendance de manière très (non mais vriament très)
efficace. Il réduit le temps d&amp;rsquo;installation à son maximum.&lt;/p&gt;
&lt;p&gt;Leur exemple pour installer le package &amp;ldquo;Trio&amp;rdquo; sur le page de présetnation est très parlant :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://github.com/astral-sh/uv/assets/1309177/03aa9163-1c79-4a87-a31d-7a9311ed9310&#34;
style=&#34;
background: currentColor;
width: 90vw;
padding: 0.5em;
&#34;
/&gt;&lt;/p&gt;
&lt;p&gt;Je vous disais, l&amp;rsquo;installation est centralisée, donc les packages s&amp;rsquo;installent dans un cache global. Et lorsque vous
&amp;ldquo;installez&amp;rdquo; le package dans un environnement virtuel, il crée un lien &amp;ldquo;hard link&amp;rdquo; vers le cache.&lt;/p&gt;
&lt;p&gt;En gros, voilà ce que ça donne :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/pdm-+-uv-duo-de-choc-pour-python/./uvlink.png&#34; alt=&#34;Uv liens vers projets&#34;&gt;&lt;/p&gt;
&lt;p&gt;Dans le graph ci-dessus, comprenez bien que &lt;strong&gt;les flèches représentent des liens &amp;ldquo;hard links&amp;rdquo;&lt;/strong&gt; entre les paquets dans le
cache et les environnements virtuels. &lt;strong&gt;Et non pas des copies !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Donc, uv, c&amp;rsquo;est cool. Il a un alias &lt;code&gt;uv pip&lt;/code&gt;, et j&amp;rsquo;ai deux énormes avantages :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C&amp;rsquo;est vraiment rapide;&lt;/li&gt;
&lt;li&gt;je réduis &lt;strong&gt;considérablement&lt;/strong&gt; l&amp;rsquo;occupation disque.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En passant mes projets Python sous &lt;code&gt;uv&lt;/code&gt;, sachant que j&amp;rsquo;ai régulièrement besoin de &lt;code&gt;PyTorch&lt;/code&gt; (torch), qui pèse 700Mo&amp;hellip;
sur 40 projets&amp;hellip; faites le calcul. Avec toutes les autres dépendances qui étaient dupliquées, et maintenant centralisées,
mon disque qui était à 90% d&amp;rsquo;occupation est redescendu à 60%.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est juste énorme.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;MAIS !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ouais il fallait bien un &amp;ldquo;mais&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; est bien plus soupe au lait que &lt;code&gt;poetry&lt;/code&gt;. La gestion dans le ficher
&lt;code&gt;pyproject.toml&lt;/code&gt; est un peu lourdingue, il faut forcer le chemin des sources du projet, ça demande de taper de la conf.
Je suis pas fénéant, mais j&amp;rsquo;aime bien éviter de passer trop de temps à optimiser un simple fichier de conf.&lt;/p&gt;
&lt;p&gt;Et là, arrive PDM.&lt;/p&gt;
&lt;h2 id=&#34;pdm-lautre-poetry&#34;&gt;PDM, l&amp;rsquo;autre Poetry&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://pdm-project.org/en/latest/&#34;&gt;PDM&lt;/a&gt; est un gestionnaire de projet à la &amp;ldquo;&lt;code&gt;poetry&lt;/code&gt;&amp;rdquo;, mais avec une approche plus
fidèle aux derniers standards. Et, vous savez quoi, il sait aussi faire des installations &amp;ldquo;linkées&amp;rdquo;, donc faire ce que
fait &lt;code&gt;uv&lt;/code&gt; : un cache centralisé des installations.&lt;/p&gt;
&lt;p&gt;Du moins en mettant ceci dans &lt;code&gt;~/.config/pdm/config.toml&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-toml&#34;&gt;[install]
cache = true
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Tout comme &lt;code&gt;uv&lt;/code&gt;, il fait des liens entre le cache central et les environnements virtuels.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Mais, à l&amp;rsquo;inverse de &lt;code&gt;uv&lt;/code&gt;, il est beaucoup plus simple à utiliser. Je retrouve le confort de &lt;code&gt;poetry&lt;/code&gt;, avec des options plus
claires à utiliser dans &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pour créer un projet, c&amp;rsquo;est aussi simple que :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pdm init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il sait même partir des options de &lt;code&gt;poetry&lt;/code&gt; si vous avez déjà un fichier &lt;code&gt;pyproject.toml&lt;/code&gt; existant. Mais j&amp;rsquo;ai préféré
refaire le fichier de conf, pour être sûr que tout soit propre.&lt;/p&gt;
&lt;p&gt;Bon, seul bémol, bien qu&amp;rsquo;il sache faire des installations &amp;ldquo;linkées&amp;rdquo;, il utilise &lt;code&gt;pip&lt;/code&gt;, et donc c&amp;rsquo;est lent.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et d&amp;rsquo;un coup, dans la doc, le Graal.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Je lis la doc (oui, je lis les docs, c&amp;rsquo;est fou hein), et je tombe sur ça : &lt;a href=&#34;https://pdm-project.org/en/latest/usage/uv/&#34;&gt;https://pdm-project.org/en/latest/usage/uv/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attends !!!&lt;/strong&gt; on peut utiliser &lt;code&gt;uv&lt;/code&gt; avec &lt;code&gt;pdm&lt;/code&gt; ?&lt;/p&gt;
&lt;h2 id=&#34;le-duo-de-choc-pdm--uv&#34;&gt;Le duo de choc, PDM + Uv&lt;/h2&gt;
&lt;p&gt;Une simple ligne de commande à taper, et je me dis que je suis passé à côté de ça pendant tellement de temps.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pdm config use_uv true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela ajoute une simple entrée dans le fichier de conf dans &lt;code&gt;~/.config/pdm/config.toml&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-toml&#34;&gt;use_uv = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là, la vache&amp;hellip; quand vous faites un &lt;code&gt;pdm add torch&lt;/code&gt;, paf, ça utilise &lt;code&gt;uv&lt;/code&gt;. Les dépendances &amp;ldquo;Nvidia&amp;rdquo; de la mort, avec
leur Gigaoctets de données, se pointent.&lt;/p&gt;
&lt;p&gt;Un second projet ? &lt;strong&gt;Bim, quelques millisecondes pour installer la même dépendance.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;et-cest-vraiment-efficace-dans-le-cicd&#34;&gt;Et c&amp;rsquo;est vraiment efficace dans le CI/CD&lt;/h2&gt;
&lt;p&gt;Le CD, pour &amp;ldquo;Continuous Delivery&amp;rdquo;, c&amp;rsquo;est le fait de déployer des trucs automatiquement depuis un outil qui gère ça en
fonction de règles. Par exemple, Gitlab CI/CD, Github Actions, Jenkins, etc.&lt;/p&gt;
&lt;p&gt;Mon délire avec &lt;code&gt;uv&lt;/code&gt; est allé loin, je fais pas mal de projets IA/ML et j&amp;rsquo;ai un JupyterHub (un Jupyter Lab
multi-utilisateurs) qui tourne sur ma bécane de dev. Et j&amp;rsquo;avais un vrai dileme à propos des &amp;ldquo;kernels python&amp;rdquo; pour gérer
des versions de paquets différentes, par notebook, etc. Mon idée a été de forcer un environnement virtuel notebook et de
forcer aussi &lt;code&gt;uv pip&lt;/code&gt; en lieu et place de &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et ça marche !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Donc, une soirée à mettre ça au propre, et je crée un dépôt Gitlab.&lt;/p&gt;
&lt;div style=&#34;
  display: flex;
  align-items: center;
  border-radius: 12px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.08);
  padding: 20px;
  max-width: 650px;
  margin: 20px auto;
  background: #ffffff;
  transition: transform 0.2s, box-shadow 0.2s;
&#34;&gt;
  &lt;img src=&#34;https://gitlab.com/uploads/-/system/project/avatar/74323203/logo-192x192.png?width=48&#34;
       alt=&#34;Logo Jupyter kernel uv venv&#34;
       style=&#34;width: 64px; height: 64px; border-radius: 12px; margin-right: 20px; flex-shrink: 0;&#34;&gt;
  &lt;div style=&#34;flex: 1;&#34;&gt;
    &lt;h3 style=&#34;margin: 0 0 6px 0; color: #1d72b8; font-size: 1.25rem;&#34;&gt;Jupyter kernel uv venv&lt;/h3&gt;
    &lt;p style=&#34;margin: 0; color: #555; font-size: 0.95rem; line-height: 1.4;&#34;&gt;
      Jupyter Lab / Hub kernel using &lt;strong&gt;uv&lt;/strong&gt; to manage one virtual environment per notebook,
      and stop duplicating package installation.
    &lt;/p&gt;
  &lt;/div&gt;
  &lt;a href=&#34;https://gitlab.com/metal3d/jupyter-uv-venv&#34;
     style=&#34;
       margin-left: 20px;
       padding: 10px 18px;
       background-color: #1d72b8;
       color: #fff;
       font-weight: bold;
       text-decoration: none;
       border-radius: 8px;
       transition: background-color 0.2s;
       white-space: nowrap;
     &#34;
     onmouseover=&#34;this.style.backgroundColor=&#39;#155a8a&#39;&#34;
     onmouseout=&#34;this.style.backgroundColor=&#39;#1d72b8&#39;&#34;
     target=&#34;_blank&#34;&gt;
    Voir le projet
  &lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Mais ça, c&amp;rsquo;est que dalle. Ce que j&amp;rsquo;ai vraiment kiffé, c&amp;rsquo;est la gestion de la publication de paquets sur PyPI. Dans mon
&lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, j&amp;rsquo;ai ça :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;stages:
  - release

publish-package:
  image: python:3.12-bookworm
  stage: release
  id_tokens:
    PYPI_ID_TOKEN:
      aud: &amp;quot;pypi&amp;quot;
  before_script:
    - pip install pdm
  script:
    - pdm publish
  rules:
    - if: $CI_COMMIT_TAG =~ /^v.*/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Juste ça, rien que ça&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est quoi cette magie là ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Gitlab et PyPI supportent l&amp;rsquo;authentification OIDC (OpenID Connect), il faut aller configurer son compte &lt;a href=&#34;https://pypi.org/manage/account/publishing/&#34;&gt;ici&lt;/a&gt; et définir une entrée pour GitLab, avec le nom du projet, le chemin du fichier CI, etc. Ça prend 10 secondes.&lt;/p&gt;
&lt;p&gt;Le &lt;code&gt;id_tokens&lt;/code&gt; dans le &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; permet de récupérer un token d&amp;rsquo;authentification temporaire pour pousser le
paquet. Entendez bien, je n&amp;rsquo;ai absolment pas besoin de stocker un token dans les &amp;ldquo;variables&amp;rdquo; ou &amp;ldquo;secrets&amp;rdquo; du projet Gitlab. C&amp;rsquo;est &lt;strong&gt;automatique&lt;/strong&gt;. Et c&amp;rsquo;est expliqué &lt;a href=&#34;https://pdm-project.org/latest/usage/publish/&#34;&gt;dans la documentation de PDM&lt;/a&gt; - quand je vous dis que lire les docs, c&amp;rsquo;est important&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;bref-je-vous-le-dis&#34;&gt;Bref, je vous le dis&lt;/h2&gt;
&lt;p&gt;Tentez l&amp;rsquo;expérience, lisez les doc de &lt;code&gt;uv&lt;/code&gt; et &lt;code&gt;pdm&lt;/code&gt;, faites des tests.&lt;/p&gt;
&lt;p&gt;Je sais, j&amp;rsquo;ai utilisé un ton très enthousiaste, mais je vous assure que ces deux outils m&amp;rsquo;ont vraiment soulagé. Je ne
dis pas que poetry est mauvais, je ne rejette pas pip et le fait que beaucoup de projets utilisent encore
&lt;code&gt;requirements.txt&lt;/code&gt;. Ce que je dis, c&amp;rsquo;est que si vous passez votre temps à gérer des dépendances, des environnements
virtuels, et que vous codez beaucoup en Python, ce couple d&amp;rsquo;outils va vous faire gagner un temps précieux, et de la
place sur votre disque.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai réellement gagné 20% de mon disque, j&amp;rsquo;ai été le premier surpris. Bien sûr, passer mes projets sous &lt;code&gt;pdm&lt;/code&gt; m&amp;rsquo;a demandé
un peu de temps (supprimer les vieux &lt;code&gt;venv&lt;/code&gt;, refaire les fichiers de conf, etc.), mais franchement, ça vaut le coup.
Linux commençait vraiment à ne pas être content et malgré des phases de nettoyage podman, bleachbit, etc. j&amp;rsquo;étais à 80-90%
d&amp;rsquo;occupation disque.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Non, les Taskfile ne sont pas plus simples que Makefile</title>
      <link>https://www.metal3d.org/blog/2025/makefile-est-bien-plus-simple-que-taskfile/</link>
      <pubDate>Wed, 03 Sep 2025 08:57:49 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2025/makefile-est-bien-plus-simple-que-taskfile/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2025/makefile-est-bien-plus-simple-que-taskfile/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Voilà des mois que les &amp;ldquo;Taskfiles&amp;rdquo;, fichiers utilisés par &amp;ldquo;GoTask&amp;rdquo;, censé être le remplaçant de Makefile, font débat dans la
communauté des développeurs. Les Makefile sont jugés trop compliqués, illisibles, vieux, pas adaptés&amp;hellip; j&amp;rsquo;entends tout et n&amp;rsquo;importe
quoi. Alors que la promesse des Taskfile est de rendre tout magiquement simple, lisible, etc.&lt;/p&gt;
&lt;p&gt;Donc, avant de vous lancer dans GoTask parce que c&amp;rsquo;est la mode, et de répondre à l&amp;rsquo;appel des sirènes du &amp;ldquo;c&amp;rsquo;est moderne&amp;rdquo;, on va
simplement faire un TL;DR pour savoir faire un Makefile. Et je voue le dis, ce qui suit est &lt;strong&gt;tout ce que vous devez
savoir pour faire un Makefile&lt;/strong&gt;. Alors que GoTask, vous allez avoir besoin d&amp;rsquo;une doc (très belle, je ne dis pas le
contraire) assez conséquente.&lt;/p&gt;
&lt;p&gt;Mais, d&amp;rsquo;abord, avant de passer à la pratique, parlons un peu de ce qui m&amp;rsquo;énerve avec les fans de GoTask.&lt;/p&gt;
&lt;p&gt;Prêt ?&lt;/p&gt;
&lt;h2 id=&#34;avant-de-lire-la-suite-cest-important&#34;&gt;Avant de lire la suite, c&amp;rsquo;est IMPORTANT&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Détendez-vous, je vais être sarcastique et volontairement jouer le nerveux. C&amp;rsquo;est pour rire. Je suis un gars gentil et
je respecte vos préférences. Ce n&amp;rsquo;est qu&amp;rsquo;un billet de blog ! &amp;#x1f604; &amp;#x1f48b; &amp;#x2764;&amp;#xfe0f;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;J&amp;rsquo;aime GoTask, je l&amp;rsquo;utilise parfois. Je suis développeur Go, j&amp;rsquo;aime Go, j&amp;rsquo;aime le YAML. Mais je suis pragmatique. Je ne
sombre pas dans le fanatisme crasse et l&amp;rsquo;argumentation fallacieuse sous prétexte qu&amp;rsquo;un truc me vante ses mérites.&lt;/p&gt;
&lt;p&gt;Je sais que Makefile peut devenir un bordel sans nom, qu&amp;rsquo;il peut devenir illisible, compliqué, etc. Mais je sais aussi
qu&amp;rsquo;on peut faire les choses proprement. J&amp;rsquo;ai appris à faire un Makefile à peine quelques jours après avoir fait mes
premiers pas en C. Je vous le dis, j&amp;rsquo;avais 20 ans, je n&amp;rsquo;avais jamais touché un PC de ma vie, et pourtant j&amp;rsquo;ai trouvé
Makefile &lt;strong&gt;ultra-simple à comprendre et à utiliser&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;GoTask, il a plein d&amp;rsquo;avantages. C&amp;rsquo;est indéniable. Mais comme énormément de nouvelles choses qui sortent, je vois passer
des posts LinkedIn, Mastodon, ou je ne sais quoi, avec un objectif clair : faire passer GoTask / Taskfile comme &amp;ldquo;le héros
de tous les temps&amp;rdquo;, et chier sur Makefile avec des arguments fallacieux.&lt;/p&gt;
&lt;p&gt;Je suis toujours prêt à discuter, échanger, débattre. Les désaccords sont normaux. Mais là, je lis des trucs qui me font
bondir.&lt;/p&gt;
&lt;p&gt;Je vous échauffe un peu, mais au final, je vais vous demander juste une chose :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;build:
	commande qui builde un truc
	par exemple:
	go build -o ./dist/app .

clean:
	rm -rf ./dist
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Est-ce que ça, ça vous dépasse ?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Si oui, partez de cet article. Ça ne sert à rien, vous allez refuser d&amp;rsquo;écouter mes arguments.&lt;/li&gt;
&lt;li&gt;Sinon, alors vous allez pouvoir accepter un échange d&amp;rsquo;idées.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Maintenant, clairement, je vais &lt;strong&gt;volontairement casser un peu de sucre sur le dos de GoTask&lt;/strong&gt;. C&amp;rsquo;est normal, c&amp;rsquo;est une
exagération pour faire réagir, pour que vous compreniez ce que moi, utilisateur averti des deux outils, je ressens.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si vous acceptez ce ton, le sarcasme, et que vous savez sourire à tout ça, vous êtes les bienvenus pour lire la suite.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;les-arguments-qui-ne-passent-pas&#34;&gt;Les arguments qui ne passent pas&lt;/h2&gt;
&lt;p&gt;J&amp;rsquo;entends et je lis énormément d&amp;rsquo;arguments de &amp;ldquo;pro Taskfile / GoTask&amp;rdquo; et beaucoup de trucs contre Makefile. Et quand je
les lis, je reste pantois, bouche bée, ébahi, estomaqué&amp;hellip; choisissez dans la liste.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est parti&amp;hellip;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;GoTask est plus moderne que Makefile&amp;rdquo;&lt;/strong&gt;.&lt;br&gt;
&lt;strong&gt;Non&amp;hellip;&lt;/strong&gt; utiliser YAML ne rend pas un truc &amp;ldquo;moderne&amp;rdquo;. Par contre, ça le rend
sujet à tout un merdier que YAML apporte. Je vous donne une énigme à résoudre juste après, juste pour vous rendre
compte que YAML, ce n&amp;rsquo;était pas l&amp;rsquo;idée du siècle pour &amp;ldquo;coder des tâches&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;La syntaxe de Makefile est compliquée&amp;rdquo;&lt;/strong&gt;.&lt;br&gt;
&lt;strong&gt;Non&amp;hellip;&lt;/strong&gt; la syntaxe de Makefile est simple. Simple parce qu&amp;rsquo;il n&amp;rsquo;y a &lt;strong&gt;aucun&lt;/strong&gt; mot clef à connaitre pour démarrer.
Vous donnez un nom de tâche, et les commande à effectuer pour cette tâche. Voilà, point.&lt;br&gt;
Par contre, avec Taskfile, vous allez devoir apprendre &lt;strong&gt;deux syntaxes&lt;/strong&gt; :
YAML (et les noms des directives autorisées&amp;hellip;) et GoTmpl. Je ne vois vraiment pas comment on peut estimer qu&amp;rsquo;utiliser
GoTmpl est &amp;ldquo;sympa&amp;rdquo;. Pourtant, je développe en Go hein, et je fais des Helm Chart. Mais je vous le dis,
c&amp;rsquo;est loin d&amp;rsquo;être idéal. Taskfile, il vous impose de connaitre sa sémantique, ses noms d&amp;rsquo;attributs&amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;C&amp;rsquo;est clair, le fichier se décrit lui-même&amp;rdquo;&lt;/strong&gt;.&lt;br&gt;
&lt;strong&gt;Non&amp;hellip;&lt;/strong&gt; Pourquoi &amp;ldquo;run: once&amp;rdquo; ? pourquoi la règle c&amp;rsquo;est pas ce que ça génère ? pourquoi on doit dire &amp;ldquo;generates:&amp;rdquo;?
Ça demande à connaitre les directives de Taskfile. La
moindre erreur, le moindre truc qu&amp;rsquo;on n&amp;rsquo;a pas vu dans la doc, le Taskfile ne fait pas ce qu&amp;rsquo;on veut. Makefile &lt;strong&gt;n&amp;rsquo;a pas
de langage de directive&lt;/strong&gt;. Il applique les tâches / cibles / ce que vous voulez, et c&amp;rsquo;est tout. La cible, c&amp;rsquo;est ce
que vous voulez faire. Si c&amp;rsquo;est un fichier, la cible est un fichier, si c&amp;rsquo;est une tâche, alors ça effectue une tâche. Makefile = pas
la peine de lui expliquer l&amp;rsquo;évidence.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Je vous parlais d&amp;rsquo;une énigme à résoudre. Amusons-nous. &amp;#x1f609;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Voici un Taskfile :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;version: &amp;quot;3&amp;quot;
silent: true

vars:
  REFUSED: no

tasks:
  ask:
    cmds:
      - |
        read -p &amp;quot;Would you really delete your entire hardirve ? (yes/no): &amp;quot; answer
        if [ &amp;quot;$answer&amp;quot; == ${REFUSED} ]; then
          exit 0
        else
          echo &amp;quot;ok, let&#39;s delete all your data&amp;quot;
        fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous lancez &lt;code&gt;task ask&lt;/code&gt; et vous répondez &amp;ldquo;no&amp;rdquo;. Bonne chance&amp;hellip;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ task ask
Would you really delete your entire hardirve ? (yes/no) no
ok, let&#39;s delete all your data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What ? Mais j&amp;rsquo;ai répondu &amp;ldquo;no&amp;rdquo; ! Pourquoi il a pas compris ?&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;&lt;/tictle&gt;Vous voulez la solution de cette énigme ?&lt;/summary&gt;
  &lt;p&gt;
  Eh oui, en YAML &lt;code&gt;no&lt;/code&gt; est un booléen. C&#39;est l&#39;équivalent de &lt;code&gt;false&lt;/code&gt;.
  Donc, dans le script, on compare &lt;code&gt;&#34;$answer&#34;&lt;/code&gt; à &lt;code&gt;false&lt;/code&gt;, ce qui n&#39;est jamais vrai.
  &lt;/p&gt;
  &lt;p&gt;
  Il faut écrire &lt;code&gt;REFUSED: &#34;no&#34;&lt;/code&gt; pour que ça marche, avec des doubles quotes.
  Et ça, c&#39;est une des conneries que le YAML apporte... Parce que oui, YAML c&#39;est bien, mais y&#39;a un paquet de trucs qui
    foutent le zbeul.
  &lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;Effectivement mon exemple est volontairement dangereux, mais ça peut être un vrai souci à déboguer.&lt;/p&gt;
&lt;p&gt;Ce que je veux dire, dans cet exemple, c&amp;rsquo;est qu&amp;rsquo;utiliser YAML qui, de base, n&amp;rsquo;est pas fait pour ce genre de chose, c&amp;rsquo;est
introduire des risques.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On a déjà bien mangé nos nerfs sur Ansible à cause des conneries de ce genre, GoTask ne va pas
magiquement passer à travers.&lt;/strong&gt; YAML, c&amp;rsquo;est un point de friction, que vous le vouliez ou non, et cette petite énigme en
est une preuve, et j&amp;rsquo;en ai plein sous la main !&lt;/p&gt;
&lt;h2 id=&#34;alors-pourquoi-les-gens-pensent-que-makefile-est-compliqué-&#34;&gt;Alors pourquoi les gens pensent que Makefile est compliqué ?&lt;/h2&gt;
&lt;p&gt;Parce que le souci, c&amp;rsquo;est que quand vous le maitrisez, je veux dire quand vous allez loin, très (trop) loin, alors vous
risquez de rendre le contenu imbuvable. Je le sais, c&amp;rsquo;est un de mes défauts&amp;hellip; Parfois, je pousse trop.&lt;/p&gt;
&lt;p&gt;Sauf que là, on parle d&amp;rsquo;une utilisation très spécifique et surtout technique des Makefile.&lt;/p&gt;
&lt;p&gt;Sur un projet web, pour un truc qui lance des conteneurs, build du NodeJS, installe des dépendances PHP&amp;hellip; Vous n&amp;rsquo;aurez
absolument pas à utiliser des options poussives.&lt;/p&gt;
&lt;p&gt;Prenons un exemple simple, un truc que je fais presque à chaque fois :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;
# docker ou podman
CONTAINER=docker

up:
  $(CONTAINER) compose up -d

down:
  $(CONTAINER) compose down

# alias vers bin/myapp
build: bin/myapp

bin/myapp: *.go
  go build -o app/bin .

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je vais tout détailler après, mais là, &lt;strong&gt;ne me faites par croire que ce Makefile est compliqué&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vraiment, quand vous me dites que ça, vos collègues ne savent pas comment l&amp;rsquo;utiliser, &lt;strong&gt;je ne vous crois pas&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Non mais vraiment, qu&amp;rsquo;est-ce qui vous rebute là ???&lt;/p&gt;
&lt;p&gt;&lt;code&gt;make up&lt;/code&gt; démarre les conteneurs, &lt;code&gt;make down&lt;/code&gt; les arrête, &lt;code&gt;make build&lt;/code&gt; appelle &lt;code&gt;make app/myapp&lt;/code&gt; qui construit le binaire si un fichier &lt;code&gt;.go&lt;/code&gt; a
changé. Y&amp;rsquo;a rien à étudier, pas une doc à ouvrir, c&amp;rsquo;est évident.&lt;/p&gt;
&lt;p&gt;En Taskfile, ça donne ça :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;version: &amp;quot;3&amp;quot;

vars:
  container: &amp;quot;docker&amp;quot;

tasks:
  up:
    cmds:
      - &amp;quot;{{.container}} compose up -d&amp;quot;

  down:
    cmds:
      - &amp;quot;{{.container}} compose down&amp;quot;

  build:
    deps:
      - &amp;quot;bin/myapp&amp;quot;

  bin/myapp:
    cmds:
      - &amp;quot;go build -o bin/myapp .&amp;quot;
    sources:
      - &amp;quot;*.go&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Non mais soyez honnête&amp;hellip; En quoi c&amp;rsquo;est plus lisible ?
En quoi c&amp;rsquo;est plus simple ? En quoi c&amp;rsquo;est plus court ? En quoi
c&amp;rsquo;est plus moderne ? C&amp;rsquo;est le bordel !&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est une série de directives dont il faut se souvenir (&lt;code&gt;deps&lt;/code&gt;, &lt;code&gt;cmds&lt;/code&gt;, et d&amp;rsquo;autre dont j&amp;rsquo;oublis toujours le nom&amp;hellip;,
&lt;code&gt;sources&lt;/code&gt;, etc.) et une arborescence longue comme un roman de Ken Follett.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Et bon sang&amp;hellip; faire des listes de 1 seul élément, c&amp;rsquo;est cool ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On double le nombre de lignes, ça va gueuler si je me trompe dans la syntaxe GoTmpl..&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je suis désolé, mais l&amp;rsquo;argument de la modernité et de la lisibilité ne passent pas. Mais alors pas &lt;strong&gt;du tout&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Je veux bien entendre plein d&amp;rsquo;arguments qui sont pour GoTask, mais pas ceux-là.&lt;/p&gt;
&lt;h2 id=&#34;créer-un-makefile-tout-ce-que-vous-devez-savoir&#34;&gt;Créer un Makefile, tout ce que vous devez savoir&lt;/h2&gt;
&lt;p&gt;Donc, on va se mettre d&amp;rsquo;accord. Vous lisez cette partie, ça va durer 10 minutes en gros. Ensuite, si vous continuez de me
dire que Makefile, c&amp;rsquo;est compliqué, je pense qu&amp;rsquo;on n&amp;rsquo;est pas de la même planète et je vous demanderai de garer votre
soucoupe sur un espace réservé.&lt;/p&gt;
&lt;p&gt;Un Makefile, c&amp;rsquo;est un fichier nommé &amp;ldquo;&lt;code&gt;Makefile&lt;/code&gt;&amp;rdquo; (ou &amp;ldquo;&lt;code&gt;GNUmakefile&lt;/code&gt;&amp;rdquo;) qui contient des &lt;strong&gt;cibles&lt;/strong&gt;, des
dépendances en options et des commandes. Voilà&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Avant de vous prendre la tête, gardez à l&amp;rsquo;esprit ce crédo valable pour tout le document qui suit :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Un Makefile est
une description de trucs à faire. C&amp;rsquo;est pas une structure, pas un objet et encore moins un programme à coder, c&amp;rsquo;est
une des-crip-tion&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Une cible, c&amp;rsquo;est arbitraire. C&amp;rsquo;est soit un simple nom de tâche (&lt;code&gt;build&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;clean&lt;/code&gt;, &lt;code&gt;deploy&lt;/code&gt;, etc.), soit un nom de fichier à
générer. Si la cible est un nom de fichier, &lt;code&gt;make&lt;/code&gt; vérifiera s&amp;rsquo;il est à jour en fonction des dépendances.&lt;/p&gt;
&lt;p&gt;Et les dépendances, ce sont aussi des cibles. Donc soit une tâche à effectuer, soit un fichier existant ou à générer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Donc, arrêtez de vous prendre la tête, une cible, c&amp;rsquo;est tout ce que vous voulez. Y&amp;rsquo;a pas de règle à connaitre. C&amp;rsquo;est
la même chose dans GoTask, vous n&amp;rsquo;avez pas à vous faire saigner du nez pour comprendre ça.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et, &lt;strong&gt;la première cible est la cible par défaut&lt;/strong&gt;. Si vous faites &lt;code&gt;make&lt;/code&gt; sans argument, c&amp;rsquo;est la première cible
qui sera exécutée.&lt;/p&gt;
&lt;p&gt;La syntaxe est simplement :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;cible1: dépendance1 dépendance2
	commande1
	commande2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Les dépendances sont optionnelles, bien entendu. Si une dépendance est listée, elle sera exécutée avant la cible.&lt;/p&gt;
&lt;p&gt;🏁 &lt;strong&gt;STOP !!!&lt;/strong&gt; Arrêtez-vous ici.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Là, vous avez déjà &lt;strong&gt;tout ce qu&amp;rsquo;il faut pour faire un Makefile&lt;/strong&gt;. Vous pouvez arrêter de lire
et utiliser ce que vous avez appris. Ça suffit pour faire le travail demandé. Alors oui, la suite vous apprendra d&amp;rsquo;autres
trucs, et je vous conseille d&amp;rsquo;aller au bout. Mais, ce bout de code là, &lt;strong&gt;c&amp;rsquo;est suffisant pour faire ce que vous voulez !&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Donc, me dire que c&amp;rsquo;est trop compliqué, c&amp;rsquo;est de la mauvaise foi.&lt;/p&gt;
&lt;p&gt;Important:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dans les exemples qui suivent, je mets &amp;ldquo;&lt;code&gt;@&lt;/code&gt;&amp;rdquo; pour ne pas que &lt;code&gt;make&lt;/code&gt; n&amp;rsquo;affiche les commandes. Vous pouvez aussi utiliser
&lt;code&gt;.SILENT:&lt;/code&gt; au début du Makefile pour rendre toutes les commandes silencieuses. Ou, l&amp;rsquo;option &lt;code&gt;make -s&lt;/code&gt; à l&amp;rsquo;appel de &lt;code&gt;make&lt;/code&gt;.
Je vous rappelle, une fois de plus, que c&amp;rsquo;est &lt;strong&gt;pareil sur GoTask&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ensuite, les variables. Vous pouvez en créer autant que vous voulez :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;# des variables
FOO = bar
BAR = baz

# testons:
test:
	echo $(FOO)
	echo $(BAZ)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Les variables sont référencées avec &lt;code&gt;$(NOM_DE_LA_VARIABLE)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Un truc cool, vous pouvez utiliser d&amp;rsquo;autres symboles d&amp;rsquo;affectation :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;=&lt;/code&gt; veut dire &amp;ldquo;la valeur est évaluée une fois, au &lt;strong&gt;moment de son expansion, quand elle apparait dans une cible&lt;/strong&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:=&lt;/code&gt; &amp;ldquo;la valeur est évaluée une fois, au &lt;strong&gt;moment de la déclaration, quand on lance &lt;code&gt;make&lt;/code&gt;&lt;/strong&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+=&lt;/code&gt; concaténation&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?=&lt;/code&gt; la valeur est affectée seulement si la variable n&amp;rsquo;existe pas déjà (utile pour les valeurs à passer en ligne de
commande)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On va juste parler de la subtilité entre &lt;code&gt;=&lt;/code&gt; et &lt;code&gt;:=&lt;/code&gt;, parce que je sais que c&amp;rsquo;est un point de friction pour beaucoup.&lt;/p&gt;
&lt;p&gt;Si le contenu de la variable est &amp;ldquo;simple&amp;rdquo;, pas long à assigner, donc comme dans 99% des cas, utilisez &amp;ldquo;&lt;code&gt;=&lt;/code&gt;&amp;rdquo;. Si par
contre, vous préférez que la variable soit évaluée une fois pour toutes, genre son contenu est long à générer,
utilisez &amp;ldquo;&lt;code&gt;:=&lt;/code&gt;&amp;rdquo;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais franchement, &lt;strong&gt;on s&amp;rsquo;en tape&lt;/strong&gt; dans notre cas d&amp;rsquo;utilisation, utilisez &amp;ldquo;&lt;code&gt;=&lt;/code&gt;&amp;rdquo; partout, ça ira très bien.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et enfin, pour &lt;code&gt;?=&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;FOO ?= la valeur par défaut

test:
	echo $(FOO)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est simple :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ make -s
la valeur par défaut

$ make -s FOO=couou
coucou
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Holala c&amp;rsquo;était dur&amp;hellip; 🥱&lt;/p&gt;
&lt;h2 id=&#34;plus-loin&#34;&gt;Plus loin&lt;/h2&gt;
&lt;p&gt;Comme je vous le disais, les &amp;ldquo;cibles&amp;rdquo; sont tout et n&amp;rsquo;importe quoi. Si un fichier porte le nom de la cible, alors &lt;code&gt;make&lt;/code&gt;
va évaluer son existence et sa date de modification par rapport aux dépendances. Si le fichier n&amp;rsquo;existe pas ou s&amp;rsquo;il est plus
vieux, alors la cible sera exécutée. Si le fichier existe et est plus récent que toutes les dépendances, alors la cible
ne sera pas exécutée (sauf si on appelle &lt;code&gt;make&lt;/code&gt; avec l&amp;rsquo;option &lt;code&gt;-B&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Surprise, c&amp;rsquo;est pareil dans GoTask&amp;hellip; 😒&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;example.txt: dépendance1 dépendance2
	echo &amp;quot;Génération de example.txt&amp;quot;
	touch example.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ make -s
Génération de example.txt

$ make -s
make: « example.txt » est à jour.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sérieux, qu&amp;rsquo;est-ce qui vous rebute là ?&lt;/p&gt;
&lt;p&gt;Avec les dépendances :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;example.txt: *.foo
	echo &amp;quot;Génération de example.txt&amp;quot;
	touch example.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quel que soit le fichier qui se termine par &lt;code&gt;.foo&lt;/code&gt; dans le répertoire, si l&amp;rsquo;un d&amp;rsquo;eux est plus récent que
&lt;code&gt;example.txt&lt;/code&gt;, alors la cible sera exécutée.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ touch a.foo
$ make -s
Génération de example.txt

$ make -s
make: « example.txt » est à jour.

$ touch a.foo
$ make -s
Génération de example.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et, si on utilisait &lt;code&gt;make&lt;/code&gt; pour générer &amp;ldquo;&lt;code&gt;b.foo&lt;/code&gt;&amp;rdquo; ?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;example.txt: *.foo b.foo
	echo &amp;quot;Génération de example.txt&amp;quot;
	touch example.txt

b.foo:
	touch b.foo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En appelant &lt;code&gt;make&lt;/code&gt; ou &lt;code&gt;make example.txt&lt;/code&gt;, si &lt;code&gt;b.foo&lt;/code&gt; n&amp;rsquo;existe pas, il sera généré avant, puis &lt;code&gt;example.txt&lt;/code&gt; sera généré.&lt;/p&gt;
&lt;p&gt;Et, c&amp;rsquo;est &lt;del&gt;différent&lt;/del&gt; ha non zut, c&amp;rsquo;est pareil dans GoTask&amp;hellip; sauf que c&amp;rsquo;est implicite avec Makefile. J&amp;rsquo;ai pas eu à lui
expliquer un truc bateau, normal, logique. En gros, Makefile est plus intelligent.&lt;/p&gt;
&lt;h2 id=&#34;les-variables-spéciales-à-connaitre-pour-aller-plus-loin&#34;&gt;Les variables spéciales à connaitre pour aller plus loin&lt;/h2&gt;
&lt;p&gt;Voilà, là oui, je l&amp;rsquo;admets, c&amp;rsquo;est la partie qui est le vrai sujet de discorde. Parce que oui, il y a des variables
&amp;ldquo;magiques&amp;rdquo; dans Makefile et ça peut vraiment poser un problème à ceux qui ne les connaissent pas.&lt;/p&gt;
&lt;p&gt;Si c&amp;rsquo;est ce point qui bloque, je peux le comprendre. Il n&amp;rsquo;y en a pas beaucoup, mais elles ne sont pas hyper intuitives.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais, dans ce cas, ne les utilisez pas. Voilà&amp;hellip; ça règle le problème !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Il y a des variables spéciales utilisables n&amp;rsquo;import où :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$@&lt;/code&gt;, c&amp;rsquo;est &amp;ldquo;le nom de la cible&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$&amp;lt;&lt;/code&gt;, le premier élément dans la liste des dépendances.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$^&lt;/code&gt;, tous les éléments dans la liste des dépendances.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$?&lt;/code&gt;, tous les éléments dans la liste des dépendances qui sont plus récentes que la cible.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$*&lt;/code&gt;, le nom de la cible sans son suffixe (utile pour les règles génériques).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et aussi :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;%&lt;/code&gt; dans une cible ou une dépendance, c&amp;rsquo;est un joker qui représente n&amp;rsquo;importe quelle chaîne de caractères. Sa valeur
est reportée partout, vous allez voir c&amp;rsquo;est génial.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;En gros une cible &lt;code&gt;%.html: %.md&lt;/code&gt; est générique, je peux demande &lt;code&gt;foo.html&lt;/code&gt;, automatiquement &lt;code&gt;make&lt;/code&gt; va vérifier &lt;code&gt;foo.md&lt;/code&gt;
(et le générer si une règle existe pour ce fichier). Dans l&amp;rsquo;exemple ci-dessous, je m&amp;rsquo;en sers pour générer des fichiers
HTML avec &lt;code&gt;pandoc&lt;/code&gt; :&lt;/p&gt;
&lt;p&gt;Je vous donne un exemple :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;# quelque soit le fichier HTML qu&#39;on veut générer, on prend son
# équivalent Markdown dans le répertoire src
doc/%.html: src/%.md
	pandoc -o $@ $^

# pour générer plein de docs
docs: doc/index.html doc/about.html doc/contact.html

# ou, on utilise des fonctions spéciales
docs: $(wildcard doc/*.html)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez appeler &lt;code&gt;make docs&lt;/code&gt; pour générer toute la doc, ou générer un seul fichier avec &lt;code&gt;make doc/index.html&lt;/code&gt;, ou &lt;code&gt;make doc/about.html&lt;/code&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Comprenez que :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pandoc -o $@ $^
# équivaut à prendre la cible dans $@ et les dépendances dans $^, donc
# make doc/index.html produit:
pandoc -o doc/index.html src/index.md
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Astuce mnémotechnique :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$&amp;lt;&lt;/code&gt;, c&amp;rsquo;est une flèche qui pointe vers la première dépendance,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$^&lt;/code&gt;, c&amp;rsquo;est une flèche vers le haut qui pointe sur les dépendances&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;&lt;/blockquote&gt;
&lt;p&gt;Donc, maintenant :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;make doc/index.html&lt;/code&gt; va générer &lt;code&gt;doc/index.html&lt;/code&gt; à partir de &lt;code&gt;doc/index.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;make docs&lt;/code&gt; va générer &lt;code&gt;doc/index.html&lt;/code&gt;, &lt;code&gt;doc/about.html&lt;/code&gt; et &lt;code&gt;doc/contact.html&lt;/code&gt; à partir de
&lt;code&gt;doc/index.md&lt;/code&gt;, &lt;code&gt;doc/about.md&lt;/code&gt; et &lt;code&gt;doc/contact.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est vrai, ça je vous l&amp;rsquo;accorde, ces variables, ça peut rebuter&amp;hellip; Mais au point de partir sur GoTask qui va vous demander plus de
boulot, et ne pas vous proposer ce genre d&amp;rsquo;options ? Et donc, de vous taper du GoTmpl qui rend fou ? J&amp;rsquo;ai du mal à le
comprendre.&lt;/p&gt;
&lt;h2 id=&#34;les-cerises-sur-le-gâteau&#34;&gt;Les cerises sur le gâteau&lt;/h2&gt;
&lt;p&gt;Makefile a plein de petites options qui rendent l&amp;rsquo;outil d&amp;rsquo;une puissance rare.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.PHONY&lt;/code&gt; est une cible spéciale qui indique que les cibles listées ne sont pas des fichiers. Comme ça on évite les
&amp;ldquo;machin est à jour&amp;rdquo; alors qu&amp;rsquo;on ne veut pas&amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.ONE_SHELL:&lt;/code&gt; à partir de GNU Make 4.0, cette directive indique que toutes les commandes d&amp;rsquo;une même cible doivent être
exécutées dans le même shell. Par défaut, chaque ligne est exécutée dans un shell séparé, ce qui peut poser des soucis
sur les changements de répertoire, les variables d&amp;rsquo;environnement, etc.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$(MAKE)&lt;/code&gt; permet d&amp;rsquo;appeler &lt;code&gt;make&lt;/code&gt;, ça évite des problèmes sur certains OS donc la commande n&amp;rsquo;est pas focément &lt;code&gt;make&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-include fichier&lt;/code&gt; permet d&amp;rsquo;inclure un autre Makefile. Le &lt;code&gt;-&lt;/code&gt; indique que l&amp;rsquo;inclusion est optionnelle (si le fichier
n&amp;rsquo;existe pas, pas d&amp;rsquo;erreur)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$(shell commande)&lt;/code&gt; exécute une commande shell et retourne son résultat. Utile pour affecter des variables.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@&lt;/code&gt; au début d&amp;rsquo;une commande empêche l&amp;rsquo;affichage de la commande avant son exécution. Utile pour éviter le bruit dans la
sortie. On peut utiliser &lt;code&gt;.SILENT:&lt;/code&gt; pour rendre toutes les commandes silencieuses.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-&lt;/code&gt; au début d&amp;rsquo;une commande indique que les erreurs de cette commande doivent être ignorées.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$(wildcard motif)&lt;/code&gt; retourne une liste de fichiers correspondant au motif. Utile pour les dépendances.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$(patsubst motif, remplacement, texte)&lt;/code&gt; remplace les occurrences du motif dans le texte par le remplacement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et en dernier lieu, on peut faire des tests conditionnels :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;LOCAL_INSTALL=1

install:
ifeq ($(LOCAL_INSTALL),1)
	echo &amp;quot;Installation locale&amp;quot;
	cp machin ~/.local/bin/machin
else
	echo &amp;quot;Installation globale&amp;quot;
	sudo cp machin /usr/local/bin/machin
endif
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;comparé-à-gotask--taskfile&#34;&gt;Comparé à GoTask / Taskfile&lt;/h2&gt;
&lt;p&gt;Make sait faire du parallélisme avec l&amp;rsquo;option &lt;code&gt;-j&lt;/code&gt; (nombre de jobs en parallèle). On me sort cet argument pour GoTask,
j&amp;rsquo;ai l&amp;rsquo;impression que les détracteurs de &lt;code&gt;make&lt;/code&gt; n&amp;rsquo;on pas ouvert la doc &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;La gestion de dépendances est automatique avec &lt;code&gt;make&lt;/code&gt;, il n&amp;rsquo;y a pas besoin de les lister partout.&lt;/p&gt;
&lt;p&gt;Et pareillement, la gestion de construction de fichier est implicite, pas besoin de définir une tâche de 8 lignes pour
dire &amp;ldquo;alors si ça, c&amp;rsquo;est pas à jour, il faut que tu regardes ces fichiers&amp;rdquo;. Dans un Makefile, &lt;strong&gt;c&amp;rsquo;est automatique&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On me sort souvent que le YAML c&amp;rsquo;est pratique et moderne. Pratique oui, moderne&amp;hellip; ça se discute. YAML c&amp;rsquo;est avant tout
un format de sérialisation de données. Pas un langage de script. Donc, on se retrouve avec un format qui n&amp;rsquo;est pas
directement prévu pour cela. Et de ce fait, on va faire des routines tordues pour effectuer des tâches sous conditions,
alors que le langage de Makefile est prévu pour cela.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;entends que vous aimez YAML, et je ne dis pas que GoTask est mauvais. Je dis simplement que si vous prenez quelques
minutes pour lire cet article, ou une doc condensée sur Makefile, vous verrez que &lt;strong&gt;Makefile est simple à utiliser&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Oui, il y a des variables spéciales, oui, il y a des subtilités, mais franchement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;y&amp;rsquo;en a peu, vous les retenez et vous commentez votre Makefile, ça ira tout seul&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vous n&amp;rsquo;êtes pas obligé de les utiliser&lt;/strong&gt;, vous pouvez faire votre tambouille avec des règles simples, sans gérer les
variables spéciales, sans faire de règles génériques, etc. Vous pouvez faire un Makefile textuel bateau.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je refuse de croire que des ingénieurs, des gens qui codent tous les jours des machins tordus avec des langages
compliqués, n&amp;rsquo;arrivent pas à comprendre un Makefile avec des règles simples.
J&amp;rsquo;ai peut-être trop confiance en les capacités des techniciens, peut-être que je suis trop optimiste&amp;hellip; Mais bon sang, j&amp;rsquo;ai
appris à faire un Makefile en 10 minutes, à 20 ans, sans jamais avoir touché un PC de ma vie&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Qu&amp;rsquo;est-ce qui a fait que moi, sans compétences, sans aucune expérience, à 20 balais, j&amp;rsquo;ai absorbé la simplicité de Makefile, et pas vous ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Les gens qui me disent que Makefile est compliqué sont les mêmes qui me disent &amp;ldquo;mais si, la programmation des &amp;ldquo;generics&amp;rdquo; en
C# / Java / C++ c&amp;rsquo;est super simple&amp;rdquo;. Ou encore &amp;ldquo;comment ça l&amp;rsquo;injection de dépendance de Symfony est compliquée ? C&amp;rsquo;est
juste du XML&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Ce sont aussi les mêmes qui me disent que la syntaxe de template Go utilisée dans Taskfile est simple. Je suis
développeur Go, j&amp;rsquo;aime beaucoup Go, je l&amp;rsquo;adore, mais je ne trouve pas la syntaxe de template évidente. Loin de là&amp;hellip;&lt;/p&gt;
&lt;p&gt;Donc, là, cette page que j&amp;rsquo;ai produit, &lt;strong&gt;elle recense 90% de ce que vous devez savoir pour faire un Makefile&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;À moins que vous ayez des étapes de compilation de bourrin (genre des projets en C/C++ avec des tonnes de fichiers), le
Makefile que vous ferez sera simple, lisible, efficace et rapide à écrire. &lt;strong&gt;Beaucoup plus lisible et plus simple que
du Taskfile&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Tiens, vous voulez voir le Makefile que j&amp;rsquo;utilise pour ce site web ? Le voilà :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;.ONE_SHELL:

# contains KUBECONFIG, S3_ROOT, S3_BUCKET
-include .env


# start the website locally
dev:
	hugo server -s src

# build the website
build:
	mkdir -p public
	hugo -s src -d ../public

# deploy the new version
deploy: build push-on-s3 restart-website clean

# push code to scaleway S3 bucket, using &amp;quot;rclone&amp;quot;
push-on-s3:
	rclone sync -Pv \
		--fast-list --checksum \
		--no-update-modtime \
		./public/ ${S3_ROOT}:${S3_BUCKET}/

restart-website:
	KUBECONFIG=$(KUBECONFIG) kubectl -n metal3d \
		rollout restart deployment blog-metal3d-blog

# remove the generated site
clean:
	rm -rf public

# create a new blog post
# in blog/&amp;lt;yeear&amp;gt;/&amp;lt;name&amp;gt;/index.md
# NAME is the title, replace &#39; &#39; by &#39;-&#39;
NAME?=default-title
NAME:=$(shell printf &amp;quot;%s&amp;quot; &amp;quot;$(NAME)&amp;quot;|tr &#39; &#39; &#39;-&#39;)
YEAR:=$(shell date +&amp;quot;%Y&amp;quot;)

blog-post:
	hugo -s src new content &amp;quot;blog/$(YEAR)/$(NAME)/index.md&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors, je peux comprendre l&amp;rsquo;intérêt de GoTask, et j&amp;rsquo;accepte volontiers que vous le préfériez. Mais, &lt;strong&gt;je refuse
l&amp;rsquo;argument de la complexité de Makefile&lt;/strong&gt;. &lt;code&gt;make&lt;/code&gt; est simple du moment où vous décidez de l&amp;rsquo;utiliser simplement,
c&amp;rsquo;est justement son atout majeur !&lt;/p&gt;
&lt;p&gt;Makefile a plusieurs niveaux de complexité, mais c&amp;rsquo;est vous qui décidez quand vous arrêter de faire de l&amp;rsquo;optim.&lt;/p&gt;
&lt;p&gt;Dans vos projets, tout ce que vous devez faire, c&amp;rsquo;est :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;tache: dependance1 dependance2
	commande1
	commande2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est &lt;strong&gt;tout ce qu&amp;rsquo;il y a à retenir&lt;/strong&gt;. Le reste, c&amp;rsquo;est du bonus.&lt;/p&gt;
&lt;p&gt;Et je conclus : les gars (et les dames), bien entendu que je trouve GoTask sympa, pratique dans certains cas, et que je
l&amp;rsquo;utilise. Ne me prenez pas pour un vieux con archaïque qui ne jure que par la vieille méthode. Mais je refuse de lâcher
des trucs qui marche bien, qui sont pratiques et clairement adaptés, sous prétexte que la mode est d&amp;rsquo;infantiliser les
développeurs.&lt;/p&gt;
&lt;p&gt;Apprendre, maitriser et utiliser plein d&amp;rsquo;outils, de langages, c&amp;rsquo;est quand même plus passionnant.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Livebox limitée, Aidons-la avec Dnsmasq</title>
      <link>https://www.metal3d.org/blog/2025/livebox-limit%C3%A9e-aidons-la-avec-dnsmasq/</link>
      <pubDate>Mon, 11 Aug 2025 13:14:34 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2025/livebox-limit%C3%A9e-aidons-la-avec-dnsmasq/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2025/livebox-limit%C3%A9e-aidons-la-avec-dnsmasq/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Depuis des années, les utilisateurs avancés&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; râlent, mais Orange s&amp;rsquo;en soucie autant que de la durée de vie d&amp;rsquo;un poil pubien. Après tout, si ça ne concerne pas 90% des utilisateurs, pourquoi s&amp;rsquo;en soucier ? Pourtant, c&amp;rsquo;est un vrai casse-tête pour ceux qui veulent avancer dans le paramétrage de leur réseau local.&lt;/p&gt;
&lt;p&gt;En gros, &lt;strong&gt;impossible de manipuler les DNS des Livebox&lt;/strong&gt;. Pas de noms de machines supplémentaires, pas de sous-domaines, et encore moins de DNS autres que ceux d&amp;rsquo;Orange. Pas de filtrage, pas de sécurisation pour la famille. Heureusement, on peut contourner le problème en déléguant ce travail à une autre machine, comme un Raspberry Pi ou un vieux PC.&lt;/p&gt;
&lt;p&gt;Dans cet article, on va utiliser &lt;strong&gt;dnsmasq&lt;/strong&gt; sur une Fedora (mais n&amp;rsquo;importe quelle distribution fera l&amp;rsquo;affaire). Il sera notre serveur DNS et DHCP. Voici ce qu&amp;rsquo;on va faire :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configurer une machine (un Raspberry Pi) en IP statique&lt;/li&gt;
&lt;li&gt;En faire un serveur DNS et DHCP&lt;/li&gt;
&lt;li&gt;Couper le DHCP de la Livebox et ne plus utiliser son service DNS (elle ne servira plus que de routeur)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Trois étapes, mais il faudra un peu jongler pour ne pas se retrouver dans le noir.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pas de panique, suivez les explications et tout ira bien.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Un Raspberry Pi 3 ou 4 suffira amplement pour faire tourner dnsmasq.&lt;/p&gt;
&lt;p&gt;Et oui, je connais Pi-Hole, une solution très intéressante, mais j&amp;rsquo;ai eu pas mal de soucis avec. Configurer DNSMasq demande un peu plus d&amp;rsquo;efforts, mais ça a marché sans problème. J&amp;rsquo;ai mis seulement 20 minutes à tout configurer sur mon Raspberry Pi, surtout parce que j&amp;rsquo;avais les traces de mes erreurs à chaque test.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Faire les choses soi-même, c&amp;rsquo;est aussi apprendre, comprendre, et réaliser à quel point on peut aller plus loin.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;prérequis&#34;&gt;Prérequis&lt;/h2&gt;
&lt;p&gt;Ayez une machine dédiée, comme un Raspberry Pi ou un vieux PC, connectée en permanence au réseau, de préférence en Ethernet. Vous devez pouvoir vous y connecter à distance via SSH ou physiquement avec un clavier/écran.&lt;/p&gt;
&lt;p&gt;Un éditeur de texte comme &lt;code&gt;vim&lt;/code&gt;, &lt;code&gt;nano&lt;/code&gt;, ou même &lt;code&gt;emacs&lt;/code&gt; si vous êtes un peu fou, est nécessaire pour corriger les fichiers.&lt;/p&gt;
&lt;p&gt;Mon setup : un Raspberry Pi 4 avec Fedora 41, branché sur une Livebox 6. Adaptez les instructions à votre environnement.&lt;/p&gt;
&lt;p&gt;Assurez-vous d&amp;rsquo;avoir &lt;code&gt;nmcli&lt;/code&gt; (client NetworkManager) sur votre machine dédiée. Sinon, adaptez la configuration de l&amp;rsquo;IP statique.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Toutes les commandes doivent être exécutées avec les droits administrateur&lt;/strong&gt;, donc utilisez &lt;code&gt;sudo&lt;/code&gt; ou connectez-vous en tant que &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Prenez un café ou un remontant pour rester concentré.&lt;/p&gt;
&lt;h2 id=&#34;avant-de-vous-lancer&#34;&gt;Avant de vous lancer&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Ne vous lancez pas sans savoir à quoi servent ces configurations.&lt;/strong&gt; Vous devez comprendre :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ce qu&amp;rsquo;est un serveur DNS,&lt;/li&gt;
&lt;li&gt;et ce qu&amp;rsquo;est un serveur DHCP,&lt;/li&gt;
&lt;li&gt;et que vous allez perdre la connexion réseau sur certaines machines pendant la configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Premier point&lt;/strong&gt; : On va désactiver le DHCP de la Livebox. Pendant quelques minutes, votre réseau local va être perturbé. Votre machine dédiée (un Linux toujours allumé, un Raspberry Pi chez moi) prendra le relais.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Donc, assurez-vous de pouvoir vous connecter à votre machine DHCP/DNS&lt;/strong&gt; soit avec un clavier et un écran, soit en ajustant le paramétrage réseau.&lt;/p&gt;
&lt;p&gt;Pas de panique, je vais vous donner les clés pour éviter de rester dans le noir trop longtemps.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais avant, quelques définitions&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;cest-quoi-un-dhcp-&#34;&gt;C&amp;rsquo;est quoi un DHCP ?&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Dynamic Host Configuration Protocol&lt;/em&gt; : Un service qui automatise la configuration de votre réseau local. Quand une machine rejoint le réseau, elle n&amp;rsquo;a pas d&amp;rsquo;adresse IP, pas de DNS, etc. Elle envoie un message en Broadcast pour obtenir ces informations. Votre Livebox (ou Freebox) fait office de serveur DHCP&amp;hellip; pour le moment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/livebox-limit%C3%A9e-aidons-la-avec-dnsmasq/./dhcp-simple.png&#34; alt=&#34;DHCP&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;cest-quoi-un-serveur-dns-&#34;&gt;C&amp;rsquo;est quoi un serveur DNS ?&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Domain Name Server&lt;/em&gt; : Un serveur qui traduit les noms de domaine en adresses IP. Votre Livebox, comme beaucoup de routeurs, fait office de serveur DNS. Elle résout les noms de domaine internet et les noms de machines de votre réseau.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/livebox-limit%C3%A9e-aidons-la-avec-dnsmasq/./dns.png&#34; alt=&#34;Principe du DNS&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;les-deux-ensemble-cest-mieux-&#34;&gt;Les deux ensemble, c&amp;rsquo;est mieux ?&lt;/h3&gt;
&lt;p&gt;Le DNS et le DHCP peuvent être séparés, mais les avoir ensemble est très pratique. Dnsmasq, par exemple, enregistre le nom de la machine dans le DNS quand il lui attribue une IP. Actuellement, c&amp;rsquo;est votre box internet qui gère les deux.&lt;/p&gt;
&lt;h2 id=&#34;configuration-réseau-de-la-machine-dédiée&#34;&gt;Configuration réseau de la machine dédiée&lt;/h2&gt;
&lt;p&gt;Votre machine qui va servir à faire office de DNS et de DHCP doit être configurée pour avoir une adresse IP statique et
ne plus utiliser &lt;code&gt;systemd-resolved&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;première-chose-passer-la-machine-dédiée-en-ip-statique&#34;&gt;Première chose, passer la machine dédiée en IP statique&lt;/h3&gt;
&lt;p&gt;Pourquoi une IP statique ? Parce que cette machine deviendra le DNS du réseau local. Si son adresse change, vous risquez une désynchronisation des adresses IP sur les machines du réseau. On fige donc l&amp;rsquo;IP.&lt;/p&gt;
&lt;p&gt;Je pars du principe que vous avez connecté cette machine dédiée en Ethernet directement sur la Livebox. C&amp;rsquo;est plus stable, plus rapide et ça évite bien des problèmes.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est une étape délicate, car vous allez perdre la connexion à cette machine lorsque vous changerez son IP.&lt;/p&gt;
&lt;p&gt;On va utiliser l&amp;rsquo;adresse IP 192.168.1.250. Normalement, la Livebox n&amp;rsquo;assigne pas d&amp;rsquo;adresse IP au-delà de 192.168.1.150. Vérifiez quand même que cette adresse n&amp;rsquo;est pas déjà utilisée par une autre machine. Pour le moment, on garde le DNS de la Livebox.&lt;/p&gt;
&lt;p&gt;La manipulation est simple, mais si vous faites cela sur un shell SSH, vous allez perdre la connexion à votre Raspberry Pi, ce qui est normal. Il faudra se reconnecter avec la nouvelle adresse IP.&lt;/p&gt;
&lt;p&gt;On garde la Livebox comme serveur DNS et le domaine &amp;ldquo;home&amp;rdquo;. Pour le moment, on continue de l&amp;rsquo;utiliser.&lt;/p&gt;
&lt;p&gt;On va utiliser &lt;code&gt;nmcli&lt;/code&gt; (client NetworkManager). Si votre machine est configurée différemment, utilisez l&amp;rsquo;outil adéquat.&lt;/p&gt;
&lt;p&gt;Pour trouver le nom de la connexion réseau, utilisez :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;nmcli connection --show
NAME                UUID                                  TYPE      DEVICE
Wired connection 1  9b63ca3f-bbf2-3567-8c24-39cc956e92fe  ethernet  end0
lo                  f5674142-e3f6-468d-b8a7-b89bb8e609c0  loopback  lo
podman1             f27ed4fc-043e-4671-aab8-f3f1b294342d  bridge    podman1
Livebox-0730        440b56e8-7407-4ff1-84b3-d78d76722594  wifi      --

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le nom &amp;ldquo;&lt;code&gt;Wired connection 1&lt;/code&gt;&amp;rdquo; est le nom de la connexion réseau à changer chez moi, elle peut être différente chez vous.
Gardez aussi en tête le nom de l&amp;rsquo;interface utilisée, chez moi est &lt;code&gt;end0&lt;/code&gt;, ça peut être un truc différent chez vous.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vous remarquez que j&amp;rsquo;ai Podman sur cette machine, ça induit de faire attention quand on va paramétrer le serveur DNS
de Dnsmasq, car Podman utilise aussi un DNS interne. On va le voir plus tard.&lt;/p&gt;&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;## nom de la connexion réseau
CONNECTION=&amp;quot;Wired connection 1&amp;quot;
## votre nouvelle IP pour le raspberry pi
NEW_IP=192.168.1.250
## on va garder le même domaine que la Livebox
DOMAIN=home

## on met la connexion en statique
nmcli connection modify &amp;quot;$CONNECTION&amp;quot; \
  ipv4.addresses $NEW_IP/24 \
  ipv4.gateway 192.168.1.1 \
  ipv4.dns  192.168.1.1 \
  ipv4.dns-search $DOMAIN \
  ipv4.method manual
systemctl restart NetworkManager
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc voilà, on a mis une nouvelle adresse IP, une passerelle (la Livebox), un DNS (la Livebox) et un domaine
(&amp;ldquo;home&amp;rdquo;). On a bien spécifié que la méthode est &amp;ldquo;manuelle&amp;rdquo;, donc que l&amp;rsquo;on ne veut pas une IP assignée par le DHCP de la
Livebox.&lt;/p&gt;
&lt;p&gt;Là, vous allez certainement perdre la connexion SSH. Donc, reconnectez-vous sur 192.168.1.250 et vérifiez que tout va
bien. Faites un &lt;code&gt;ping&lt;/code&gt; sur &amp;ldquo;google.com&amp;rdquo;, une machine du réseau, etc&amp;hellip;&lt;/p&gt;
&lt;h3 id=&#34;suppression-de-systemd-resolved&#34;&gt;Suppression de systemd-resolved&lt;/h3&gt;
&lt;p&gt;Vous devriez avoir retrouvé la connexion SSH sur votre machine dédiée (le Raspberry Pi chez moi). On continue la
configuration.&lt;/p&gt;
&lt;p&gt;On va supprimer &lt;code&gt;systemd-resolved&lt;/code&gt;, c&amp;rsquo;est un outil sympa sur les postes clients, mais sur un serveur avec réseau
statique, qui plus est un serveur DNS, c&amp;rsquo;est une plaie. Il va nous faire des misères de conflits de port, ça va nous
énerver plus que nous rendre service. Allez ça dégage !&lt;/p&gt;
&lt;p&gt;Supprimer &lt;code&gt;systemd-resolved&lt;/code&gt; ne suffit pas, il faut forcer le système à
régénérer un fichier &lt;code&gt;/etc/resolv.conf&lt;/code&gt; qui ne soit pas un lien symbolique vers
&lt;code&gt;/run/systemd/resolve/stub-resolv.conf&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo systemctl stop systemd-resolved
systemctl disable --force systemd-resolved

## et on supprime /etc/resolv.conf qui est un lien symbolique
rm -f /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Attention&lt;/strong&gt; à partir de maintenant, vous n&amp;rsquo;avez plus de serveur DNS**, relancez la connexion et vérifiez que le
fichier &lt;code&gt;/etc/resolv.conf&lt;/code&gt; est bien créé.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl restart NetworkManager
cat /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;S&amp;rsquo;il est vide ou manquant, vérifiez que le service &lt;code&gt;systemd-resolved&lt;/code&gt; est bien arrêté et désactivé. Regardez les
erreurs avec &lt;code&gt;journalctl -e -u NetworkManager&lt;/code&gt;. Bref, trouvez pourquoi&amp;hellip;&lt;/p&gt;
&lt;h3 id=&#34;checkpoint-de-votre-config-réseau&#34;&gt;Checkpoint de votre config réseau&lt;/h3&gt;
&lt;p&gt;On résume, votre petit serveur qui va faire DNS / DHCP, est à présent maitre de sa configuration réseau.&lt;/p&gt;
&lt;p&gt;À ce stade, son adresse IP n&amp;rsquo;est plus assignée par la Livebox. La Livebox sert encore de routeur et de serveur DNS, mais
ça ne va pas durer.&lt;/p&gt;
&lt;h2 id=&#34;configurer-dnsmasq-pour-faire-le-job&#34;&gt;Configurer DNSMasq pour faire le job&lt;/h2&gt;
&lt;p&gt;On va enfin installer l&amp;rsquo;outil magique qui va remplacer ce que la Livebox fait très mal : &lt;strong&gt;dnsmasq&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&#34;dnsmasq-partie-dns&#34;&gt;DNSMasq, partie DNS&lt;/h3&gt;
&lt;p&gt;Pour le moment, c&amp;rsquo;est votre Livebox qui fait office de serveur DNS.&lt;/p&gt;
&lt;p&gt;On va maintenant faire en sorte que cette machine dédiée devienne un serveur DNS.&lt;/p&gt;
&lt;p&gt;Vous commencez par installer &lt;code&gt;dnsmasq&lt;/code&gt; et deux ou trois outils pour tester notre configuration (&lt;code&gt;dig&lt;/code&gt;, &lt;code&gt;ping&lt;/code&gt;&amp;hellip;).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;dnf install -y dnsmasq bind-utils iputils
## utilisateurs de Debian/Ubuntu, débrouillez vous avec apt...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Très important&lt;/strong&gt; : il faut accepter d&amp;rsquo;être contacté pour les requêtes DNS et DHCP, donc ouvrir le firewall sur les
bons ports ; on peut donner les noms de services, plus clair :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;## je vous rappelle que je suis sur Fedora, donc firewall-cmd
## est l&#39;outil de gestion du firewall
firewall-cmd --add-service=dns --permanent
firewall-cmd --add-service=dhcp --permanent
firewall-cmd --reload

## si vous utilisez une Debian/like, vous pouvez utiliser ufw
ufw allow dns
ufw allow dhcp
ufw reload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La chose bien foutue sur Fedora (je ne sais pas pour les autres distributions) est que &lt;code&gt;dnsmasq&lt;/code&gt; propose un fichier de
configuration &lt;em&gt;de base&lt;/em&gt; complètement commenté dans &lt;code&gt;/etc/dnsmasq.conf&lt;/code&gt;. Et je vous conseille de ne pas le modifier. Il
doit servir de référence. La configuration perso, on la place dans des fichiers à l&amp;rsquo;intérieur du répertoire
&lt;code&gt;/etc/dnsmasq.d/&lt;/code&gt;. C&amp;rsquo;est une bonne pratique, car ça permet de séparer les configurations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En tout cas, chez Ubuntu, &lt;a href=&#34;https://doc.ubuntu-fr.org/configuration_serveur_dns_dhcp&#34;&gt;leur doc&lt;/a&gt;, c&amp;rsquo;est la fête de la saucisse,
édition à la bourrin dans le fichier de base ! Ils me tuent&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;On va créer un fichier de configuration dans &lt;code&gt;/etc/dnsmasq.d/main.conf&lt;/code&gt; pour les trucs de base :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# interface réseau à écouter (end0, eth0 ...)
INTERFACE=$(ip r | awk &#39;/default/{print $5; exit}&#39;)

cat 1&amp;gt;/etc/dnsmasq.d/main.conf &amp;lt;&amp;lt; EOF
## port à écouter
port=53
## quelles interfaces écouter
bind-interfaces
interface=$INTERFACE

## Ajoutez des DNS externes pour le web
## on va prendre les DNS de Cloudflare et de Quad9
server=1.1.1.1
server=9.9.9.9
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Juste un détail, mais tout à l&amp;rsquo;heure, je vous ai dit que j&amp;rsquo;ai Podman, donc je suis obligé de spécifier l&amp;rsquo;interface
&lt;code&gt;end0&lt;/code&gt; (détectée avec ma commande &amp;ldquo;&lt;code&gt;ip r...&lt;/code&gt;&amp;rdquo;). Sinon, Dnsmasq voudrait écouter sur toutes les interfaces réseau,
y-compris &amp;ldquo;&lt;code&gt;podman1&lt;/code&gt;&amp;rdquo;. Et comme un serveur DNS est déjà présent ici, DNSMasq plantera.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Vous pouvez lancer &lt;code&gt;dnsmaq&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo systemctl enable --now dnsmasq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Si vous avez un message d&amp;rsquo;erreur, allez voir &lt;code&gt;journalctl -e -u dnsmasq&lt;/code&gt; pour comprendre ce qui ne va pas.&lt;/strong&gt; Ne continuez
pas tant que dnsmasq ne fonctionne pas !&lt;/p&gt;
&lt;p&gt;Si tout va bien, alors on va changer le DNS utilisé sur cette machine pour qu&amp;rsquo;elle utilise &lt;code&gt;dnsmasq&lt;/code&gt; comme serveur DNS.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;## nom de la connexion réseau
CONNECTION=&amp;quot;Wired connection 1&amp;quot;
NEW_IP=192.168.1.250
## on change le DNS de la connexion réseau
nmcli connection modify &amp;quot;$CONNECTION&amp;quot; \
  ipv4.dns  $NEW_IP
nmcli device reapply &amp;quot;$CONNECTION&amp;quot;

# je ne sais plus si c&#39;est utile :
systemctl restart NetworkManager
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous remarquez que je mets l&amp;rsquo;adresse de DNS sur le Raspberry pi sur &amp;ldquo;lui-même&amp;rdquo;. C&amp;rsquo;est normal ! parce que le
Raspberry Pi &lt;strong&gt;est le serveur DNS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Testons !&lt;/strong&gt; C&amp;rsquo;est important, il faut absolument que &lt;code&gt;dnsmasq&lt;/code&gt; fonctionne proprement en tant que serveur DNS.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;## On regarde si NetworkManager a bien pris en compte
## la nouvelle configuration
cat /etc/resolv.conf
## doit ressembler à ça:
## Generated by NetworkManager
## search home
## nameserver 192.168.1.250

## on teste une résolution avec dnsmasq...
dig @127.0.0.1 +short google.com

## et faites le sans dire quel DNS utiliser
dig google.com | grep SERVER:
## répondra quelque chose comme:
##;; SERVER: 192.168.1.250#53(192.168.1.250) (UDP)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Si aucune adresse IP n&amp;rsquo;est retournée ou que ce n&amp;rsquo;est pas l&amp;rsquo;adresse de votre machine qui sert de DNS,
c&amp;rsquo;est que dnsmasq ne fonctionne pas. Vous devez corriger ça !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Si tout se passe bien, d&amp;rsquo;abord &amp;ldquo;bravo à vous&amp;rdquo;, et on passe à la suite !&lt;/p&gt;
&lt;h3 id=&#34;dnsmasq-partie-dhcp&#34;&gt;DNSMasq, partie DHCP&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est le moment de dire adieu au DHCP de la Livebox.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Maintenant, on va dire à notre DNSMasq qu&amp;rsquo;il va faire office de DHCP. C&amp;rsquo;est donc lui qui répondra à toutes les machines
qui vont arriver sur le réseau.&lt;/p&gt;
&lt;p&gt;Il faut qu&amp;rsquo;il leur fournisse une adresse IP, un DNS, une passerelle.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l&amp;rsquo;adresse IP, on va lui dire qu&amp;rsquo;on ne dépassera pas 192.168.1.249, on va même dire qu&amp;rsquo;on veut des adresses entre
192.168.1.10 à 192.168.1.249. Ça nous permet de se réserver les adresses IP que personne ne peut avoir à moins de le
faire à la main,&lt;/li&gt;
&lt;li&gt;le nouveau DNS, donc l&amp;rsquo;IP de la machine dédiée,&lt;/li&gt;
&lt;li&gt;la passerelle, c&amp;rsquo;est l&amp;rsquo;adresse IP de la Livebox (192.168.1.1 par défaut)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour garder la même logique que ce que faisait la Livebox, on va utiliser un suffixe DNS en &amp;ldquo;.home&amp;rdquo;. On l&amp;rsquo;a spécifié
tout à l&amp;rsquo;heure pour configurer notre connexion statique.&lt;/p&gt;
&lt;p&gt;Donc, configurons :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;DOMAIN=home
IP_ADDR=$(hostname -I | awk &#39;{print $1}&#39;)

cat 1&amp;gt; /etc/dnsmasq.d/dhcp.conf &amp;lt;&amp;lt;EOF
log-dhcp
## rang d&#39;ip qu&#39;on peut donner aux machines, on
## se réserve les IPs de 0 à 10 et au delà de 249
dhcp-range=192.168.1.10,192.168.1.249,12h
dhcp-option=option:netmask,255.255.255.0
dhcp-option=option:router,192.168.1.1
dhcp-option=option:dns-server,$IP_ADDR
dhcp-option=option:domain-name,$DOMAIN
dhcp-option=option:domain-search,$DOMAIN
enable-ra

## et on est dans un domaine
domain=$DOMAIN
local=/$DOMAIN/

# et on notre DHCP c&#39;est un peu comme Sauron
dhcp-authoritative
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En gros (et en simplifiant) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On assigne un intervalle d&amp;rsquo;adresses IP de 192.168.1.10 à 192.168.1.249, nos machines auront une adresse IP
pendant 12 heures (12h). Si elles ne sont plus là pendant ce laps de temps, alors elles perdent la réservation. Une
nouvelle IP pourra leur être assignée. Augmentez la durée si vous voulez, mais 12 heures est une valeur assez commune.&lt;/li&gt;
&lt;li&gt;Quand on configure une machine, on lui assigne un masque de sous-réseau, le DNS (notre raspberry Pi) et la
passerelle (la Livebox)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;domain=home&lt;/code&gt; : on dit à dnsmasq que le domaine de notre réseau est &amp;ldquo;home&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;local=/home/&lt;/code&gt; : on dit à dnsmasq de considérer que les machines du réseau sont dans le domaine &amp;ldquo;home&amp;rdquo; (donc il
retrouvera les noms de machines avec un suffixe .home)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dhcp-authoritative&lt;/code&gt; : on dit à dnsmasq qu&amp;rsquo;il est le seul serveur DHCP du réseau, et qu&amp;rsquo;il n&amp;rsquo;y a pas de conflit
possible avec la Livebox.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enable-ra&lt;/code&gt; : on active les annonces de routeur (Router Advertisements) pour que les machines puissent
découvrir la passerelle et le DNS (optionnel, mais recommandé)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi mettre un domaine ?&lt;/strong&gt; Pour deux raisons&amp;hellip;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parce que c&amp;rsquo;est plus propre, qu&amp;rsquo;on va éviter des conflits avec les noms de domaines internet si une machine fait
n&amp;rsquo;importe quoi,&lt;/li&gt;
&lt;li&gt;et puis &lt;code&gt;systemd-resolved&lt;/code&gt; va nous râper les raisins à ne pas accepter de faire des résolutions de noms sans
domaine. &lt;strong&gt;En conséquence, on est obligé de lui en donner un.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Notez que c&amp;rsquo;est la raison qui a fait que j&amp;rsquo;ai rendu mon routeur TP-Link qui n&amp;rsquo;assigne aucun suffixe DNS&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Si vous voulez forcer une IP pour une machine en particulier, faitez un fichier de configuration qui s&amp;rsquo;appelle par
exemple &lt;code&gt;/etc/dnsmasq.d/reserved.conf&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;## dhcp-host=&amp;lt;MAC&amp;gt;,&amp;lt;IP&amp;gt; ou encore...
## dhcp-host=&amp;lt;MAC&amp;gt;,&amp;lt;IP&amp;gt;,&amp;lt;NOM&amp;gt;,&amp;lt;TEMPS&amp;gt;
## par exemple, pour mon poste fixe:
dhcp-host=a6:db:13:b0:df:45,192.168.1.98
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et enfin, on configure le fichier &lt;code&gt;/etc/dnsmasq.d/home.conf&lt;/code&gt; pour les options de base du DNS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cat 1&amp;gt; /etc/dnsmasq.d/home.conf &amp;lt;&amp;lt;EOF
no-resolv
no-negcache
no-hosts
domain-needed
bogus-priv

## mon Raspberry Pi sert aussi de NAS
address=/nas/192.168.1.250
address=/nas.home/192.168.1.250
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Houlla&amp;hellip; Alors, je vous explique ce que ça veut dire:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;no-resolv: on ne va pas utiliser le fichier &lt;code&gt;/etc/resolv.conf&lt;/code&gt; pour les requêtes DNS&lt;/li&gt;
&lt;li&gt;no-negcache: on ne va pas mettre en cache les réponses négatives (celles qui disent que le nom n&amp;rsquo;existe pas)&lt;/li&gt;
&lt;li&gt;no-hosts: on ne va pas utiliser le fichier &lt;code&gt;/etc/hosts&lt;/code&gt; pour les résolutions de noms&lt;/li&gt;
&lt;li&gt;domain-needed: on ne va pas répondre aux requêtes DNS sans domaine (c&amp;rsquo;est important)&lt;/li&gt;
&lt;li&gt;bogus-priv: si aucune adresse IP n&amp;rsquo;est trouvée, on ne va pas répondre aux requêtes pour les adresses privées
(192.168.x.x, 10.x.x.x, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pourquoi j&amp;rsquo;ajoute &lt;code&gt;address=/nas/...&lt;/code&gt; et &lt;code&gt;address=/nas.home/...&lt;/code&gt; ?&lt;/p&gt;
&lt;p&gt;Mon Raspberry pi qui sert de DHCP/DNS est configuré en IP statique. Il ne s&amp;rsquo;enregistre pas tout seul dans le DNS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Donc, je dois le déclarer manuellement.&lt;/strong&gt; Parce que le DHCP ne s&amp;rsquo;est pas occupé de lui-même.&lt;/p&gt;
&lt;p&gt;Évidemment que vous pouvez utiiliser un autre nom. Mon Raspberry Pi est aussi un NAS, je l&amp;rsquo;appelle comme
ça. Vous pouvez l&amp;rsquo;appeler &amp;ldquo;rpi&amp;rdquo;, &amp;ldquo;domain&amp;rdquo; ou &amp;ldquo;jean-michel&amp;rdquo;, c&amp;rsquo;est vous qui voyez.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maintenant, on passe aux choses sérieuses !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On redémarre le service &lt;code&gt;dnsmasq&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl restart dnsmasq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il ne faut &lt;strong&gt;aucune erreur&lt;/strong&gt; !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;À partir de maintenant, la machine dédiée est le serveur DNS et le serveur DHCP de votre réseau local.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est le moment fatidique, on coupe le DHCP de la Livebox.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/livebox-limit%C3%A9e-aidons-la-avec-dnsmasq/./stop-dhcp.png&#34; alt=&#34;Coupure le DHCP de la Livebox&#34;&gt;&lt;/p&gt;
&lt;p&gt;Attention, toutes les machines vont avoir un souci pendant quelques secondes, voir plusieurs minutes.
Leur configuration réseau ne sera pas à jour, donc il faut relancer la phase de négociation DHCP. En gros, couper le
réseau des machines et le redémarrer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pour le moment, votre réseau est aussi bordélique qu&amp;rsquo;une chambre d&amp;rsquo;ado élevé avec la méthode Montessori.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;C&amp;rsquo;est la phase &amp;ldquo;critique&amp;rdquo;, tant que vos machines ne se reconnectent pas, elles n&amp;rsquo;ont pas reçu les informations du DHCP.&lt;/p&gt;
&lt;h2 id=&#34;on-teste-sur-une-machine-du-réseau&#34;&gt;On teste sur une machine du réseau&lt;/h2&gt;
&lt;p&gt;Sur mon laptop (ouais, mon &amp;ldquo;ordinateur portable&amp;rdquo;), j&amp;rsquo;ai coupé le wifi et je l&amp;rsquo;ai relancé. Puis je vérifie :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;ip r get 1                                                         metal3d@patrice-laptop
## m&#39;affiche
## 1.0.0.0 via 192.168.1.1 dev wlo1 src 192.168.1.98 uid 1000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;J&amp;rsquo;ai bien une IP et la passerelle par défaut, ouf !&lt;/p&gt;
&lt;p&gt;Et sinon, coté DNS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;resolvectl status
## affiche:
##...
Link 3 (wlo1)
    Current Scopes: DNS LLMNR/IPv4 LLMNR/IPv6
         Protocols: +DefaultRoute LLMNR=resolve -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 192.168.1.250
       DNS Servers: 192.168.1.250
        DNS Domain: home
     Default Route: yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tout va bien ! Le DHCP m&amp;rsquo;a assigné le DNS qu&amp;rsquo;on a configuré, et le domaine &amp;ldquo;home&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vous pouvez être fiers de vous, tout est OK !&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;quelques-vérifications-supplémentaires&#34;&gt;Quelques vérifications supplémentaires&lt;/h2&gt;
&lt;p&gt;Sur le raspberry Pi ou la machine dédié qui fait DHCP/DNS, vous pouvez vérifier que les machines sont bien enregistrées :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt; cat /var/lib/dnsmasq/dnsmasq.leases
1754853901 20:4e:f6:30:2b:2e 192.168.1.213 Roomba-DB09C7BAA7C54FC881662B9B88592317 *
1754855117 d8:6c:63:5f:34:e8 192.168.1.94 Chromecast *
1754853936 6a:07:a8:80:6e:19 192.168.1.90 patrice-desktop 01:6a:07:a8:80:6e:19
1754853936 50:14:79:1b:dc:7a 192.168.1.62 iRobot-85DB52B2707343789E585364D0C28099 *
1754867719 a6:db:13:b0:df:45 192.168.1.98 patrice-laptop 01:a6:db:13:b0:df:45
1754854162 66:e0:43:eb:2b:e9 192.168.1.26 Z-Flip6-de-Patrice 01:66:e0:43:eb:2b:e9
 #... etc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On peut aussi vérifier que le DNS fonctionne, en prenant un nom de machine :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;ping -c 2 patrice-desktop
PING patrice-desktop.home (192.168.1.90) 56(84) bytes of data.
64 bytes from patrice-desktop.home (192.168.1.90): icmp_seq=1 ttl=64 time=3.31 ms
64 bytes from patrice-desktop.home (192.168.1.90): icmp_seq=2 ttl=64 time=3.15 ms
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On voit par ailleurs que le nom de domaine &amp;ldquo;.home&amp;rdquo; est bien utilisé et que ça répond.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et maintenant, on peut aller beaucoup plus loin !&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;ce-que-livebox-ne-permettait-pas-on-peut-le-faire&#34;&gt;Ce que Livebox ne permettait pas, on peut le faire&lt;/h2&gt;
&lt;p&gt;D&amp;rsquo;abord, on utilise désormais les DNS qu&amp;rsquo;on veut. On a mis 1.1.1.1 et 9.9.9.9 mais vous pouvez mettre ceux de Orange,
de Google, de votre FAI&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et dorénavant, vous pouvez aussi ajouter des noms de machines, des alias, des domaines, etc.&lt;/p&gt;
&lt;p&gt;Reprenons l&amp;rsquo;exemple initial, j&amp;rsquo;ai une machine &amp;ldquo;patrice-desktop&amp;rdquo; qui est une machine sur laquelle j&amp;rsquo;ai installé pas mal de
trucs. Je veux pouvoir y accéder avec plusieurs noms.&lt;/p&gt;
&lt;p&gt;Un truc intéressant avec un &lt;em&gt;vrai&lt;/em&gt; serveur DNS, c&amp;rsquo;est qu&amp;rsquo;au lieu de dire &amp;ldquo;alors ce nom correspond à telle adresse IP&amp;rdquo;,
on peut lui dire &amp;ldquo;ce nom, il est un alias de tel nom&amp;rdquo;. Je vulgarise un peu hein&amp;hellip; C&amp;rsquo;est ce qu&amp;rsquo;on appelle un
&lt;code&gt;CNAME&lt;/code&gt; (Canonical Name).&lt;/p&gt;
&lt;p&gt;Donc, on va créer un fichier de configuration pour des alias, par exemple
&lt;code&gt;/etc/dnsmasq.d/alias.conf&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cname=ai.home,patrice-desktop.home
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Relancez le service &lt;code&gt;dnsmasq&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl restart dnsmasq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et sur un poste&amp;hellip;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;dig ai.home
##...
;; ANSWER SECTION:
ai.home.                0       IN      CNAME   patrice-desktop.home.
patrice-desktop.home.   0       IN      A       192.168.1.90
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comprenez bien, je peux désormais accéder à mon serveur avec le nom &lt;code&gt;ai.home&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai ensuite configuré ma machine pour que &lt;code&gt;ai.home&lt;/code&gt; pointe sur le conteneur &lt;a href=&#34;https://github.com/open-webui/open-webui&#34;&gt;OpenWebUI&lt;/a&gt; pour
utiliser des modèles locaux d&amp;rsquo;IA. J&amp;rsquo;ai aussi ajouté &lt;code&gt;ollama.home&lt;/code&gt; qui pointe sur le même serveur,
mais qui est un alias pour aller sur mon conteneur Ollama.&lt;/p&gt;
&lt;p&gt;Et vous pouvez faire autant de bordel que vous voulez.&lt;/p&gt;
&lt;p&gt;Tiens, un bonus : &lt;a href=&#34;https://github.com/KnightmareVIIVIIXC/AIO-Firebog-Blocklists&#34;&gt;https://github.com/KnightmareVIIVIIXC/AIO-Firebog-Blocklists&lt;/a&gt; contient des
fichiers à placer dans votre &lt;code&gt;/etc/dnsmasq.d/&lt;/code&gt; pour bloquer des domaines, des publicités, etc.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai plutôt utilisé &lt;a href=&#34;https://oisd.nl/setup/dnsmasq&#34;&gt;la liste simple de OISD&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;curl https://small.oisd.nl/dnsmasq2 &amp;gt; /etc/dnsmasq.d/oisd-blocklist.conf
systemctl restart dnsmasq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais vous pouvez tout à fait en faire autant avec n&amp;rsquo;importe quel domaine :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;## par exemple dans /etc/dnsmasq.d/blacklist.conf
##ipv4 et ipv6
local=/foo.com/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pourquoi &amp;ldquo;local&amp;rdquo; ? En fait, vous dites à DNSMasq de tenter de résoudre le nom de domaine localement. Et comme aucune
entrée locale n&amp;rsquo;existe, il ne trouvera pas le nom de domaine. Donc, il ne répondra pas. Ça rend donc la résolution de ce
domaine impossible. C&amp;rsquo;est un moyen hyper simple de bloquer un domaine.&lt;/p&gt;
&lt;p&gt;Résultat, par exemple sur le site Futura Sciences, réputé blindé de pubs&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/livebox-limit%C3%A9e-aidons-la-avec-dnsmasq/./blocked.png&#34; alt=&#34;Futura Sciences sans pub&#34;&gt;&lt;/p&gt;
&lt;p&gt;Bref, gérer son DNS c&amp;rsquo;est cool.&lt;/p&gt;
&lt;h2 id=&#34;alors-heureux-&#34;&gt;Alors, heureux ?&lt;/h2&gt;
&lt;p&gt;La plupart des box internet du marché, pour les particuliers, ne sont pas à la hauteur en termes de gestion réseau. Ce
sont de très bons routeurs internet pour la majorité, mais elles limitent vraiment les utilisateurs avancés.&lt;/p&gt;
&lt;p&gt;Configurer son propre DHCP/DNS peut faire peur. Ça demande un peu de patience, de temps et le courage d&amp;rsquo;affronter la crainte
de casser le réseau. Mais tout est rapidement réversible.&lt;/p&gt;
&lt;p&gt;DNSMasq ne m&amp;rsquo;a jamais déçu, je m&amp;rsquo;en suis servi à mainte reprise en local (pour réduire les latences de résolution de
nom ou avec Docker/Podman), je l&amp;rsquo;avais réglé pour supprimer les publicités sur une de mes machines, et je l&amp;rsquo;avais même
intégré dans un cluster pour corriger un gros souci de résolution interne.&lt;/p&gt;
&lt;p&gt;Il est léger, et donc tourne sans accroc sur un tout petit Raspberry Pi. Il est simple à configurer (c&amp;rsquo;est très
lisible).&lt;/p&gt;
&lt;p&gt;Là, je l&amp;rsquo;utilise pour mon réseau local depuis des semaines. Aucun souci.&lt;/p&gt;
&lt;p&gt;Donc oui, moi, je suis heureux d&amp;rsquo;avoir la maitrise de mon DNS, de pouvoir faire du cache, de mieux contrôler mon domaine
et de pouvoir faire des alias, couper les pubs, etc.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Je parle d&amp;rsquo;utilisateurs avancés, pas forcément des ingénieurs réseaux. Une personne qui bidouille un Raspberry Pi, un NAS, ou n&amp;rsquo;importe quel appareil connecté&amp;hellip;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>Exit Kaniko et BuildX, place à Buildah</title>
      <link>https://www.metal3d.org/blog/2025/exit-kaniko-et-buildx-passez-%C3%A0-buildah/</link>
      <pubDate>Wed, 06 Aug 2025 13:20:53 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2025/exit-kaniko-et-buildx-passez-%C3%A0-buildah/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2025/exit-kaniko-et-buildx-passez-%C3%A0-buildah/cover.png&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Kaniko est mort, et tout le monde se rue sur BuildX. Parce que Docker en parle, car &lt;em&gt;ça parait simple&lt;/em&gt;. Et au milieu
de tout ça, se trouve &lt;a href=&#34;https://buildah.io/&#34;&gt;Buildah&lt;/a&gt;. Et il devrait être la réponse par défaut chez les DevOps.&lt;/p&gt;
&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Utilisez Buildah pour construire des images OCI dans vos pipelines CI/CD. Avec les commandes :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;buildah build -t mon-image:latest /tmp/monimage
buildah push ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou bien: utilisez l&amp;rsquo;API pour faire des images sans Containerfile / Dockerfile si cela vous simplifie la vie.&lt;/p&gt;
&lt;p&gt;Pour les actions GitHub, ce template vous servira, en adaptant (par exemple pour créer le tag avec &lt;code&gt;github.ref_name&lt;/code&gt; et
ajouter le &amp;ldquo;&lt;code&gt;latest&lt;/code&gt;&amp;rdquo;, en ajoutant des labels, etc.) :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build image

# changez en on push, on release, etc. selon vos besoins
on: workflow_dispatch

# pour la suite
env:
  IMAGE_NAME: ${{ github.repository }}
  REGISTRY: ghcr.io

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Install Buildah
        run: |-
          sudo apt-get update
          sudo apt-get install -y buildah
      - name: Buildah build
        run: |-
          buildah build -t $REGISTRY/${IMAGE_NAME} -f ./oci/Containerfile .
          # puis... 
          # buiildah tag $REGISTRY/${IMAGE_NAME} $REGISTRY/${IMAGE_NAME}:latest
          # buildah tag $REGISTRY/${IMAGE_NAME} $REGISTRY/${IMAGE_NAME}:${{ github.ref_name }}
      - name: Buildah Push
        run: |-
          buildah login $REGISTRY -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
          buildah push $REGISTRY/${IMAGE_NAME,
          # et si vous avez taggé, vous pouvez faire
          # buildah push $REGISTRY/${IMAGE_NAME}:latest
          # buildah push $REGISTRY/${IMAGE_NAME}:${{ github.ref_name }}
          # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou utilisez les actions de Red Hat :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;env:
  IMAGE_NAME: ${{ github.repository }}
  REGISTRY: ghcr.io

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Build image
        id: build-image
        uses: redhat-actions/buildah-build@v2
        with:
          image: ${{ env.REGISTRY}}/${{ env.IMAGE_NAME }}
          tags: latest ${{ github.ref_name }}
          containerfiles: |
            ./oci/Containerfile
      - name: Push image
        uses: redhat-actions/push-to-registry@v2
        with:
          image: ${{ steps.build-image.outputs.image }}
          tags: ${{ steps.build-image.outputs.tags }}
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, si vous voulez en savoir plus sur Buildah, continuez la lecture.&lt;/p&gt;
&lt;h2 id=&#34;lorigine-du-bazar&#34;&gt;L&amp;rsquo;origine du bazar&lt;/h2&gt;
&lt;p&gt;Sur votre machine, vous &amp;ldquo;buildez&amp;rdquo; vos images Docker tranquille avec un petit &lt;code&gt;docker build&lt;/code&gt;, tout roule.&lt;/p&gt;
&lt;p&gt;Puis arrive la CI/CD&amp;hellip; et là, c’est le drame. Les runners tournent souvent &lt;em&gt;en conteneur&lt;/em&gt;, et Docker, lui,
a besoin d’un &lt;em&gt;daemon&lt;/em&gt;, d’un socket, de droits &lt;em&gt;root&lt;/em&gt;&amp;hellip; bref, tout ce qu’on veut éviter.&lt;/p&gt;
&lt;p&gt;Oui&amp;hellip; &lt;a href=&#34;https://docs.docker.com/engine/security/rootless/&#34;&gt;Docker a un mode &amp;ldquo;rootless&amp;rdquo;&lt;/a&gt; merci, je sais. Mais il va quand
même vous demander un daemon, un socket&amp;hellip; Et regardez la méthode pour le mettre en place, je vous laisse juger.&lt;/p&gt;
&lt;p&gt;Du coup, vous testez DinD (&lt;em&gt;Docker in Docker&lt;/em&gt;), et après quelques sueurs, ça fonctionne. À peu près.&lt;/p&gt;
&lt;p&gt;Google tente de nous sauver avec &lt;a href=&#34;https://github.com/GoogleContainerTools/kaniko&#34;&gt;Kaniko&lt;/a&gt; : pas besoin de daemon, ça build des images OCI comme un chef. On y
croit. Même Gitlab le propose dans sa documentation, on se dit que ça y est, on est soulagé.&lt;/p&gt;
&lt;p&gt;Et là&amp;hellip; &lt;em&gt;pouf&lt;/em&gt;, projet archivé. Plus de support. Rideau.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Résultat : tout le monde retourne chez BuildX. Parce que Docker, c’est Docker, hein.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Sauf que &lt;strong&gt;BuildX, c’est un peu comme DinD en costard&lt;/strong&gt;. Toujours dépendant du daemon, avec une stack à rallonge
pour le faire tourner proprement en CI. Alors oui, il &lt;em&gt;pourrait&lt;/em&gt; fonctionner sans daemon local, mais je vous passe les
détails, c&amp;rsquo;est un setup demandant des compétences assez avancées.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Spoiler : Buildah fait ça sans broncher, et sans daemon foireux à configurer.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;podman-un-début-de-réponse&#34;&gt;Podman, un début de réponse&lt;/h2&gt;
&lt;p&gt;Pendant que Docker et ses potes foutent le zbeul en CI/CD, y’a un petit héros discret qui traîne dans mon
terminal depuis des années : &lt;strong&gt;Podman&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://github.com/containers/podman.io/blob/main/static/logos/raw/podman-logo-orig.png?raw=true&#34; alt=&#34;Podman&#34;&gt;&lt;/p&gt;
&lt;p&gt;C’est clairement mon remplaçant préféré de Docker pour bosser en local. Les commandes sont quasiment les
mêmes (&lt;code&gt;podman run&lt;/code&gt;, &lt;code&gt;podman build&lt;/code&gt;, tout ça), donc pas besoin de réapprendre l’alphabet. Mais sous le capot,
c’est une autre histoire.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors, en vrai, depuis un bon moment, pour moi c&amp;rsquo;est Docker qui est une alternative à Podman. Sans rire, je trouve
Docker trop lourd, moins bien pensé, voire problématique. Sans méchanceté, je préfère Podman.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Il m&amp;rsquo;a supprimé une grosse charge mentale&amp;hellip;&lt;/p&gt;
&lt;p&gt;Parce qu’il est &lt;em&gt;daemonless&lt;/em&gt; — pas de service qui tourne en arrière-plan à l’insu de mon plein gré — et &lt;em&gt;rootless&lt;/em&gt; —
pas besoin de lui filer les clés de l’appart pour retrouver la vaiselles dégeux dans l&amp;rsquo;éviers (et je ne vous parle pas
de l&amp;rsquo;état des chiottes).&lt;/p&gt;
&lt;p&gt;Cerise sur le conteneur : il fonctionne dans votre répertoire utilisateur, avec votre ID, votre user, vos petits droits propres.
Il construit des images OCI aussi bien que Docker (ou plutôt comme Kaniko &lt;em&gt;avant son décès&lt;/em&gt;),
compatibles avec Docker / RunC / Nerdctl, etc.&lt;/p&gt;
&lt;p&gt;Mais voilà le twist : &lt;strong&gt;Podman, c’est surtout pour le développeur ou l&amp;rsquo;utilisateur qui veut faire tourner des machins
sur son poste&lt;/strong&gt;. Il est parfait pour lancer un conteneur vite fait pendant que vous tisez votre café, ou avec un fichier
&amp;ldquo;compose&amp;rdquo; (à la docker-compose), voire utiliser des Quadlets (bon sang que j&amp;rsquo;aime ce truc). Mais il n&amp;rsquo;est pas réellement
pensé pour des pipelines CI/CD à la chaîne.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Même s&amp;rsquo;il le peut, ce ne serait pas une vraie bonne idée.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;L’astuce ?&lt;/strong&gt; Le vrai cerveau de la construction d’image derrière Podman, c’est pas Podman lui-même. C’est son binôme discret : &lt;strong&gt;Buildah&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Et lui, on va en reparler&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;buildah-la-solution&#34;&gt;Buildah, la solution&lt;/h2&gt;
&lt;p&gt;Le &amp;ldquo;builder&amp;rdquo; d&amp;rsquo;image sous-jacent de Podman, c&amp;rsquo;est Buildah.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://buildah.io/images/buildah.png&#34; alt=&#34;Buildah logo&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://buildah.io/&#34;&gt;Buildah&lt;/a&gt; est, en gros, la sous-partie utilisable pour créer des images. Et vous allez voir qu&amp;rsquo;il propose des fonctionnalités
avancées qui peuvent vraiment vous intéresser.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En gros, quand vous faites &lt;code&gt;podman build&lt;/code&gt;, vous faites l&amp;rsquo;équivalent de &lt;code&gt;buildah build&lt;/code&gt;. On peut donc ne pas installer
Podman et n&amp;rsquo;utiliser que Buildah si notre but est de &lt;strong&gt;seulement&lt;/strong&gt; construire des images et les pousser sur un
registre.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Buildah peut fonctionner selon deux modes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;avec un fichier Containerfile (ou Dockerfile), via &lt;code&gt;buildah build&lt;/code&gt; (ce qui est la méthode la plus courrante, proche de
ce que vous connaissez déjà)&lt;/li&gt;
&lt;li&gt;de manière programmatique, en utilisant son API et donc de scripter une création d&amp;rsquo;image avec des conditions fines&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;la-construction-simple&#34;&gt;La construction simple&lt;/h2&gt;
&lt;p&gt;Amusons-nous et créons un fichier &lt;code&gt;/tmp/monimage/Containerfile&lt;/code&gt; et dedans, vous mettez :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM alpine:latest

RUN apk add --no-cache nginx
CMD [&amp;quot;nginx&amp;quot;, &amp;quot;-g&amp;quot;, &amp;quot;daemon off;&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et tapez la commande suivante :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;buildah build -t mon-image:latest /tmp/monimage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous le voyez, c&amp;rsquo;est super simple. La commande est typiquement la même que pour Docker ou Podman.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dans certaines documentations, vous allez voir &amp;ldquo;bud&amp;rdquo; au lieu de &amp;ldquo;build&amp;rdquo;. C&amp;rsquo;est la même chose, c&amp;rsquo;est juste un alias. En
fait la commande historique était &lt;code&gt;buildah bud&lt;/code&gt; pour &amp;ldquo;build using Dockerfile&amp;rdquo;. Mais depuis un certain temps, la
commande &amp;ldquo;&lt;code&gt;buildah --help&lt;/code&gt;&amp;rdquo; ne liste que &amp;ldquo;build&amp;rdquo; bien que l&amp;rsquo;alias &amp;ldquo;bud&amp;rdquo; existe encore.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Tentons de faire la même chose &lt;strong&gt;dans un conteneur&lt;/strong&gt;. Vous avez besoin de &amp;ldquo;capabilities&amp;rdquo; pour taper dans &lt;code&gt;/proc&lt;/code&gt; alors,
on va utiliser &lt;code&gt;--privileged&lt;/code&gt; pour l&amp;rsquo;occasion, mais on pourrait tout à fait utiliser des &amp;ldquo;capabilities&amp;rdquo; plus fines.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# avec podman ou docker, vous pouvez faire
podman run --rm -it --privileged \
  -v /tmp/monimage:/tmp/monimage:z \
  fedora:42
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, dans le conteneur, installez Buildah et faite un &amp;ldquo;build&amp;rdquo; de l&amp;rsquo;image :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;dnf install buildah -y
# On force le driver de stockage à vfs
export STORAGE_DRIVER=vfs

# puis
buildah build -t mon-image:latest /tmp/monimage
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;On force Buildah à utiliser le driver &amp;ldquo;vfs&amp;rdquo; car dans un conteneur, FUSE posera des problèmes. Il a besoin de
modules pour faire des points de montages, et nous avons des restrictions.
VFS est un poil plus lent, mais il fonctionne très bien dans un conteneur.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ça a marché. Sans monter de socket Docker/Podman, sans lancer de service, sans rien faire de spécial. Buildah a
simplement construit l&amp;rsquo;image OCI. C&amp;rsquo;est net.&lt;/p&gt;
&lt;p&gt;Petite note avec les distributions Debian like, genre Ubuntu, bien entendu, ils font les choses mal (oui, je trolle) et vous
avez des étapes supplémentaires à faire&amp;hellip;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;apt update &amp;amp;&amp;amp; apt upgrade ca-certificates -y &amp;amp;&amp;amp; apt install buildah -y
# puis, faire le reste
export STORAGE_DRIVER=vfs
buildah build -t mon-image:latest /tmp/monimage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas insurmontable, mais sans la mise à jour de &amp;ldquo;&lt;code&gt;ca-certificates&lt;/code&gt;&amp;rdquo;, vous allez avoir des erreurs de validation des
certificats pour récupérer les images de base.&lt;/p&gt;
&lt;h2 id=&#34;le-scripting-avec-buildah&#34;&gt;Le scripting avec Buildah&lt;/h2&gt;
&lt;p&gt;Mais là où Buildah devient intéressant, c&amp;rsquo;est qu&amp;rsquo;il permet de construire des images OCI de manière programmatique. Vous
pouvez effectuer les étapes de build les une après les autres, et agir en fonction de vos besoins. Donc, entendez bien ce
que je dis, vous pouvez éviter d&amp;rsquo;utiliser un Dockerfile ou un Containerfile et utiliser un script Bash. Faisons la même
image que précédemment, mais en scriptant la construction.&lt;/p&gt;
&lt;p&gt;Toujours dans un conteneur Alpine, avec Buildah installé, vous pouvez faire :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# dans un conteneur
export STORAGE_DRIVER=vfs

# on construit étape par étape
container=$(buildah from alpine:latest)
buildah run $container apk add --no-cache nginx
buildah config --cmd &#39;nginx -g &amp;quot;daemon off;&amp;quot;&#39; $container
buildah config --port 80 $container
buildah config --port 443 $container
buildah commit $container mon-image:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors &lt;strong&gt;ATTENTION&lt;/strong&gt;, ne vous méprenez pas. Le &amp;ldquo;run&amp;rdquo; de Buildah n&amp;rsquo;est pas le &amp;ldquo;run&amp;rdquo; de Docker ou Podman. Il ne lance pas
un conteneur mais effectue l&amp;rsquo;opération &amp;ldquo;RUN&amp;rdquo; d&amp;rsquo;un Containerfile / Dockerfile. Je vous rappelle que Buildah est
un outil de &lt;strong&gt;construction d&amp;rsquo;image OCI&lt;/strong&gt;, pas un outil de gestion de conteneurs.&lt;/p&gt;
&lt;p&gt;Je ne sais pas si vous réalisez l&amp;rsquo;intérêt de ce genre de construction. Imaginez par exemple que je veuille créer
une image en fonction de paramètres extérieurs, ajouter des labels depuis des résultats de commandes&amp;hellip; tiens par
exemple :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;container=$(buildah from alpine:latest)
buildah run $container apk add --no-cache nginx
buildah config --cmd &#39;nginx -g &amp;quot;daemon off;&amp;quot;&#39; $container
# on ajoute un label pour se souvenir de l&#39;envirinnement de build
# avec uname, date etc:
buildah config --label &amp;quot;build.env=$(uname -a)&amp;quot; $container
buildah config --label &amp;quot;build.date=$(date)&amp;quot; $container

buildah commit $container mon-image:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous allez pouvoir faire des scripts qui vont construire des images de manière conditionnelle, en fonction de
configuration, d&amp;rsquo;environnement, de paramètres passés en ligne de commande, etc.&lt;/p&gt;
&lt;p&gt;Alors effectivement, en règle générale, on préfèrera utiliser des Containerfile, mais le simple fait de pouvoir
descendre dans les couches d&amp;rsquo;abstraction vous confère un pouvoir et de grandes possibilités.&lt;/p&gt;
&lt;h2 id=&#34;expérimenter-dans-une-action-github&#34;&gt;Expérimenter dans une Action GitHub&lt;/h2&gt;
&lt;p&gt;Avant de vous parler des deux actions de Red Hat, on peut s&amp;rsquo;amuser à tester si Buildah fonctionne sans trop se taper la
tête contre le clavier.&lt;/p&gt;
&lt;p&gt;On va donc créer un petit test. J&amp;rsquo;ai un simple Containerfile dans un dépôt GitHub qui part de &amp;ldquo;nginx&amp;rdquo;, dépose un fichier
index dans le répertoire &lt;code&gt;/usr/share/nginx/html/&lt;/code&gt;. Ça suffit amplement.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM docker.io/nginx

COPY index.html /usr/share/nginx/html/index.html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, dans &lt;code&gt;.github/workflows/build-oci.yml&lt;/code&gt;, on va faire un petit test :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build image

on: workflow_dispatch

# pour la suite
env:
  IMAGE_NAME: ${{ github.repository }}
  REGISTRY: ghcr.io

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Install Buildah
        run: |-
          sudo apt-get update
          sudo apt-get install -y buildah
      - name: Buildah
        run: |-
          buildah build -t $REGISTRY/${IMAGE_NAME} -f ./oci/Containerfile .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&amp;rsquo;événement &lt;code&gt;workflow_dispatch&lt;/code&gt; permet de lancer le workflow manuellement depuis l&amp;rsquo;interface de GitHub. C&amp;rsquo;est plus
simple pour tester deux trois choses.&lt;/p&gt;
&lt;p&gt;Bref, voilà le résultat :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;# ...
STEP 1/2: FROM docker.io/nginx
Trying to pull docker.io/library/nginx:latest...
Getting image source signatures
Copying blob sha256:1d9f51194194364d2bbc948f82bc5161b65f277c6da4b0e47764e4fee2136e42
Copying blob sha256:140da4f89dcb7e49c2985bdd85b5c1c4e800916ee22abf7ada6b0bc2a99642a2
Copying blob sha256:59e22667830bf04fb35e15ed9c70023e9d121719bb87f0db7f3159ee7c7e0b8d
Copying blob sha256:2ef442a3816e24347655167c51e8d4fd033bf7da8f954f2f1cfd456cf6a06a80
Copying blob sha256:96e47e70491e3d5a8a65384c656bf4db13930a353c92252338e1167dfb1aeb84
Copying blob sha256:4b1e45a9989f25a049b0eace36b5405e64b4c2a31de65a92abfd86dbfb2b4a78
Copying blob sha256:f30ffbee4c546383f6ad349c50f8a2d2db5a83292489bbae5284b425960bcd42
Copying config sha256:2cd1d97f893f70cee86a38b7160c30e5750f3ed6ad86c598884ca9c6a563a501
Writing manifest to image destination
STEP 2/2: COPY index.html /usr/share/nginx/html/index.html
COMMIT imagetest
Getting image source signatures
Copying blob sha256:7cc7fe68eff66f19872441a51938eecc4ad33746d2baa3abc081c1e6fe25988e
Copying blob sha256:30837a0774b97fd5fd6306e4a67b9dba10fff174d82f0e7d2c6104dc79716fc3
Copying blob sha256:a6b19c3d00b104e9f5ae798917606b4706bf27748a008a5b44211b801ede45d8
Copying blob sha256:6b1b97dc92853f9880a37c378d2fe582eaca6567f3962d6d69dcbf35f65d95f9
Copying blob sha256:5c91a024d899eb09709296029b21f98722fa446ac7e37bb85b36f6484c499a2e
Copying blob sha256:0662742b23b26cd3b0c231ac4003d7343e3df50c62765b0baf6336c58156fa76
Copying blob sha256:f17478b6e8f3dcfdeb55c9e82529529ff6a984b3f72daa2fd723435f9ee9f93f
Copying blob sha256:6912176a43ddce467ba1cb2897ad73b6dbc97706370c1ce5ce9c55a8f439d06a
Copying config sha256:e3438ea014a5d5026f692e244b6190f53aeba0e0ea9604fcc8edf55e3da53c93
Writing manifest to image destination
--&amp;gt; e3438ea014a5
Successfully tagged localhost/imagetest:latest
e3438ea014a5d5026f692e244b6190f53aeba0e0ea9604fcc8edf55e3da53c93
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est tout bonnement OK.&lt;/p&gt;
&lt;p&gt;Et si vous voulez utiliser le registre GitHub de votre projet, ajoutez :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;buildah login $REGISTRY -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
buildah push $REGISTRY/${IMAGE_NAME}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bien entendu, vous pouvez le faire dans un &amp;ldquo;step&amp;rdquo; séparé. Aussi, pensez à &lt;code&gt;{{github.ref_name}}&lt;/code&gt; pour tagger les images,
ajoutez les labels pour avoir une description, etc.&lt;/p&gt;
&lt;p&gt;Et donc, par conséquent, on peut se passer de Containerfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Install Buildah
        run: |-
          sudo apt-get update
          sudo apt-get install -y buildah
      - name: Buildah
        run: |-
          container=$(buildah from alpine:latest)
          buildah run $container apk add --no-cache nginx
          buildah copy $container index.html /usr/share/nginx/html/index.html
          buildah config --port 80 $container
          buildah config --port 443 $container
          buildah config --cmd &#39;nginx -g &amp;quot;daemon off;&amp;quot;&#39; $container
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bref, Buildah est un outil puissant et flexible pour construire des images OCI, que ce soit avec un Containerfile ou de
manière programmatique. Il est idéal pour les environnements CI/CD, car il ne nécessite pas de service en arrière-plan
et peut être utilisé dans des conteneurs sans problème de sécurité.&lt;/p&gt;
&lt;h2 id=&#34;github-actions-red-hat&#34;&gt;GitHub Actions Red Hat&lt;/h2&gt;
&lt;p&gt;Red Hat vous propose deux actions pour faire du build d&amp;rsquo;image OCI avec Buildah.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/redhat-actions/buildah-build&#34;&gt;https://github.com/redhat-actions/buildah-build&lt;/a&gt; permet de build l&amp;rsquo;image&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/redhat-actions/push-to-registry&#34;&gt;https://github.com/redhat-actions/push-to-registry&lt;/a&gt; pour pousser&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce qui veut dire que votre workflow n&amp;rsquo;aura besoin que de deux étapes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;build de l&amp;rsquo;image&lt;/li&gt;
&lt;li&gt;push de l&amp;rsquo;image&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce qui va ressembler à ça:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;steps:
  - name: Checkout repository
    uses: actions/checkout@v4
  - name: Build image
    id: build-image
    uses: redhat-actions/buildah-build@v2
    with:
      image: ${{ env.REGISTRY}}/${{ env.IMAGE_NAME }}
      tags: latest ${{ github.ref_name }}
      containerfiles: |
        ./oci/Containerfile
  - name: Push image
    uses: redhat-actions/push-to-registry@v2
    with:
      image: ${{ steps.build-image.outputs.image }}
      tags: ${{ steps.build-image.outputs.tags }}
      registry: ${{ env.REGISTRY }}
      username: ${{ github.actor }}
      password: ${{ secrets.GITHUB_TOKEN }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comparaison avec BuidX à la sauce Github&amp;hellip;&lt;/p&gt;
&lt;p&gt;On nous propose :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une action de login vers le registre&lt;/li&gt;
&lt;li&gt;une action pour installer Qemu (ha ?)&lt;/li&gt;
&lt;li&gt;une action pour installer BuildX (bah ouais)&lt;/li&gt;
&lt;li&gt;une action &amp;ldquo;build and push&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En d&amp;rsquo;autres termes, avec BuildX à la sauce GitHub, ça va ressembler à ça :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Build OCI image
on:
  release:
    types:
      - published
  push:
    branches:
      - &amp;quot;features/**&amp;quot;
env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Log in to the Container registry
        uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./oci/Containerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ça ne parait pas énorme à première vue, mais regardez bien les étapes. On passe de 2 étapes à 4 étapes. Des setups assez
lourds, et un build bien plus long. Rappelez-vous que, plus vous ajoutez de &amp;ldquo;steps&amp;rdquo;, plus vous risquez un plantage du
setup, et plus ce sera difficile à corriger.&lt;/p&gt;
&lt;p&gt;Avec Buildah, vous pouvez le faire à la main, ou utiliser les actions de Red Hat, dans les deux cas c&amp;rsquo;est plus souple,
plus léger et plus facile à adapter. J&amp;rsquo;aime éviter l&amp;rsquo;accumulation de couches trop abstraites.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je fais partie des gens qui estiment qu&amp;rsquo;il est souvent plus simple de faire un truc soi-même que de se reposer sur des intégrations
obscures, à la limite de la boite noire, et de passer des heures à faire des diagnostics pour comprendre
qu&amp;rsquo;il manquait une option perdue dans les bas-fonds d&amp;rsquo;une doc mal écrite. On sait coder bordel, on sait scripter.
Arrêtez de vous laisser bouffer par des surcouches qui vous promettent la lune.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;le-multi-arch-le-point-sensible&#34;&gt;Le multi-arch, le point sensible&lt;/h2&gt;
&lt;p&gt;Buildah sait très bien gérer le build multi-arch. Cependant, il faut l&amp;rsquo;admettre, c&amp;rsquo;est un point un peu plus technique et
moins évident à comprendre.&lt;/p&gt;
&lt;p&gt;Pour préciser, un build &amp;ldquo;multi-arch&amp;rdquo; permet de construire une image qui peut être utilisée sur plusieurs architectures
(arm64, amd64, etc.). C&amp;rsquo;est essentiel pour les environnements modernes où les conteneurs peuvent tourner sur des
machines ayant différents types de microprocesseurs.&lt;/p&gt;
&lt;p&gt;Les registres OCI/Docker en version 2.2 savent bien différencier ces architectures pour une image donnée. Pour cela, il
faut créer un manifest et indiquer les images à utiliser. Docker rend ce truc un peu plus simple. Je l&amp;rsquo;avoue sans mal.
Pour autant, quand on a compris l&amp;rsquo;astuce, ça parait évident avec Buildah.&lt;/p&gt;
&lt;p&gt;Faisons deux images :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# image de base AMD64
buildah build -t mon-image:latest-amd64 --arch amd64 /tmp/monimage

# image ARM64
buildah build -t mon-image:latest-arm64 --arch arm64 /tmp/monimage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On peut pousser ces images telles quelles dans un registre, mais pour simplifier la vie des utilisateurs, on va
créer un &amp;ldquo;manifest&amp;rdquo; qui va regrouper ces deux images. Pour cela, on va utiliser la commande &lt;code&gt;buildah manifest&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;buildah manifest create mon-image:multi-arch
buildah manifest add mon-image:multi-arch mon-image:latest-amd64
buildah manifest add mon-image:multi-arch mon-image:latest-arm64

# pour pousser le manufest
buildah manifest push --all mon-image:multi-arch $REGISTRY/$IMAGE_NAME:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si maintenant un utilisateur lance une de ces commandes :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# avec docker
docker pull $REGISTRY/$IMAGE_NAME:latest
# avec podman
podman pull $REGISTRY/$IMAGE_NAME:latest
# ou encore avec nerdctl
nerdctl pull $REGISTRY/$IMAGE_NAME:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors, l&amp;rsquo;image adaptée pour son CPU sera automatiquement récupérée. C&amp;rsquo;est le principe du manifest OCI.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est ce poit qui est un peu douloureux, Buildah vous demande de faire des choses à la main. Ce n&amp;rsquo;est pas insurmontable,
mais il faut l&amp;rsquo;avouer, c&amp;rsquo;est moins simple qu&amp;rsquo;avec Docker.&lt;/p&gt;
&lt;h2 id=&#34;et-gitlab--ils-ont-déjà-fait-le-job&#34;&gt;Et Gitlab ? Ils ont déjà fait le job&lt;/h2&gt;
&lt;p&gt;Si vous jetez un œil &lt;a href=&#34;https://docs.gitlab.com/ci/docker/buildah_rootless_multi_arch/&#34;&gt;sur cette page de documentation&lt;/a&gt;,
vous allez voir que GitLab a déjà fait le job pour vous.&lt;/p&gt;
&lt;p&gt;Vous remarquerez qu&amp;rsquo;ils proposent d&amp;rsquo;utiliser l&amp;rsquo;image &lt;code&gt;quay.io/buildah/stable&lt;/code&gt; qui a déjà tout le bazar pour utiliser les
outils Buildah. Et pour parfaire le tout, cette image contient aussi les outils pour faire du &amp;ldquo;multi-arch&amp;rdquo;. Chose que
fait très bien Buildah.&lt;/p&gt;
&lt;p&gt;Quand vous comparer Buildah avec &lt;a href=&#34;https://docs.gitlab.com/ci/docker/using_docker_build/&#34;&gt;la page de doc pour Docker&lt;/a&gt; ou
celle &lt;a href=&#34;https://docs.gitlab.com/ci/docker/using_buildkit/&#34;&gt;pour utiliser BuildKit&lt;/a&gt;, vous êtes en droit de vous demander
pourquoi vous continuez à vous faire autant de mal.&lt;/p&gt;
&lt;p&gt;Mais je vous l&amp;rsquo;avoue, je me suis surtout penché sur GitHub pour le moment.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;J&amp;rsquo;ai du mal à comprendre pourquoi BuildX est autant mis en avant. Je suis conscient que Docker est roi, que Podman n&amp;rsquo;a
pas la force de frappe en termes de communication, et que les vieilles habitudes sont dures à abandonner.&lt;/p&gt;
&lt;p&gt;Mais, nous sommes en 2025, le monde du DevOps est de qualité, &lt;strong&gt;les ingénieurs sont ultra-compétents&lt;/strong&gt; et adorent fouiller
et tester. Podman (et notamment la version Desktop qui plait énormément sur Windows) est un outil fantastique. Buildah
fonctionne du tonnerre. Ces deux outils simplifient notre travail, ils soulagent les machines, ils font le job comme
on l&amp;rsquo;entend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Je pense, en toute honnêteté, que Buildah devrait être l&amp;rsquo;outil par défaut dans les pipelines CI/CD.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Je l&amp;rsquo;ai déjà dit sur différents réseaux sociaux, &lt;strong&gt;j&amp;rsquo;admire le travaille effectué par Docker&lt;/strong&gt;, ils ont révolutionné le
monde de l&amp;rsquo;informatique en proposant un système de conteneurs simple et efficace. Docker a changé la donne, a modifié
les paradigmes et permis l&amp;rsquo;avènement de Kubernetes. Cependant&amp;hellip; Docker se concentre sur les postes de travail, sur le
hub et sur sa version Desktop sous licence. C&amp;rsquo;est un choix assumé et je le respecte. Mais, nous, ingénieurs DevOps,
nous devons nous concentrer sur l&amp;rsquo;efficacité, la sécurité et la simplicité.&lt;/p&gt;
&lt;p&gt;Buildah répond à ces conditions.&lt;/p&gt;
&lt;p&gt;Quant à Podman, devenu l&amp;rsquo;outil par défaut pour Fedora / RockyLinux / Alma / Red Hat, il est temps que vous y prêtiez
attention si vous êtes développeurs.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Effet Bleach Bypass, les maths c&#39;est cool</title>
      <link>https://www.metal3d.org/blog/2025/effet-bleach-bypass-les-maths-cest-cool/</link>
      <pubDate>Wed, 05 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2025/effet-bleach-bypass-les-maths-cest-cool/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2025/effet-bleach-bypass-les-maths-cest-cool/cover.png&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Je bosse sur de la photo, des images, et je vais vous montrer pourquoi les maths c&amp;rsquo;est vraiment sympa quand on se penche
un peu sur la question. Et c&amp;rsquo;est &amp;ldquo;tous niveaux&amp;rdquo;, ou presque. Allez, disons niveau lycée.&lt;/p&gt;
&lt;p&gt;Vous ne connaissez peut-être pas le nom de l&amp;rsquo;effet &amp;ldquo;Bleach Bypass&amp;rdquo; (passage à l&amp;rsquo;eau de javel dans la langue de
Jean-Claude Van Damme), mais vous avez sûrement déjà vu des images traitées avec cet effet. C&amp;rsquo;est un effet qui donne une
impression &amp;ldquo;délavée&amp;rdquo;, très dramatique, brûlée. Steven Speilberg l&amp;rsquo;a utilisé dans &amp;ldquo;Il faut sauver le soldat Ryan&amp;rdquo;, mais
on le retrouve dans plein d&amp;rsquo;autres oeuvres cinématographiques. Par exemple &amp;ldquo;300&amp;rdquo; de Zack Snyder, ou encore dans &amp;ldquo;Seven&amp;rdquo;
de David Fincher.&lt;/p&gt;
&lt;p&gt;Et les photographes se sont aussi emparé de cet effet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vous avez le droit de ne pas aimer cet effet, mais ce n&amp;rsquo;est pas le sujet de l&amp;rsquo;article.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Cet effet, &lt;a href=&#34;https://fr.wikipedia.org/wiki/Traitement_sans_blanchiment&#34;&gt;expliqué sur la page Wikipedia&lt;/a&gt;, est assez simple
à mettre en place dans la plupart des logiciels. Sans math, juste avec quelques paramètres et un peu de flair.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Bleach_bypass_Example_Greg_Keene_crop.jpg/1024px-Bleach_bypass_Example_Greg_Keene_crop.jpg?20131026185449&#34; alt=&#34;L&amp;rsquo;exemple Wikipedia&#34;&gt;&lt;/p&gt;
&lt;p&gt;(By Soerfm - Own work, CC BY-SA 3.0, &lt;a href=&#34;https://commons.wikimedia.org/w/index.php?curid=29205537&#34;&gt;https://commons.wikimedia.org/w/index.php?curid=29205537&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id=&#34;leffet-est-simple-sur-blender-par-exemple&#34;&gt;L&amp;rsquo;effet est simple, sur Blender par exemple&lt;/h2&gt;
&lt;p&gt;J&amp;rsquo;adore &lt;a href=&#34;https://blender.org&#34;&gt;Blender&lt;/a&gt;. C&amp;rsquo;est un superbe outil pour faire des images de synthèse, du montage vidéo,
des animations, et plein d&amp;rsquo;autres trucs. Je l&amp;rsquo;utilise depuis 1999, c&amp;rsquo;est dingue ce qu&amp;rsquo;il a évolué.&lt;/p&gt;
&lt;p&gt;Et il a aussi un outil de &amp;ldquo;compositing&amp;rdquo;, en d&amp;rsquo;autres termes, on peut manipuler des vidéos et des photos pour faire du
montage et du travail de &amp;ldquo;post production&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Et je peux faire ce genre d&amp;rsquo;effet dans le &amp;ldquo;compositeur&amp;rdquo; d&amp;rsquo;image. C&amp;rsquo;est pas très compliqué :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;désaturer l&amp;rsquo;image (lui enlever de la couleur)&lt;/li&gt;
&lt;li&gt;passer par une courbe pour écraser les ombres et forcer les lumières&lt;/li&gt;
&lt;li&gt;et faire un &amp;ldquo;overlay&amp;rdquo; (une surcouche) de ce résultat sur l&amp;rsquo;image originale&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et ça marche plutôt bien.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/effet-bleach-bypass-les-maths-cest-cool/bleach-bypass-blender.png&#34; alt=&#34;L&amp;rsquo;effet dans Blender&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;L&amp;rsquo;image de référence, elle vient de cet article de Tristan Kneschke, que je vous recommande fortement :
&lt;a href=&#34;https://www.premiumbeat.com/blog/color-grading-bleach-bypass-look-in-davinci-resolve/&#34;&gt;https://www.premiumbeat.com/blog/color-grading-bleach-bypass-look-in-davinci-resolve/&lt;/a&gt;.
Le résultat m&amp;rsquo;a permis de comparer mes opérations à ce qu&amp;rsquo;il propose, et franchement ça m&amp;rsquo;a énormément aidé.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Dans Blender, clairement, il y a assez d&amp;rsquo;outils pour faire ça vite. Mais y&amp;rsquo;a pas que Blender dans la vie&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;comfyui-un-outil-pour-faire-des-images-avec-lia&#34;&gt;ComfyUI, un outil pour faire des images avec l&amp;rsquo;IA&lt;/h2&gt;
&lt;p&gt;Et puis je me dis que ce serait cool de faire ce &amp;ldquo;nœud&amp;rdquo; de traitement dans &lt;a href=&#34;https://www.comfy.org/&#34;&gt;ComfyUI&lt;/a&gt;, c&amp;rsquo;est un
outil qui permet de créer des images avec des modèles (de l&amp;rsquo;IA en veux-tu), et qui permet de faire des &amp;ldquo;flux&amp;rdquo; (workflow).&lt;/p&gt;
&lt;p&gt;Sauf que ComfyUI n&amp;rsquo;a pas de nœud pour manipuler une courbe. Cela ne veut pas dire qu&amp;rsquo;on ne peut pas le faire, mais il
va falloir changer de méthode. Le plus simple, pour moi, développeur, c&amp;rsquo;est de créer mon propre nœud en Python.&lt;/p&gt;
&lt;p&gt;Mais, si je ne peux pas faire un éditeur de courbe comme sur Blender, quel outil me reste-t-il ?&lt;/p&gt;
&lt;p&gt;La réponse&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les maths !&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;holla-avant-de-lire-la-suite&#34;&gt;Holla, avant de lire la suite&lt;/h2&gt;
&lt;p&gt;L&amp;rsquo;idée de cet article, ce n&amp;rsquo;est pas de faire le kéké en math. Ce que je vais vous montrer, n&amp;rsquo;importe quel mathématicien
ou bon matheux vous dira que c&amp;rsquo;est &amp;ldquo;super simple&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Moi, je suis conscient que ce n&amp;rsquo;est pas évident pour tout le monde.&lt;/p&gt;
&lt;p&gt;Mon but, là, ce n&amp;rsquo;est pas de vous faire peur ou de vous faire sentir comme étant idiot parce que ce que vous ne comprenez
pas le principe que je vais montrer. C&amp;rsquo;est tout l&amp;rsquo;inverse !&lt;/p&gt;
&lt;p&gt;En fait, je vais vous demander, en lisant ces lignes, de partir du principe que ça va être assez simple.&lt;/p&gt;
&lt;p&gt;Vous allez voir que je ne cherche pas à expliquer des trucs difficiles en math, mais au contraire de passer outre des
concepts et ne s&amp;rsquo;intéresser qu&amp;rsquo;à ce qui va nous servir.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est exactement le but. Vous allez voir qu&amp;rsquo;on va se foutre royalement de ce que fait telle fonction ou telle opération.
On va juste se servir des outils et y aller à l&amp;rsquo;arrache.&lt;/p&gt;
&lt;p&gt;Et donc, au final, je vous dirai un truc sur les math en conclusion qui va vous &amp;ldquo;étonner&amp;rdquo;&amp;hellip; mais lisez d&amp;rsquo;abord le boxon
qui suit &amp;#x1f604;&lt;/p&gt;
&lt;h2 id=&#34;la-courbe-en-s&#34;&gt;La courbe en &amp;ldquo;S&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;Ce que j&amp;rsquo;essaie de faire, c&amp;rsquo;est d&amp;rsquo;arriver à reproduire un minimum de choses que je
fais avec des machins à la souris, mais en calcul pur. Alors, autant la formule pour la partie de traitement appelée
&amp;ldquo;overlay&amp;rdquo; est chiante à mourir, autant la courbe en &amp;ldquo;S&amp;rdquo; est assez sympa à manipuler.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si si, je vous assure que c&amp;rsquo;est sympa, mais il va falloir accepter un truc : ne pas avoir peur des symboles
mathématiques, des noms de fonction ou encore de l&amp;rsquo;écriture.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Les courbes en &amp;ldquo;S&amp;rdquo;, il en existe des tas qui sont définies en math. On les appelle comme ça parce que leur forme
ressemble à un &amp;ldquo;S&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Sans être un gros matheux, si votre mémoire est bonne (mais je vais vous la rafraichir), il existe une courbe qu&amp;rsquo;on a déjà
vue au lycée qui a cette forme de &amp;ldquo;S&amp;rdquo;. La fonction &amp;ldquo;tangente hyperbolique&amp;rdquo;.&lt;/p&gt;
$$tanh(x)$$&lt;blockquote&gt;
&lt;p&gt;Alors, on va être clair, on se fout de savoir d&amp;rsquo;où elle vient, ce qu&amp;rsquo;elle fait dans la vie, le nom de son chat etc. Ce
qui nous intéresse, c&amp;rsquo;est la forme qu&amp;rsquo;elle a.&lt;/p&gt;&lt;/blockquote&gt;
&lt;iframe src=&#34;https://www.desmos.com/calculator/847wfsufdi?embed&#34; width=&#34;500&#34; height=&#34;500&#34; style=&#34;border: 1px solid #ccc&#34; frameborder=0&gt;&lt;/iframe&gt;
&lt;p&gt;Voilà, ça ressemble à un &amp;ldquo;S&amp;rdquo; et c&amp;rsquo;est ce qu&amp;rsquo;on veut.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et là j&amp;rsquo;insiste, c&amp;rsquo;est ce qu&amp;rsquo;on veut, POINT. On sait que cette fonction a, à peu près, la forme qu&amp;rsquo;on veut, et c&amp;rsquo;est
tout ce dont on a besoin pour le moment.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Dans Blender, on dessinait cette courbe à la main, avouez que ça ressemble à la fonction mathématique \(tanh\) :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/effet-bleach-bypass-les-maths-cest-cool/curve-node.png&#34; alt=&#34;Le node &amp;ldquo;curve&amp;rdquo; de Blender&#34;&gt;&lt;/p&gt;
&lt;p&gt;Bon par contre y&amp;rsquo;a un ou deux trucx qui ne vont pas&amp;hellip;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;De manière pragmatique, la courbe dans Blender, elle part de 0 et elle va vers 1, alors que la tangente hyperbolique,
elle pond aussi  des valeurs négatives et elle s&amp;rsquo;étire pratiquement vers &amp;ldquo;2&amp;rdquo;. Ça, c&amp;rsquo;est pas cool pour nous.&lt;/li&gt;
&lt;li&gt;Et puis, dans Blender, je peux vraiment bouger la courbe comme je veux, la rendre plus plate, plus tendue, forcer les
valeurs hautes, etc. alors que là, bah j&amp;rsquo;ai juste une fonction qui fait un &amp;ldquo;S&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En gros, je veux ça, dans le carré vert, on se fiche de ce qui dépasse :&lt;/p&gt;
&lt;iframe src=&#34;https://www.desmos.com/calculator/krqiioz77d?embed&#34; width=&#34;500&#34; height=&#34;500&#34; style=&#34;border: 1px solid #ccc&#34; frameborder=0&gt;&lt;/iframe&gt;
&lt;p&gt;Vous devez bien capter ce que vous voyez : là, la courbe, elle passe par \(0,0\) et \(1,1\), c&amp;rsquo;est à dire les coins du
carré vert. &lt;strong&gt;C&amp;rsquo;est hyper important&lt;/strong&gt;, il faut que la courbe passe par ces deux points. sinon, on va perdre plein de pixels
de notre image quand on va la traiter.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sploier alert : on va y arriver.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;C&amp;rsquo;est à ce moment qu&amp;rsquo;il faut prendre son courage à deux mains, et se dire que bon, OK c&amp;rsquo;est pas complètement bon, mais
on doit pouvoir &amp;ldquo;modifier&amp;rdquo; cette fonction \(tanh(x)\) pour qu&amp;rsquo;elle colle au besoin.&lt;/p&gt;
&lt;h2 id=&#34;la-formule-magique-cest-pas-sil-te-plait-cest-des-maths&#34;&gt;La formule magique (c&amp;rsquo;est pas &amp;ldquo;s&amp;rsquo;il te plait&amp;rdquo;&amp;quot;, c&amp;rsquo;est des maths)&lt;/h2&gt;
&lt;p&gt;Alors, il faut soit se souvenir des cours de maths du lycée, soit aller poser la question à des gens, des IA, potes, ce
que vous voulez.&lt;/p&gt;
&lt;p&gt;Mais je vous donne la réponse, elle se résume en cette formule :&lt;/p&gt;
$$k*(x-b)+c$$&lt;p&gt;En fait, dans cette formule qui semble barbare, seules \(x\) est variable, c&amp;rsquo;est la valeur d&amp;rsquo;un pixel, la note des
élèves, un niveau sonore, votre salaire mensuel dans votre tableau Excel&amp;hellip;&lt;/p&gt;
&lt;p&gt;Les trois autres, c&amp;rsquo;est nous qui choisissons les valeurs, au besoin, selon la situation.&lt;/p&gt;
&lt;p&gt;Il faut se souvenir que :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;\(k\) change la pente d&amp;rsquo;une courbe&lt;/li&gt;
&lt;li&gt;\(b\) déplace la courbe à droite et à gauche (on appelle ça un &amp;ldquo;offset horizontal)&amp;rdquo;&lt;/li&gt;
&lt;li&gt;\(c\) déplace la courbe de haut en bas (on appelle ça un &amp;ldquo;offset vertical&amp;rdquo;)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est le truc le plus compliqué de l&amp;rsquo;article&amp;hellip; vous notez cette formule quelque part, vous le gardez, c&amp;rsquo;est vraiment un
truc à retenir. Je m&amp;rsquo;en sers plus souvent que vous ne pouvez le croire, pour travailler sur un tableur (pas Excel),
quand je dois analyser des données, en imagerie, partout je vous dis.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;On n&amp;rsquo;a pas besoin de bouger la courbe verticalement, donc on va virer \(c\) de notre formule pour le reste de
l&amp;rsquo;article.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Et ça marche avec n&amp;rsquo;importe quoi. Ces règles, bien appliquées, vous pouvez les utiliser sur toutes les fonctions
qui prennent &amp;ldquo;x&amp;rdquo; en entrée. Vous choisissez des valeurs &amp;ldquo;k&amp;rdquo; et &amp;ldquo;b&amp;rdquo; (appelez les comme vous le voulez hein), et ça
fonctionne.&lt;/p&gt;
&lt;p&gt;La preuve en image interactive (réglez k et b pour voir):&lt;/p&gt;
&lt;iframe src=&#34;https://www.desmos.com/calculator/u8itwcj9co&#34; style=&#34;width: 100%; height: 550px&#34; frameborder=0&gt;&lt;/iframe&gt;
&lt;blockquote&gt;
&lt;p&gt;Par contre, faut pas se planter, je dois bien multiplier &lt;strong&gt;l&amp;rsquo;ensemble&lt;/strong&gt; &amp;ldquo;\(x+b\)&amp;rdquo; par &amp;ldquo;\(k\)&amp;rdquo;, les parenthèses sont
importantes :&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
$$k*(x+b)$$&lt;/blockquote&gt;
&lt;p&gt;Mais vraiment, au pire, gardez juste la règle &amp;ldquo;\(k(x+b)\)&amp;rdquo; en tête et appliquez là au besoin. Par exemple, si je vous
propose une fonction \(P_{rout}(x)\) qui fait un superbe prout mathématique, vous pourrez la transformer en
\(P_{rout}(k(x+b))\), et donc l&amp;rsquo;écraser et la décaler sous le nez du voisin.&lt;/p&gt;
&lt;h2 id=&#34;on-applique&#34;&gt;On applique&lt;/h2&gt;
&lt;p&gt;Donc, je prends \(tanh(x)\) et je lui ajoute mes valeurs \(k\) et \(b\) pour faire les transformations que je veux :&lt;/p&gt;
$$S(x) = tanh(k.(x+b))$$&lt;p&gt;C&amp;rsquo;est tout&amp;hellip; J&amp;rsquo;ai pas tortillé 2 ans, j&amp;rsquo;ai juste appliqué la formule magique.&lt;/p&gt;
&lt;p&gt;Avec \(k\) et \(b\) des valeurs que je vais choisir pour avoir la forme que je veux. \(k\) modifie la pente, et \(b\)
le décalage à droite et à gauche.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je sais, donc, changer la &amp;ldquo;force&amp;rdquo; de la courbe (sa pente), et je sais la déplacer&amp;hellip; mais dans le mauvais sens
crénondedjiou.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et oui, si \(b\) vaut, par exemple, \(0,5\) alors la courbe se déplace à gauche. Grrrrrr. Pas content.&lt;/p&gt;
&lt;p&gt;On va donc soustraire au lieu d&amp;rsquo;additionner, ce sera plus naturel :&lt;/p&gt;
$$S(x) = tanh(k.(x-b))$$&lt;p&gt;Voilà !&lt;/p&gt;
&lt;p&gt;Bon, en animant \(k\) et \(b\) (merci à &lt;a href=&#34;https://www.desmos.com/calculator&#34;&gt;Desmos&lt;/a&gt;, ce site est génial),
on peut voir comment agissent les valeurs sur la courbe (la droite en bleu c&amp;rsquo;est une représentation de la pente qui est
modifiée par &amp;ldquo;k&amp;rdquo;, et le point &amp;ldquo;b&amp;rdquo; c&amp;rsquo;est pour vous indiquer le décalage) :&lt;/p&gt;
&lt;iframe src=&#34;https://www.desmos.com/calculator/njtomwpex9&#34; style=&#34;width: 100%; height: 500px; border: 1px solid #ccc&#34;
  frameborder=0&gt;&lt;/iframe&gt;
&lt;blockquote&gt;
&lt;p&gt;Posez vous deux secondes, je vous résume ça.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;On n&amp;rsquo;a pas fait un truc de fou, je vous assure. Au pire acceptez simplement le principe :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;j&amp;rsquo;ai pris une fonction qui a la tête d&amp;rsquo;un &amp;ldquo;S&amp;rdquo; parce que c&amp;rsquo;est ce que je veux&lt;/li&gt;
&lt;li&gt;j&amp;rsquo;ai fait en sorte de pouvoir faire glisser la courbe à gauche ou à droite en ajoutant une valeur &amp;ldquo;b&amp;rdquo;&lt;/li&gt;
&lt;li&gt;j&amp;rsquo;ai fait en sorte de pouvoir l&amp;rsquo;écraser, simplement en multipliant la valeur &amp;ldquo;x&amp;rdquo; par une valeur que j&amp;rsquo;appelle &amp;ldquo;k&amp;rdquo;&lt;/li&gt;
&lt;li&gt;c&amp;rsquo;est assez simple : on multiplie pour accélérer, on ajoute pour avoir de l&amp;rsquo;élan&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clairement, on est pas mal là. Sauf qu&amp;rsquo;on voit un petit souci. Oh, rien de bien méchant&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Là, on a juste trouvé comment changer la pente et la position de la courbe. Mais on est toujours pas dans le carré
vert, c&amp;rsquo;est-à-dire qu&amp;rsquo;on ne sait pas encore utiliser seulement des valeurs entre 0 et 1, et retourner des valeurs qui
doivent passer par 0 et 1 en y.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ce que je vous dis, clairement, c&amp;rsquo;est que la courbe en \(x=1\) ne donne pas 1. Et en 0, on a des valeurs négatives (la
courbe passe à gauche, et est en dessous de 0) alors qu&amp;rsquo;on doit retourner 0.&lt;/p&gt;
&lt;p&gt;Il va falloir utiliser une opération connue en informatique : &lt;strong&gt;la normalisation&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;normalisation-de-la-courbe&#34;&gt;Normalisation de la courbe&lt;/h2&gt;
&lt;p&gt;Une normalisation, c&amp;rsquo;est une opération qui vise à transformer des valeurs dans un intervalle donné entre deux autres
valeurs choisies.&lt;/p&gt;
&lt;p&gt;En français, notre fonction balance des valeurs entre -1 et 1, on veut qu&amp;rsquo;elle se place entre 0 et 1.&lt;/p&gt;
&lt;p&gt;Sachant que nous allons lui envoyer, en entrée, que des valeurs entre 0 et 1, parce qu&amp;rsquo;une image ça marche comme ça et
puis c&amp;rsquo;est tout, on n&amp;rsquo;a pas à s&amp;rsquo;occuper de ce qui est en dehors de cet intervalle.&lt;/p&gt;
&lt;p&gt;Alors, je ne vais pas passer par 42 chemins, une normalisation ça se calcule comme ça :&lt;/p&gt;
$$\frac{x - m_{inimum}}{m_{aximum}-m_{inimum}}$$&lt;p&gt;Et nous, comme on tape entre 0 et 1, on va prendre les valeurs \(S(0)\) et \(S(1)\) pour le minimum et le maximum.&lt;/p&gt;
&lt;p&gt;Donc, prenez votre respiration :&lt;/p&gt;
$$\frac{S(x) - S(0)}{S(1) - S(0)}$$&lt;p&gt;Et &lt;strong&gt;ça, c&amp;rsquo;est notre courbe en S pour changer la lumière et les ombres d&amp;rsquo;une image&lt;/strong&gt;. On pourra paramétrer la force avec
\(k\) et corriger la dose d&amp;rsquo;ombres et de lumière avec \(b\).&lt;/p&gt;
&lt;p&gt;Voilà, on y est. En modifiant \(k\) et \(b\) on arrive à avoir à peu près tous les types de réglages que l&amp;rsquo;on veut pour
faire adapter l&amp;rsquo;image et passer à la suite.&lt;/p&gt;
&lt;iframe src=&#34;https://www.desmos.com/calculator/1gjpbj3ulk&#34;
  style=&#34;width: 100%; height: 500px; border: 1px solid #ccc&#34;
  frameborder=0&gt;&lt;/iframe&gt;
&lt;blockquote&gt;
&lt;p&gt;Je veux juste qu&amp;rsquo;on soit bien clairs entre nous. La fonction qu&amp;rsquo;on a créée, elle a deux valeurs qui nous servent de
réglage. \(k\) et \(b\). Alors que \(x\) sera la valeur de chaque pixel de l&amp;rsquo;image, en réalité chaque couleur de
chaque pixel de l&amp;rsquo;image. De mon côté, j&amp;rsquo;utilise cette fonction en Python, mais en vérité, vous pouvez la &amp;ldquo;coder&amp;rdquo; avec
Blender (avec les nœuds), l&amp;rsquo;utiliser dans Excel ou OnlyOffice, avec autre chose que \(tanh\).&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;le-overlay&#34;&gt;Le &amp;ldquo;overlay&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;Ce sera moins compliqué, on va juste appliquer la formule pour faire ce qu&amp;rsquo;on appelle un &amp;ldquo;Overlay&amp;rdquo;. En gros, ce que fait
cette opération, surtout utilisée pour le traitement d&amp;rsquo;image, c&amp;rsquo;est de prendre deux images : une &amp;ldquo;base&amp;rdquo; et une &amp;ldquo;couche
supérieure&amp;rdquo;. Là où la base est sombre, la couche supérieure doit devenir plus sombre, et là où la base est claire, la
couche supérieure doit l&amp;rsquo;être encore plus.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est une opération qui vise, donc, à utiliser une image comme référence d&amp;rsquo;ombres et lumières pour régler la seconde (la
sortie).&lt;/p&gt;
&lt;p&gt;La formule est décrite dans la page Wikipedia &lt;a href=&#34;https://en.wikipedia.org/wiki/Blend_modes#Overlay&#34;&gt;https://en.wikipedia.org/wiki/Blend_modes#Overlay&lt;/a&gt; et elle utilise une
condition.&lt;/p&gt;
&lt;p&gt;On prend \(a\) et \(b\), avec \(a\) étant la base, et \(b\) la couche supérieure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Si \(a\) est inférieur à 0,5, alors on fait &amp;ldquo;\(2 *a* b\)&amp;rdquo;&amp;quot;,&lt;/li&gt;
&lt;li&gt;sinon on doit faire \(1-2(1-a)(1-b)\)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En écriture plus &amp;ldquo;matheuse&amp;rdquo; on écrit :&lt;/p&gt;
$$
\begin{align}
O_{verlay}(a,b) = \left\{
\begin{array}{cl}
2ab &amp; \text{si } a &lt; 0,5 \\
1-2(1-a)(1-b) &amp; \text{si } a\ge0,5
\end{array}
\right.
\end{align}
$$&lt;p&gt;Il faut juste savoir une chose ici, \(a\) et \(b\), ce sont les valeurs qu&amp;rsquo;on va prendre dans la tronche, les couleurs
de chaque pixel, les une après les autres. Mais en Python, avec &lt;code&gt;numpy&lt;/code&gt;, on ne se prend pas la tête. On code les
opérations comme si c&amp;rsquo;était une seule valeur, alors qu&amp;rsquo;en réalité, on reçoit l&amp;rsquo;image en entier (un tableau, de valeurs à
plein de dimensions) - c&amp;rsquo;est lui qui se débrouille. On est de sacrés fainéants, mais c&amp;rsquo;est pour ça qu&amp;rsquo;on aime Python.&lt;/p&gt;
&lt;p&gt;Ce ne sont que des multiplications, des additions et des soustractions. Je vous assure, prenez le temps de lire, c&amp;rsquo;est
relativement simple..&lt;/p&gt;
&lt;h2 id=&#34;on-passe-à-python&#34;&gt;On passe à Python&lt;/h2&gt;
&lt;p&gt;Je ne vous donne pas le code complet du &amp;ldquo;nœud&amp;rdquo; de ComfyUI que je développe, parce qu&amp;rsquo;il y a tout un pan de qui
sert à la présentation dans l&amp;rsquo;interface, la gestion des entrées etc.&lt;/p&gt;
&lt;p&gt;Avant de lire, sachez que je vais utiliser &lt;code&gt;numpy&lt;/code&gt;, puisqu&amp;rsquo;il simplifie grandement les algorithmes à développer. En
gros, au lieu de faire le calcul moi-même, pixel par pixel, couleur par couleur, il va le faire pour moi&amp;hellip; Numpy gère
les matrices sans qu&amp;rsquo;on ait à se battre avec les boucles.&lt;/p&gt;
&lt;p&gt;La fonction de base, c&amp;rsquo;est le calcul \(tanh(k.(x-b))\) :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-Python&#34;&gt;import numpy as np

def T(x:np.ndarray, k: float, b: float) -&amp;gt; np.ndarray:
  return np.tanh(k*(x-b))

# en mode dégueux on ne donnerait pas les &amp;quot;tupes&amp;quot; attendus
def T(x, k, b):
  return np.tanh(k*(x-b))

# ça marche aussi, mais c&#39;est infâme à utiliser dans les éditeurs de code... pensez à
# indiquer les types attendus, ça aide grandement
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;ldquo;x&amp;rdquo;, ici, c&amp;rsquo;est l&amp;rsquo;image sous formel de tableau de valeurs. Quand je vous dis que c&amp;rsquo;est simple avec &lt;code&gt;numpy&lt;/code&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Alors, oui, je me balade les valeurs &amp;ldquo;k&amp;rdquo; et &amp;ldquo;b&amp;rdquo; partout, parce que je ne veux pas les mettre en &amp;ldquo;valeur globale&amp;rdquo;. C&amp;rsquo;est
une habitude quand on développe, mais sachez que vous auriez pu faire :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-Python&#34;&gt;# avant de créer la fonction
k=3
b=0.5

# et en gros:
def T(x):
  return np.tanh(k*(x-b))

# c&#39;est simple à lire, mais encore une fois c&#39;est pas pro ça
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bon, pour la normalisation, on a plein de manière de faire, mais le calcul &amp;ldquo;manuel&amp;rdquo; est plus clair :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-Python&#34;&gt;def Tnorm(x:np.ndarray, k:float = 3.5, b:float = .5) -&amp;gt; np.ndarray:
  return ( T(x, k, b) - T(0, k, b) ) / (T(1, k, b) - T(0, k, b))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On a notre effet&amp;hellip; ou presque.&lt;/p&gt;
&lt;p&gt;Alors, encore une fois, je vous passe les détails, mais en fait, il va manquer deux trois trucs pour que ça marche.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;il faut savoir ouvrir une image, notamment en utilisant le package &lt;code&gt;PIL&lt;/code&gt; (Pillow)&lt;/li&gt;
&lt;li&gt;en réalité, on doit désaturer (réduire les couleurs, en gros) l&amp;rsquo;image avant de la passer dans la courbe en &amp;ldquo;S&amp;rdquo;&lt;/li&gt;
&lt;li&gt;on doit avoir un facteur pour régler à quel point on prend l&amp;rsquo;une ou l&amp;rsquo;autre image dans le &amp;ldquo;overlay&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bref, pour vous aider à comprendre, j&amp;rsquo;ai créé un notebook, cliquez sur ce bouton pour voir le code et le résultat :&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://colab.research.google.com/drive/13leIGGExb0E00vxQF660CBG2purEnD13?usp=sharing&#34;&gt;&lt;img src=&#34;https://colab.research.google.com/assets/colab-badge.svg&#34; alt=&#34;Ouvrir dans Google Colab&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Et le résultat final comparé à l&amp;rsquo;article de référence que j&amp;rsquo;ai utilisé :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/effet-bleach-bypass-les-maths-cest-cool/result.png&#34; alt=&#34;Résultat de notre effet mathématique Bleach Bypass&#34;&gt;&lt;/p&gt;
&lt;p&gt;Merci à Tristan Kneschke pour son excellent article traitant de cet effet photo pour le logiciel &amp;ldquo;DaVinci Resolve&amp;rdquo;. Son
travail m&amp;rsquo;a servi de référence pour Blender et pour le code que j&amp;rsquo;ai produit.&lt;/p&gt;
&lt;p&gt;Son article est ici : &lt;a href=&#34;https://www.premiumbeat.com/blog/color-grading-bleach-bypass-look-in-davinci-resolve/&#34;&gt;https://www.premiumbeat.com/blog/color-grading-bleach-bypass-look-in-davinci-resolve/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;et-donc-avec-comfyui-&#34;&gt;Et donc, avec ComfyUI ?&lt;/h2&gt;
&lt;p&gt;Et bien j&amp;rsquo;ai créé un nœud qui utilise le code que je vous présente dans le notebook. Y&amp;rsquo;a pas mal de code pour la
gestion des &amp;ldquo;tenseurs&amp;rdquo; (et oui, faut pas déconner non plus, tout n&amp;rsquo;est pas si simple). Mais il marche pas mal du tout !&lt;/p&gt;
&lt;p&gt;Le projet est en cours de &amp;ldquo;push&amp;rdquo;, sur &lt;a href=&#34;https://github.com/metal3d/ComfyUI_M3D_photo_effects&#34;&gt;ma page github&lt;/a&gt; -
patience&amp;hellip; je dois nettoyer le code, documenter, tout ça&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/effet-bleach-bypass-les-maths-cest-cool/comfy-bleach-bypass.png&#34; alt=&#34;Mon propre nœud ComfyUI&#34;&gt;&lt;/p&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas grand-chose, mais ça le fait.&lt;/p&gt;
&lt;p&gt;Et ça marche, évidemment, avec les images générée par l&amp;rsquo;IA&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/effet-bleach-bypass-les-maths-cest-cool/comfyui2.png&#34; alt=&#34;ComfyUI - image généré + bleach bypass&#34;&gt;&lt;/p&gt;
&lt;p&gt;Le truc étonnant, c&amp;rsquo;est cette récupération d&amp;rsquo;ombre en fond d&amp;rsquo;image, invisible dans l&amp;rsquo;image générée. Cet effet est très
étonnant.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2025/effet-bleach-bypass-les-maths-cest-cool/clown.png&#34; alt=&#34;Les ombres remontent&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;les-maths-cest-cool-vraiment&#34;&gt;Les maths, c&amp;rsquo;est cool, vraiment&lt;/h2&gt;
&lt;p&gt;Il se peut que vous n&amp;rsquo;ayez vraiment pas capté ce que j&amp;rsquo;ai raconté. J&amp;rsquo;en suis désolé et non ça ne fait pas de vous un
idiot ou une personne en dessous des autres. Certaines personnes calent sur les maths, d&amp;rsquo;autres, c&amp;rsquo;est sur la
psychologie, et d&amp;rsquo;autres sur la mécanique.&lt;/p&gt;
&lt;p&gt;Ce que j&amp;rsquo;ai tenté de vous montrer, c&amp;rsquo;est qu&amp;rsquo;il n&amp;rsquo;est pas nécessaire d&amp;rsquo;avoir un bagage de malade pour faire des trucs en
math. Croyez-le ou non, je n&amp;rsquo;ai utilisé ici que des multiplications et des additions. Inutile de comprendre ce qu&amp;rsquo;est
une tangente hyperbolique, ou de savoir pourquoi la formule de normalisation est comme ça. Si vous avez juste une idée
de la forme d&amp;rsquo;une fonction, ou de ce qu&amp;rsquo;elle peut donner comme résultat, alors vous pouvez les utiliser comme des
outils.&lt;/p&gt;
&lt;p&gt;Je ne sais pas comment fonctionne mon réseau de flotte dans la maison, pourtant je sais me servir d&amp;rsquo;un robinet. Et bah
en math, on peut faire pareil. On peut utiliser une fonction sinus ou une normalisation pour sortir un résultat sans pour
autant avoir toute la théorie qui mène à leur création.&lt;/p&gt;
&lt;p&gt;Dans mon métier, je dois manipuler des fonctions Sigmoïde, tanh, des normalisations bizarres et des vecteurs à 1000
dimensions. Je vous assure que, dans le quotidien, je ne les utilise que pour sortir des résultats, je ne les étudie pas
et je ne pourrais d&amp;rsquo;ailleurs pas toutes les expliquer. Je sais à quoi ça sert et comment les manipuler.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Il ne faut, au final, que deux choses : observer et trouver une astuce. C&amp;rsquo;est ce qu&amp;rsquo;on a fait dans cet article.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Cela ne m&amp;rsquo;a pas empêché, finalement, de me replonger dans la théorie par la suite, mais je vous assure que ce n&amp;rsquo;est pas
une obligation.&lt;/p&gt;
&lt;p&gt;Pour finir, il est évident que cet article s&amp;rsquo;adresse à ceux qui, potentiellement, ont envie d&amp;rsquo;apprendre à développer en
Python des outils pour traiter le son et l&amp;rsquo;image, ou pour les gens qui utilisent des outils qui permettent de faire des
opérations pour traiter des images (Blender, DaVinci Resolve, Gimp, Photoshop, etc.). Dans les faits, bien souvent,
l&amp;rsquo;outil vous propose un bouton pour réaliser l&amp;rsquo;effet. Mais si vous voulez aller plus loin, voire créer votre propre
effet à vous, et bah taper dans les maths, ce n&amp;rsquo;est pas déconnant.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>L&#39;aberration Des Mails Non Chiffrés</title>
      <link>https://www.metal3d.org/blog/2024/laberration-des-mails-non-chiffr%C3%A9s/</link>
      <pubDate>Sun, 10 Nov 2024 14:34:11 +0100</pubDate>
      
      <guid>https://www.metal3d.org/blog/2024/laberration-des-mails-non-chiffr%C3%A9s/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2024/laberration-des-mails-non-chiffr%C3%A9s/cover.png&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Vendredi soir, le médecin nous appelle pour un résultat d&amp;rsquo;analyse qui nécessite une prise de médicament le lendemain. Il
est 19 h 40, le médecin est vraiment sympa de prendre du temps pour nous, et nous envoie l&amp;rsquo;ordonnance par mail. Loin de
moi l&amp;rsquo;idée de lui casser du sucre sur le dos, bien au contraire, car il nous dit au téléphone &amp;ldquo;je déteste faire ça hors
messagerie sécurisée, mais là, c&amp;rsquo;est assez urgent du coup je vous l&amp;rsquo;envoie sur votre mail directement&amp;rdquo;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Soyons clairs, on a accepté sans broncher, et cela ne nous a pas dérangé plus que ça. C&amp;rsquo;est après coup que j&amp;rsquo;ai
réfléchi à cette situation aberrante.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Précision importante :&lt;/strong&gt; cet article n&amp;rsquo;est pas une critique de ce qu&amp;rsquo;a fait le médecin. Encore une fois, il l&amp;rsquo;a fait
avec notre accord et nous sommes conscients de ce que nous avons fait. Ma réflexion est générale, cela va au-delà d&amp;rsquo;un
mail d&amp;rsquo;un médecin. J&amp;rsquo;ai déjà eu cette réflexion pour des échanges avec des clients, des amis, etc. En clair, je trouve
cela ubuesque que nous ayons laissé tomber le chiffrement de nos mails. Vraiment, c&amp;rsquo;est une aberration.&lt;/p&gt;
&lt;p&gt;Laissez-moi vous expliquer ce qui était, il y a plus de 20 ans, assez commun, et qui a disparu de nos habitudes.&lt;/p&gt;
&lt;h2 id=&#34;avant&#34;&gt;Avant&lt;/h2&gt;
&lt;p&gt;Oui, avant, les &amp;ldquo;webmail&amp;rdquo; existaient déjà.&lt;/p&gt;
&lt;p&gt;Un WebMail, c&amp;rsquo;est un site web sur lequel vous pouvez lire et envoyer des mails.
Or, le web était plus &amp;ldquo;pauvre&amp;rdquo; et les navigateurs moins puissants. Du coup, on utilisait des logiciels spécifiques pour
récupérer nos mails.&lt;/p&gt;
&lt;p&gt;Si, si&amp;hellip; souvenez-vous&amp;hellip; Outlook, Thunderbird, Evolution, etc. ces noms vous parlent ? Si oui, vous avez plus de 35
ans.&lt;/p&gt;
&lt;p&gt;Ces logiciels, on les configurait pour se connecter à un serveur mail, et on récupérait nos mails. C&amp;rsquo;était simple (ça
demandait un peu de configuration, mais c&amp;rsquo;était simple). Mais surtout, ça offrait des fonctionnalités plus élaborées que
ce que proposaient les WebMail.&lt;/p&gt;
&lt;p&gt;Et notamment, le chiffrement par pair de clef, avec GPG.&lt;/p&gt;
&lt;p&gt;Je ne me souviens plus vraiment comment j&amp;rsquo;en était venu à activer ça alors que je n&amp;rsquo;étais absolument pas informaticien,
mais c&amp;rsquo;était une évidence que je voulais, parfois, envoyer des messages sans que personne d&amp;rsquo;autres que le destinataire
ne puisse les lire.&lt;/p&gt;
&lt;p&gt;Et quand j&amp;rsquo;ai vu &amp;ldquo;confidentialité&amp;rdquo; dans les options de Thunderbird, j&amp;rsquo;ai cliqué dessus. Voilà.&lt;/p&gt;
&lt;p&gt;Alors, clairement, je n&amp;rsquo;avais pas compris tout le fonctionnement, mais j&amp;rsquo;avais compris la base : si je chiffre pour un
destinataire, alors si quelqu&amp;rsquo;un au milieu essai de lire le message, il ne pourra pas.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est simple, c&amp;rsquo;est posé, c&amp;rsquo;est compréhensible.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;chiffrement-par-paire-de-clefs&#34;&gt;Chiffrement par paire de clefs&lt;/h2&gt;
&lt;p&gt;Alors, si vous voulez avoir la base de la base, même si vous n&amp;rsquo;êtes pas obligés de tout comprendre, voilà comment ça se
passe :&lt;/p&gt;
&lt;p&gt;Au début :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vous configurez une &amp;ldquo;paire de clefs&amp;rdquo; : une clef publique et une clef privée, elles sont liée entre elles,&lt;/li&gt;
&lt;li&gt;vous donnez votre clef publique à vos contacts, voire au monde entier, c&amp;rsquo;est fait pour ça,&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et ensuite :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les gens vous envoient des messages chiffrés avec votre clef publique,&lt;/li&gt;
&lt;li&gt;vous déchiffrez les messages avec votre clef privée,&lt;/li&gt;
&lt;li&gt;et c&amp;rsquo;est &amp;ldquo;automatique&amp;rdquo; : vous n&amp;rsquo;avez rien à faire, c&amp;rsquo;est transparent pour vous si vous utiliser un client mail qui
supporte GPG (genre Thunderbird)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et inversement, pour envoyer un message chiffré, vous prenez la clef publique de votre destinataire, vous chiffrez le
message avec cette clef, et vous envoyez le message.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et c&amp;rsquo;est en fait automatique. Vraiment vous n&amp;rsquo;avez pas à chercher comment faire. Vous allez voir ma capture d&amp;rsquo;écran
plus loin c&amp;rsquo;est vraiment simple.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;thunderbird-le-client-mail&#34;&gt;Thunderbird, le client mail&lt;/h2&gt;
&lt;p&gt;Thunderbird rendait cette opération super simple. Vous cliquiez sur un bouton pour générer une paire de clefs, et puis
voilà.&lt;/p&gt;
&lt;p&gt;Quand vous enoyez un message à un contact qui n&amp;rsquo;a pas encore votre clef publique, et bien Thunderbird l&amp;rsquo;injecte dans le
mail pour vous. Le destinataire n&amp;rsquo;a plus qu&amp;rsquo;à cliquer sur un bouton pour l&amp;rsquo;ajouter à sa liste de clefs
publiques. Et à partir de là, vous pouvez lui envoyer des messages chiffrés.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et c&amp;rsquo;est encore le cas aujourd&amp;rsquo;hui !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ce que je vous raconte existe depuis des années, et ça n&amp;rsquo;a jamais disparu.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/laberration-des-mails-non-chiffr%C3%A9s/thunderbird-gpg.png&#34; alt=&#34;La configuration GPG dans Thunderbird&#34;&gt;&lt;/p&gt;
&lt;p&gt;On se foutait de savoir si le mail était chez Hotmail, Gmail, Yahoo, ou chez le fournisseur de mail de Bob. On savait
que le message était chiffré, et que seul Bob pouvait le lire.&lt;/p&gt;
&lt;p&gt;Ouais, mais ça ne plaisait pas à Google, Microsoft, Yahoo, etc&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;la-suite-après-la-pub&#34;&gt;La suite après la pub&lt;/h2&gt;
&lt;p&gt;Et oui parce que, si le message est illisible par tout le monde à part Bob, comment Google peut lire le message pour
vous proposer de la pub ciblée ?&lt;/p&gt;
&lt;p&gt;Et comment il peut vous dire &amp;ldquo;hého, je sais que t&amp;rsquo;as rendez-vous chez le proctologue, je te propose une promo sur les
suppositoires&amp;rdquo; ? Comment il peut faire ça s&amp;rsquo;il ne peut pas lire le message ?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Donc, Gmail a tout simplement refusé de supporter le chiffrement par paire de clefs sur le WebMail&amp;hellip; Simple, basique.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et comme la grande majorité des gens utilisent le WebMail, et bien personne n&amp;rsquo;a accès à un petit bouton pour &amp;ldquo;chiffrer&amp;rdquo;
le message.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sauf si vous avez un plugin, mais c&amp;rsquo;est une autre histoire.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;et-alors-pour-mon-médecin-&#34;&gt;Et alors, pour mon médecin ?&lt;/h2&gt;
&lt;p&gt;Eh bien, c&amp;rsquo;est là que c&amp;rsquo;est fou. Mon médecin doit certainement payer un truc pour chiffrer des messages et nous permettre
d&amp;rsquo;y accéder - c&amp;rsquo;est potentiellement compliqué à gérer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Si le chiffrement GPG était resté une norme, il n&amp;rsquo;aurait rien eu à faire.&lt;/strong&gt; Il aurait chiffré le message pour
moi et moi seul, et je l&amp;rsquo;aurais déchiffré avec ma clef privée.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/laberration-des-mails-non-chiffr%C3%A9s/chiffer%20un%20message.png&#34; alt=&#34;Chiffrer un message, c&amp;rsquo;est un bouton&#34;&gt;&lt;/p&gt;
&lt;p&gt;Thunderbird retient les clefs publiques des contacts, donc il chiffre automatiquement avec la bonne clef.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Le tout est absolument transparent pour l&amp;rsquo;utilisateur hein ! Pas la peine d&amp;rsquo;être ingénieur.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;juste-pour-que-vous-voyez-le-résultat&#34;&gt;Juste pour que vous voyez le résultat&lt;/h2&gt;
&lt;p&gt;Je me suis envoyé un mail chiffré à moi-même.&lt;/p&gt;
&lt;p&gt;Et sur Gmail, c&amp;rsquo;est &lt;strong&gt;impossible à lire !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/laberration-des-mails-non-chiffr%C3%A9s/message-chiffre-gmail.png&#34; alt=&#34;Message chiffrée dans Gmail&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et si je tente d&amp;rsquo;ouvrir le fichier &amp;ldquo;encrypted.asc&amp;rdquo;, j&amp;rsquo;obtiens ça :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/laberration-des-mails-non-chiffr%C3%A9s/encrypted.png&#34; alt=&#34;Le contenu est chiffré, sans clef privée on ne voit rien&#34;&gt;&lt;/p&gt;
&lt;p&gt;Ce fichier, Thunderbird sait l&amp;rsquo;utiliser comme un grand, il prend ma clef privée et le déchiffre. Je n&amp;rsquo;ai rien à faire
moi-même, c&amp;rsquo;est &lt;strong&gt;automatique&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ce que je veux dire, c&amp;rsquo;est que sur Thunderbird, je vois mon message comme n&amp;rsquo;importe quel autre message !&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/laberration-des-mails-non-chiffr%C3%A9s/message-chiffre.png&#34; alt=&#34;Message déchiffré dans Thunderbird&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vous remarquez, en haut à droite, l&amp;rsquo;icône &amp;ldquo;OpenGPG&amp;rdquo; qui indique que le message est sûr et bien chiffré.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;et-les-autres-&#34;&gt;Et les autres ?&lt;/h2&gt;
&lt;p&gt;C&amp;rsquo;est vrai, on ne passe pas son temps à envoyer des secrets à nos contacts. N&amp;rsquo;est-ce pas ?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En fait&amp;hellip; si. En soit, tout doit être un secret.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Si vous vous posez deux secondes sur la
question, vous allez vous rendre compte que tous les messages que vous envoyez sont des secrets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;D&amp;rsquo;abords, au niveau professionnel.&lt;/strong&gt; Combien de mails recevez-vous avec des informations sensibles ? Je ne parle pas
nécessairement de mots de passes, mais des données qui parlent de l&amp;rsquo;entreprise, d&amp;rsquo;un projet dont on n&amp;rsquo;aimerait pas
qu&amp;rsquo;une
boite d&amp;rsquo;informatique nous pique les idées. Et c&amp;rsquo;est là que ça fait peur, c&amp;rsquo;est que beaucoup de nos clients utilisent
&lt;strong&gt;Office 365&lt;/strong&gt;, avec un Outlook en ligne, avec &lt;strong&gt;Microsoft&lt;/strong&gt; qui peut analyser le contenu des messages.&lt;/p&gt;
&lt;p&gt;Encore une fois, la faute à qui ? Quand une entreprise comme Microsoft vous promet monts et merveilles avec leurs outils,
et un Copilot qui lit tout ce qui se passe, on ne peut pas s&amp;rsquo;étonner que les gens utilisent ces outils. Et donc, on ne
chiffre rien. Et Microsoft est content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Et puis, il y a les mails personnels.&lt;/strong&gt; Combien de fois avez-vous envoyé des photos de vos enfants, de votre famille,
que Google se délecte pour (on ne sait pas hein) entrainer son IA, analyser le contenu pour afficher de la pub, etc. Et
si ces données fuitaient ? Si, comme beaucoup trop de gens, vos mots de passes étaient faibles ? Et bien vous mettez en
danger vos photos, et les photos qu&amp;rsquo;on vous envoie.&lt;/p&gt;
&lt;h2 id=&#34;et-ce-nest-pas-que-pour-les-mails&#34;&gt;Et ce n&amp;rsquo;est pas que pour les mails&lt;/h2&gt;
&lt;p&gt;Comme je le disais, GPG est utilisé pour chiffrer des fichiers, des messages, des connexions, etc. C&amp;rsquo;est un
outil génial qui est toujours dans les outils des informaticiens, mais qui a été abandonné par le grand public.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est bête, c&amp;rsquo;est dommage.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;GPG, avec une simple ligne de commande, ou avec des outils à la souris, c&amp;rsquo;est un truc vraiment intelligent et efficace.
Pour vous donner une idée des utilisations que j&amp;rsquo;en ai:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;J&amp;rsquo;ai fourni ma clef à un client pour qu&amp;rsquo;il chiffre un mot de passe à me donner pour me connecter à une base de données&lt;/li&gt;
&lt;li&gt;signature des logiciels que je publie afin d&amp;rsquo;assurer que ce que les gens téléchargent ne peuvent provenir que de moi
(si le test de signature échoue, c&amp;rsquo;est que le fichier a été modifié par quelqu&amp;rsquo;un d&amp;rsquo;autre)&lt;/li&gt;
&lt;li&gt;chiffrement de fichiers sensibles que je stocke sur le cloud (oui, je stocke des trucs sur le cloud, mais chiffrés)&lt;/li&gt;
&lt;li&gt;chiffrement de mails, bien entendu&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Installez un client mail, utilisez le chiffrement en créant une paire de clefs (franchement, suivez juste le guide dans
la configuration de Thunderbird, c&amp;rsquo;est simple), et envoyez vos mails chiffrés.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Si tout le monde arrêtait d&amp;rsquo;utiliser des WebMail, et utilisait des clients mail qui supportent le chiffrement, on
réglerait un nombre incalculable de problèmes.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fini les messageries sécurisées pour chaque service, difficile à lire, à utiliser sur le net, et à maintenir. On
pourrait simplement envoyer un mail à la CAF, la CPAM, la banque, etc.&lt;/li&gt;
&lt;li&gt;fini d&amp;rsquo;avoir des pubs au milieu des messages, et de se demander pourquoi Google affiche des pubs à propos d&amp;rsquo;un
cadeau dont on parle en famille par mail pour un anniversaire,&lt;/li&gt;
&lt;li&gt;fini de se poser la question &amp;ldquo;comment je t&amp;rsquo;envoie le mot de passe ?&amp;rdquo;&lt;/li&gt;
&lt;li&gt;fini de se demander si le message provient bien de la personne indiquée, genre si j&amp;rsquo;ai la signature GPG de mon
banquier, personne ne peut se faire passer pour lui,&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GPG est libre, très utilisé dans le monde informatique (par exemple, on signe nos travaux Git, s&amp;rsquo;en sert pour
valider des dépôts de logiciels, etc.), et très simple à utiliser.&lt;/p&gt;
&lt;p&gt;GPG, ça sert à chiffrer et à signer. Je ne vais pas tout expliquer dans cet article déjà un peu trop long, mais vous
pouvez :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;envoyer un message en clair, mais assurer que c&amp;rsquo;est vous et personne d&amp;rsquo;autre qui a bien écrit cela&lt;/li&gt;
&lt;li&gt;envoyer un message chiffré, que seul le destinataire pourra libre&lt;/li&gt;
&lt;li&gt;ou ne rien faire, et envoyer un message en clair&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est inclut par défaut sur Linux, c&amp;rsquo;est intégré à Thunderbird pour Windows, Linux, Mac, mobile, et ça marche du feu de
dieu.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Non, vous n&amp;rsquo;avez pas besoin d&amp;rsquo;être informaticien pour chiffrer vos mails, installez Thunderbird et aller dans les
paramètres de votre compte, dans &amp;ldquo;confidentialité&amp;rdquo;, et cliquez sur &amp;ldquo;Gérer les clefs&amp;rdquo;. POINT !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Il vous suffit d&amp;rsquo;installer Thunderbird, de cliquer sur un bouton pour générer une paire de clefs, et c&amp;rsquo;est tout bon
sang&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et il existe bien entendu d&amp;rsquo;autres clients mail que Thunderbird. C&amp;rsquo;est juste que c&amp;rsquo;est, à ma connaissance, le seul qui
ait une intégration aussi simple de GPG, qui répond à 99% des besoins et qui soit largement documenté.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>J&#39;ai créé une IA qui analyse un débat politique à la télévision</title>
      <link>https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/</link>
      <pubDate>Thu, 27 Jun 2024 15:18:18 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;&lt;strong&gt;Et cerise confite sur le gâteau au chocolat noir, je vais tout faire avec des outils open-source.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est important, c&amp;rsquo;est très important… Nous allons voter dimanche 30 juin pour les législatives.
Il y a trois grands groupes politiques qui sont très présents et il se trouve qu&amp;rsquo;un débat a eu lieu, et qu&amp;rsquo;on
retrouve &lt;a href=&#34;https://www.tf1info.fr/elections/en-direct-legislatives-suivez-sur-tf1-le-premier-debat-televise-entre-attal-bardella-et-bompard-2305539.html&#34;&gt;le replay sur TF1&lt;/a&gt;, où plusieurs sujets ont été abordés.&lt;/p&gt;
&lt;p&gt;Ce que je voulais savoir, par cette approche, c&amp;rsquo;est comment, à de partir d&amp;rsquo;une donnée très compliquée à analyser,
à savoir une vidéo (disons plutôt le son de la vidéo), nous pourrions proposer un outil qui analyse et synthétise le
contenu. L&amp;rsquo;idée étant de pouvoir extraire des propos, les vérifier, résumer, ou même simplifier ce qui est dit.&lt;/p&gt;
&lt;p&gt;Vous pourrez adapter les questions à votre guise, et même poser des questions plus complexes. Mais il va falloir passer
de la vidéo à quelque chose de compréhensible par une machine.&lt;/p&gt;
&lt;p&gt;Pour cela, il y a quelques étapes à suivre. Je vais donc tenter de vous expliquer, pas à pas, comment j&amp;rsquo;ai procédé. Je
vais vous expliquer des concepts en les &lt;strong&gt;vulgarisant&lt;/strong&gt; autant que je peux. Car si l&amp;rsquo;on se penche un peu plus dans les
détails cela peut paraitre abstrait, imbitable, et cela peut même décourager.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;espère que je vais réussir à vous expliquer tout ça de manière simple et claire. Même si certains concepts ne sont
clairement pas évidents, j&amp;rsquo;ai à cœur de vous démontrer que c&amp;rsquo;est finalement assez accessible. Et si les blocs de code
vous rebutent, je vous invite à ne vous pencher que sur les concepts.&lt;/p&gt;
&lt;p&gt;Bref, j&amp;rsquo;espère que je vais être utile.&lt;/p&gt;
&lt;h2 id=&#34;mais-avant-tout-je-dois-impérativement-vous-mettre-en-garde&#34;&gt;Mais avant tout, je dois &lt;strong&gt;impérativement vous mettre en garde&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Ce que je vous propose ici est avant tout une approche didactique à des fins de recherches et de tests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;En aucun cas&lt;/strong&gt; une
intelligence artificielle ne doit vous orienter votre vote.&lt;/p&gt;
&lt;p&gt;Les débats politiques sont complexes et ont des enjeux
propres à chacun. Vous avez aussi votre propre sensibilité aux sujets abordés.&lt;/p&gt;
&lt;p&gt;Cette approche peut générer des données biaisées, erronées ou opposées aux convictions des candidats. Je vous
répèterai souvent qu&amp;rsquo;il faut vérifier les réponses et les données. Malgré tout le soin que j&amp;rsquo;ai apporté à ces tests,
il est évident que je ne peux pas garantir l&amp;rsquo;exactitude des réponses qui sont générées.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;les-étapes&#34;&gt;Les étapes&lt;/h2&gt;
&lt;p&gt;Il va falloir passer par plusieurs étapes. Ce travail prend du temps. Si vous voulez reproduire ce que je vous propose,
il faudra s&amp;rsquo;armer de patience, certainement corriger les données, faire plusieurs tests (notamment changer de modèle,
adapter les méthodes de réponses, lire de la documentation&amp;hellip;).&lt;/p&gt;
&lt;p&gt;Les étapes sont donc les suivantes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Récupération de l&amp;rsquo;audio du débat et édition du fichier pour supprimer ce qui n&amp;rsquo;est pas nécessaire (la pub&amp;hellip;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transcription automatisée&lt;/strong&gt; de l&amp;rsquo;audio en texte, qui se déroule en deux étapes :
&lt;ul&gt;
&lt;li&gt;segmenter par phrase et par locuteur (en gros, &amp;ldquo;qui dit quoi&amp;rdquo; et quand ?),&lt;/li&gt;
&lt;li&gt;transcrire chaque phrase en texte (traduire le son en texte)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;enregistrer les textes en vecteurs&lt;/strong&gt; dans une base d&amp;rsquo;indexation vectorielle (je vais vous expliquer le concept)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;poser des questions à un LLM&lt;/strong&gt; (je vais vous expliquer&amp;hellip;), en sortir des analyses, et des idées pour aller encore plus
loin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour y arriver, on aura besoin de pas mal d&amp;rsquo;outils.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je vais passer outre le téléchargement de l&amp;rsquo;audio/vidéo pour une raison simple : on est à la limite de l&amp;rsquo;égalité, car le
contenu est potentiellement sous droits d&amp;rsquo;auteurs. Je me refuse à donner
des moyens d&amp;rsquo;effectuer des téléchargements illégalement depuis des sites de chaines de télévision.
Pour autant, croyez-moi, c&amp;rsquo;est une étape très simple à réaliser. Un coup de Google et vous allez trouver des outils
pour cela (genre, cherchez &amp;ldquo;video downloader&amp;rdquo;, voilà, j&amp;rsquo;en ai trop dit).&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Par contre, pour la suite, aucun souci. Tout va passer par des outils IA :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pyannote.audio&lt;/code&gt; pour le découpage des segments de l&amp;rsquo;audio, afin d&amp;rsquo;avoir une série de phrases, et de savoir qui parle
et quand&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pydub&lt;/code&gt; pour la manipulation de l&amp;rsquo;audio&lt;/li&gt;
&lt;li&gt;&lt;code&gt;whisper&lt;/code&gt; pour la transcription de l&amp;rsquo;audio en texte&lt;/li&gt;
&lt;li&gt;&lt;code&gt;llama-index&lt;/code&gt; pour toute la partie que l&amp;rsquo;on appelle &amp;ldquo;RAG&amp;rdquo; (Retrieval Augmented Generation), en d&amp;rsquo;autres termes pour la
recherche d&amp;rsquo;informations dans un corpus de texte (pas de panique, je vais vous expliquer)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On utilisera un modèle pour que l&amp;rsquo;IA réponde à nos questions plus naturellement. J&amp;rsquo;apprécie beaucoup le modèle
&lt;a href=&#34;https://huggingface.co/Qwen/Qwen2-7B-Instruct&#34;&gt;&lt;code&gt;Qwen2&lt;/code&gt;&lt;/a&gt;, qui est
bien entrainé pour répondre en français. Localement, avec LLamaCPP, c&amp;rsquo;est viable. Mais je n&amp;rsquo;ai pas des foudres de guerre
et donc je vais passer par &lt;a href=&#34;https://huggingface.co/docs/api-inference/index&#34;&gt;l&amp;rsquo;API de HuggingFace&lt;/a&gt; pour gagner du temps.&lt;/p&gt;
&lt;p&gt;Il faudra utiliser, bien entendu, le modèle au format GGUF que vous trouverez ici &lt;a href=&#34;https://huggingface.co/Qwen/Qwen2-7B-Instruct-GGUF&#34;&gt;Qwen2-7B-Instruct-GGUF&lt;/a&gt;.
Les modèles dans ce format sont &amp;ldquo;quantizés&amp;rdquo; de différentes manières. Si j&amp;rsquo;en crois mes tests, c&amp;rsquo;est le modèle &lt;code&gt;q4_k_m&lt;/code&gt;
qui est le plus probant. Mais vous pouvez tester les autres…&lt;/p&gt;
&lt;p&gt;Par chance, &lt;code&gt;llama-index&lt;/code&gt; propose un package pour utiliser ce service.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si vous préférez OpenAI ou avoir un modèle local, voyez ma &lt;a href=&#34;#note-à-propos-de-openai-ou-llamacpp&#34;&gt;note&lt;/a&gt; en bas de l&amp;rsquo;article.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Cette API est gratuite pour un usage modéré, mais personnellement, j&amp;rsquo;ai un accès &amp;ldquo;pro&amp;rdquo; pour 9 €/mois qui repousse la
limite d&amp;rsquo;utilisation. Par contre, dans tous les cas, pour utiliser l&amp;rsquo;API ou récupérer des modèles, il vous faudra vous
enregistrer (gratuitement) sur le site et générer une clef d&amp;rsquo;API.&lt;/p&gt;
&lt;p&gt;Je vous laisse suivre les étapes expliquées sur la page &lt;a href=&#34;https://huggingface.co/docs/hub/en/security-tokens&#34;&gt;de Hugging
Face&lt;/a&gt;. Je vous conseille d&amp;rsquo;ailleurs d&amp;rsquo;installer le client en ligne
de commande &lt;a href=&#34;https://huggingface.co/docs/huggingface_hub/guides/cli&#34;&gt;comme indiqué ici&lt;/a&gt; pour enregistrer le &amp;ldquo;token&amp;rdquo; sur
votre poste et ne pas avoir à le mettre dans votre code source. La commande &lt;code&gt;huggingface-cli login&lt;/code&gt; vous
demandera la clef d&amp;rsquo;API et cela sera plus simple pour la suite.&lt;/p&gt;
&lt;p&gt;Vous pouvez aussi adapter tout ce que je vous explique ici pour utiliser les accès à OpenAPI (avec GPT donc),
ou même charger un modèle localement selon plusieurs méthodes.&lt;/p&gt;
&lt;p&gt;Allez lire la documentation du site &lt;a href=&#34;https://docs.llamaindex.ai/en/stable/&#34;&gt;de llama-index&lt;/a&gt; qui vous donnera les
explications nécessaires pour comprendre comment ça fonctionne.&lt;/p&gt;
&lt;h2 id=&#34;prérequis-et-préparation&#34;&gt;Prérequis et préparation&lt;/h2&gt;
&lt;p&gt;Normalement, tout devrait à peu près fonctionner sur un &lt;em&gt;notebook&lt;/em&gt; (comme Google Colab, Jupyter, Kaggle&amp;hellip;) - pour ma
part, j&amp;rsquo;utilise des scripts Python directement sur mon poste. Cela me permet de mieux contrôler les étapes et d&amp;rsquo;éviter
des écrasements de variables.&lt;/p&gt;
&lt;p&gt;Il vous faut absolument Python sur le poste, &lt;strong&gt;de préférence sur Linux&lt;/strong&gt;, et avoir un &lt;strong&gt;GPU&lt;/strong&gt; ayant, a minima, &lt;strong&gt;8 Go de VRAM&lt;/strong&gt;.
Malheureusement, pour la partie &amp;ldquo;transcription&amp;rdquo;, les modèles ne supportent que des GPU NVIDIA. Vous pouvez passer par
votre CPU, mais vous allez sentir passer le temps&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id=&#34;transcription-de-laudio&#34;&gt;Transcription de l&amp;rsquo;audio&lt;/h2&gt;
&lt;p&gt;On part du principe que vous avez récupéré l&amp;rsquo;audio de l&amp;rsquo;émission. Vous devez le transformer au format &lt;code&gt;.wav&lt;/code&gt;, car les
autres formats semblent poser un souci. N&amp;rsquo;oubliez pas de virer les publicités pour ne pas avoir des voix inutiles à
classifier. Un outil comme Audacity peut vous aider à éditer l&amp;rsquo;audio.&lt;/p&gt;
&lt;p&gt;Il est temps de se lancer.&lt;/p&gt;
&lt;p&gt;Créez un environnement virtuel, installez les dépendances :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# on va travailler dans un répertoire dédié
mkdir -p Projects/ML/Débat
cd Projects/ML/Débat

python -m venv venv
source venv/bin/activate
pip install pyannote.audio pydub whisper-openai
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le code &lt;code&gt;segmentazie.py&lt;/code&gt; suivant va créer un fichier &lt;code&gt;audio.rttm&lt;/code&gt; qui contiendra les segments de l&amp;rsquo;audio, avec le
locuteur. C&amp;rsquo;est une étape très importante, elle va nous permettre de savoir qui parle et quand.&lt;/p&gt;
&lt;p&gt;Avant toutes choses, notez que j&amp;rsquo;ai supprimé les publicités du fichier audio pour éviter d&amp;rsquo;avoir à identifier un nombre
conséquent de voix.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import numpy
import torch
from pyannote.audio import Pipeline
from pyannote.audio.pipelines.utils.hook import ProgressHook

# numpy.NAN is not defined, so we define it here
# I fixed this in a PR: https://github.com/pyannote/pyannote-audio/pull/1732
setattr(numpy, &amp;quot;NAN&amp;quot;, numpy.nan)

INPUT_FILE = &amp;quot;input.wav&amp;quot;

pipeline_model = &amp;quot;pyannote/speaker-diarization-3.1&amp;quot;

# check if cuda is available
device: torch.device = torch.device(&amp;quot;cuda&amp;quot; if torch.cuda.is_available() else &amp;quot;cpu&amp;quot;)

pipeline = Pipeline.from_pretrained(pipeline_model).to(device)

# apply the pipeline on the input file
with ProgressHook() as hook:
    diarization = pipeline(file=INPUT_FILE, hook=hook)

# dump the diarization output to disk using RTTM format
with open(&amp;quot;audio.rttm&amp;quot;, &amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as rttm:
    diarization.write_rttm(rttm)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le fichier &lt;code&gt;audio.rttm&lt;/code&gt; est un fichier texte qui contient ce genre de lignes :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;SPEAKER input_truncated 1 0.115 10.598 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_01 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
SPEAKER input_truncated 1 10.713 1.316 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_04 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
SPEAKER input_truncated 1 12.029 3.493 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_01 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
SPEAKER input_truncated 1 21.293 12.521 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_01 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
SPEAKER input_truncated 1 34.135 20.672 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_04 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
SPEAKER input_truncated 1 55.094 15.711 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_01 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
SPEAKER input_truncated 1 71.311 12.572 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_04 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
SPEAKER input_truncated 1 83.782 19.862 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_01 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
SPEAKER input_truncated 1 103.660 12.690 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_04 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
SPEAKER input_truncated 1 117.093 13.601 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt; SPEAKER_02 &amp;lt;NA&amp;gt; &amp;lt;NA&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On ne va pas détailler, mais il faut retenir que (en partant de la colonne 0) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La colonne 3 est le début du segment&lt;/li&gt;
&lt;li&gt;La colonne 4 est la durée du segment&lt;/li&gt;
&lt;li&gt;La colonne 7 est le &amp;ldquo;locuteur&amp;rdquo; - évidemment que c&amp;rsquo;est un identifiant &amp;ldquo;anonyme&amp;rdquo; car le script ne connait pas les voix,
il sait juste que c&amp;rsquo;est une personne différente qui parle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour ma part, j&amp;rsquo;ai écouté quelques segments en me basant sur les temps de début et j&amp;rsquo;ai pu trouver que (dans mon
cas) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SPEAKER_01&lt;/code&gt; est Anne-Claire Coudray&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SPEAKER_04&lt;/code&gt; est Gilles Bouleau&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SPEAKER_02&lt;/code&gt; est Manuel Bompard&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SPEAKER_05&lt;/code&gt; est Gabriel Attal&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SPEAKER_08&lt;/code&gt; est Jordan Bardella&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les autres segements sont généralement les moments où les candidats parlaient en même temps. Je les omets volontairement
pour la suite.&lt;/p&gt;
&lt;h2 id=&#34;transcription-de-laudio-en-textes&#34;&gt;Transcription de l&amp;rsquo;audio en textes&lt;/h2&gt;
&lt;p&gt;Maintenant que je sais qui parle et quand, je vais pouvoir transcrire les phrases en texte. On va utiliser &lt;code&gt;whisper&lt;/code&gt;, un
modèle open-source de OpenAI, très bien entrainé et franchement très bon.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai tenté d&amp;rsquo;utiliser le modèle &amp;ldquo;de base&amp;rdquo; (&lt;code&gt;base&lt;/code&gt;) mais les résultats étaient assez mauvais. Le modèle &lt;code&gt;medium&lt;/code&gt; a été
plus adapté. J&amp;rsquo;aurais adoré tester avec un modèle plus large, et par conséquent plus précis, mais ma machine a un peu de mal à
tout gérer.&lt;/p&gt;
&lt;p&gt;Ce que je vais faire, ici, c&amp;rsquo;est de créer des fichiers texte dans un répertoire par candidat. Cela permettra d&amp;rsquo;indexer
les propose plus facilement par la suite.&lt;/p&gt;
&lt;p&gt;Voilà donc le script &lt;code&gt;transcription.py&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import os

import torch
import shutil
import whisper
from pydub import AudioSegment

INPUT_FILE = &amp;quot;input.wav&amp;quot;

# model to use
model_type = &amp;quot;medium&amp;quot;  # tiny, base, small, medium, large

# open the audio file
audio = AudioSegment.from_file(INPUT_FILE, format=&amp;quot;wav&amp;quot;)
# open the diarization file
dz = open(&amp;quot;audio.rttm&amp;quot;, &amp;quot;r&amp;quot;).read().splitlines()

device = torch.device(&amp;quot;cuda&amp;quot; if torch.cuda.is_available() else &amp;quot;cpu&amp;quot;)

# load the model
model = whisper.load_model(model_type, device=device)

# only the candidates
speakers = {
    &amp;quot;SPEAKER_02&amp;quot;: &amp;quot;Manuel Bompard&amp;quot;,
    &amp;quot;SPEAKER_05&amp;quot;: &amp;quot;Gabriel Attal&amp;quot;,
    &amp;quot;SPEAKER_08&amp;quot;: &amp;quot;Jordan Bardella&amp;quot;,
}

# delete and recreate transcriptions/xxx
shutil.rmtree(&amp;quot;transcriptions&amp;quot;, ignore_errors=True)
for speaker in speakers.values():
    os.makedirs(&amp;quot;transcriptions/&amp;quot; + speaker, exist_ok=True)

current = &amp;quot;&amp;quot;
for line in dz:
    line = line.split()

    # get the speaker and the segment
    speaker = line[7]
    if speaker not in speakers:
        # not a candidate
        continue

    # get the real name of the speaker
    speaker_name = speakers.get(speaker)

    # get the start and the duration and convert to milliseconds
    start = float(line[3]) * 1000
    duration = float(line[4]) * 1000

    # get the segement
    audio_slice = audio[start : (start + duration)]

    # one channel, 16kHz
    audio_slice = audio_slice.set_channels(1).set_frame_rate(16000)

    if audio_slice.raw_data is None:
        continue

    # transcribe with raw data (converted)
    text = whisper.transcribe(
      model,
      audio=np.frombuffer(audio_slice.raw_data, dtype=np.int16).astype(np.float32)/32768.0,
      language=&amp;quot;fr&amp;quot;,
    )

    # print (optional)
    if speaker != current:
      # new speaker...
      print(f&amp;quot;--- {speaker_name} ---&amp;quot;)

    print(text[&amp;quot;text&amp;quot;])

    # append the text to the file in the speaker directory
    with open(f&amp;quot;transcriptions/{speaker_name}/propos.txt&amp;quot;, &amp;quot;a&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
        f.write(text[&amp;quot;text&amp;quot;] + &amp;quot;\n&amp;quot;)

    current = speaker
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela m&amp;rsquo;a généré 3 fichiers (dans le répertoire &amp;ldquo;transcriptions&amp;rdquo;) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Manuel Bompard/propos.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Gabriel Attal/propos.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Jordan Bardella/propos.txt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;aurais pu, bien entendu, aller plus loin, en ajoutant des contextes de question des journalistes, des réponses, voire
structurer les données. Mais je n&amp;rsquo;ai pas un temps infini&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Attention&lt;/strong&gt; whisper se trompe de temps en temps, et parfois &lt;code&gt;pyannote&lt;/code&gt; attribue des propos à la mauvaise personne.
Donc, clairement &lt;strong&gt;tout ce qui va suivre&lt;/strong&gt; doit être pris avec des pincettes. Partez du principe qu&amp;rsquo;il faut
normalement &lt;strong&gt;tout vérifier&lt;/strong&gt; et corriger les données. Or, je n&amp;rsquo;ai pas le temps, et là je vous propose uniquement une
explication dans un contexte d&amp;rsquo;actualité. Mon IA finale n&amp;rsquo;est pas précise, elle demanderait des heures de travail pour
être plus pertinente et moins dans l&amp;rsquo;erreur. J&amp;rsquo;espère avoir été clair : attention à ce que vous allez lire !&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;les-llm&#34;&gt;Les LLM&lt;/h2&gt;
&lt;p&gt;Avant d&amp;rsquo;aller plus loin, un petit rappel sur ce que sont les &amp;ldquo;LLM&amp;rdquo; (Large Language Models).&lt;/p&gt;
&lt;p&gt;Ce sont des modèles, c&amp;rsquo;est-à-dire des gros fichiers qui représentent des calculs à faire (des milliers, voir des millions
ou des milliards de paramètres) qui sont adaptés pour &amp;ldquo;traiter et générer du texte&amp;rdquo;. On parle de &amp;ldquo;modèles de langage&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Vous avez forcément entendu parler de ChatGPT, Gemini, Claude, Mistral&amp;hellip; ce sont des outils qui utilisent ces modèles
pour vous donner des réponses qui semblent cohérentes et naturelles.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ils ont, par contre, un énorme défaut !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ils sont &amp;ldquo;entrainés&amp;rdquo; à un instant T, sur des données qui sont &amp;ldquo;statiques&amp;rdquo;. Cela veut dire que, si vous leur posez une
question sur un sujet dont elle n&amp;rsquo;a pas connaissance, ce qui est très vrai en ce qui concerne l&amp;rsquo;actualité, eh bien, il
peut avoir trois réactions possibles :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dire qu&amp;rsquo;il ne sait pas&amp;hellip; ce qui est très rare&lt;/li&gt;
&lt;li&gt;donner une réponse qui n&amp;rsquo;est pas à jour&lt;/li&gt;
&lt;li&gt;raconter n&amp;rsquo;importe quoi, on dit qu&amp;rsquo;il &lt;em&gt;hallucine&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Par exemple, si je demande à ChatGPT (en 2024) la liste des &amp;ldquo;femmes premières ministres&amp;rdquo; que nous avons eu en France…&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/./chatgpt-ministre.png&#34; alt=&#34;ChatGPT n&amp;rsquo;est pas à jour&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et oui&amp;hellip; son entrainement date de 2021, il n&amp;rsquo;est donc pas au courant&amp;hellip;&lt;/p&gt;
&lt;p&gt;En ce qui concerne les cas d&amp;rsquo;hallucination, par exemple, je lui ai demandé de me donner les trois premiers ministres &amp;ldquo;femmes&amp;rdquo;
qu&amp;rsquo;on a eu en France. Selon d&amp;rsquo;où vient le vent, il me sort trois noms. Alors qu&amp;rsquo;en 2024, nous n&amp;rsquo;avons eu que deux femmes
premières ministres (E. Cresson et E. Borne).&lt;/p&gt;
&lt;p&gt;Pour la blague, il m&amp;rsquo;a cité &amp;ldquo;Ségolène Royal&amp;rdquo; dans la liste. Il a clairement halluciné la réponse &amp;#x1f609;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais pourquoi il &amp;ldquo;hallucine&amp;rdquo; ? Il est bête ou quoi ? Il ment ouvertement ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Parce que le modèle cherche à donner une réponse &lt;em&gt;coute que coute&lt;/em&gt;. Il va potentiellement commencer une phrase dont
la suite va être fausse. Qu&amp;rsquo;à cela ne tienne, il va continuer pour être cohérent.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hors, la cohérence n&amp;rsquo;est pas forcément vraie.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Comprenez bien que son but n&amp;rsquo;est pas de donner une vérité, mais de &amp;ldquo;fournir une réponse&amp;rdquo;. Il ne cherche pas
l&amp;rsquo;information, il génère du texte qui semble bon. Si l&amp;rsquo;information est biaisée, ou si elle parait ouverte, il va inventer sans
s&amp;rsquo;en rendre compte. Il faudrait qu&amp;rsquo;il ait vu une information viable pour donner une réponse viable.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors pourquoi on ne les entraine pas tous les jours ? En continu ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Parce que c&amp;rsquo;est long. Entrainer un modèle GPT prend &lt;strong&gt;des semaines, voire des mois !&lt;/strong&gt; Et ça coûte cher. Très cher. Il faut
des machines très puissantes, qui avalent de l&amp;rsquo;électricité comme Gérard enquille les litres de rouge. Vraiment, c&amp;rsquo;est
catastrophique.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et ça, c&amp;rsquo;est l&amp;rsquo;erreur que beaucoup font aujourd&amp;rsquo;hui : penser qu&amp;rsquo;un modèle LLM est une &amp;ldquo;source de vérité&amp;rdquo;. Un modèle LLM
ne sert par à donner de l&amp;rsquo;information, il sert à donner une réponse &amp;ldquo;naturelle&amp;rdquo;. La question qu&amp;rsquo;on lui pose oriente la
réponse. Si la question est mal posée, la réponse sera fausse.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Arrêtez d&amp;rsquo;utiliser ChatGPT comme si c&amp;rsquo;était un moteur de recherche !&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;C&amp;rsquo;est pour cela qu&amp;rsquo;on utilise de plus en plus la méthode &amp;ldquo;RAG&amp;rdquo;, dont on va parler plus tard, qui consiste à donner de
l&amp;rsquo;information à jour, contextualisée, au modèle.&lt;/p&gt;
&lt;h2 id=&#34;vectorisation-de-termes&#34;&gt;Vectorisation de termes&lt;/h2&gt;
&lt;p&gt;Cette phase est la plus &amp;ldquo;compliqué&amp;rdquo;. Même si, clairement, &lt;code&gt;llama-index&lt;/code&gt; est absolument génial et va vous mâcher tout le
travail, il faut prendre le temps de comprendre comment ça fonctionne.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si vous n&amp;rsquo;avez jamais entendu parler de &amp;ldquo;vectorisation de texte&amp;rdquo;, que l&amp;rsquo;on appelle aussi &lt;code&gt;embeding&lt;/code&gt;, je vous fais un
topo rapide.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;En français, et en anglais aussi, on utilise un terme pour définir &amp;ldquo;comment comprendre un mot&amp;rdquo;, et c&amp;rsquo;est assez fou
que ce terme soit exactement la définition dont on a besoin. &lt;strong&gt;On dit qu&amp;rsquo;un mot à un &amp;ldquo;sens&amp;rdquo;.&lt;/strong&gt; Vous ne vous rendez pas
compte à quel point dire cela est juste. D&amp;rsquo;ailleurs, on parle de direction quand on évoque &amp;ldquo;où veut aller le discours&amp;rdquo;,
et on dit clairement &amp;ldquo;je ne vois pas où tu veux en venir&amp;rdquo;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Car, en réalité, un &amp;ldquo;sens&amp;rdquo; indique une direction.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Alors prenons le mot &amp;ldquo;sens&amp;rdquo; au pied de la lettre. Imaginons que l&amp;rsquo;on dessine une flèche pour définir un mot.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/./aimer.svg&#34; alt=&#34;Aimer, vecteur&#34;&gt;&lt;/p&gt;
&lt;p&gt;Donc, deux mots &amp;ldquo;proches&amp;rdquo; iront à peu près dans la même direction, tandis qu&amp;rsquo;un mot opposé ira dans le sens inverse.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/./aimer-adorer-d%C3%A9tester.svg&#34; alt=&#34;Vecteur inverse&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et vous savez quoi ? Un sens, une flèche, en géométrie, on appelle ça un &lt;strong&gt;vecteur&lt;/strong&gt;. On peut le définir
mathématiquement. Un vecteur, en mathématique, c&amp;rsquo;est une direction et une longueur. On peut définir la longueur du
vecteur à partir des coordonnées.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/./vecteur-x-y.svg&#34; alt=&#34;Vecteur avec coordonnées&#34;&gt;&lt;/p&gt;
&lt;p&gt;Chose encore plus dingue, si on se débrouille bien, une addition de vecteurs peut donner un autre vecteur. Ce vecteur
résultant aura aussi un sens. Cela veut dire que, si on trouve une méthode pour savoir la direction de chaque mot,
alors :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on peut additionner les vecteurs de chaque mot d&amp;rsquo;une phrase, et avoir le sens de la phrase&lt;/li&gt;
&lt;li&gt;des vecteurs proches auront des sens proches&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela veut dire que la &amp;ldquo;sémantique&amp;rdquo; est mathématiquement représentable. C&amp;rsquo;est ce que l&amp;rsquo;on appelle la &amp;ldquo;vectorisation
de texte&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/./sentence.svg&#34; alt=&#34;Phrase vectorisée&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ici, pour vulgariser, j&amp;rsquo;ai utilisé des flèches sur un plan. Donc en deux dimensions (2D). On peut imaginer qu&amp;rsquo;un vecteur soit
défini par trois dimensions (3D)&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Par exemple, &amp;ldquo;Chat&amp;rdquo; et &amp;ldquo;Chien&amp;rdquo; sont proches, mais &amp;ldquo;Serpent&amp;rdquo; l&amp;rsquo;est moins. Si les axes ont une signification propre au
modèle (par exemple s&amp;rsquo;il approche les concepts de &amp;ldquo;mammifère&amp;rdquo;, &amp;ldquo;animal&amp;rdquo;, &amp;ldquo;domestique&amp;rdquo;, etc.), alors on peut imaginer ce
genre de vecteur en trois dimensions :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/./vector-3d.svg&#34; alt=&#34;vecteur en 3 dimensions&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sauf que mathématiquement, on n&amp;rsquo;a aucune limite. Les modèles utilisés pour la sémantique
gèrent des vecteurs en 128, 256, 512, 1024 dimensions… Le cerveau humain ne peut pas imaginer ce genre de flèche, de
vecteur. Mais on peut le représenter mathématiquement. Cela donne énormément d&amp;rsquo;informations sur le &lt;strong&gt;sens&lt;/strong&gt; d&amp;rsquo;un mot.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ainsi, un mot comme &amp;ldquo;chat&amp;rdquo; peut être représenté comme ceci:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;[-0.255, 1.25, 6.32, -0.23, 1.2, 6.3, ...]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Comment on choisit ces valeurs ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Eh bien ce n&amp;rsquo;est pas à nous, humains derrière le clavier, de décider. On utilise des algorithmes qui vont trouver tout
seul les valeurs qui rapprochent ou éloignent les concepts. Ce sont des &amp;ldquo;régressions statistiques&amp;rdquo; qui vont
trouver ces données, et c&amp;rsquo;est bien assez compliqué pour que je vous dise &amp;ldquo;ayez confiance, ça marche&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Nous, de toutes manières, on ne va pas se prendre la tête, on va utiliser des modèles déjà entrainés qui savent donner
un vecteur à chaque mot.&lt;/p&gt;
&lt;p&gt;Par contre, c&amp;rsquo;est bien beau de savoir vectoriser des mots, mais ça sert à quoi au juste ?&lt;/p&gt;
&lt;p&gt;Le but, c&amp;rsquo;est d&amp;rsquo;être capable, à terme, de pouvoir trouver des parties d&amp;rsquo;un document qui &amp;ldquo;semble parler d&amp;rsquo;un sujet en
particulier&amp;rdquo;. Cela veut dire que, si je cherche des documents qui parlent de &amp;ldquo;chien&amp;rdquo;, je vais chercher les vecteurs qui
se rapprochent le plus du vecteur &amp;ldquo;chien&amp;rdquo;. Et je vais certainement tomber sur des documents qui parlent de &amp;ldquo;canidés&amp;rdquo;, de
&amp;ldquo;toutous&amp;rdquo;, de &amp;ldquo;cabots&amp;rdquo;, etc. Parce que les vecteurs qui représentent ces mots sont proches de celui de &amp;ldquo;chien&amp;rdquo;. On dit
que leur &amp;ldquo;distance vectorielle est proche&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;La méthode la plus simple est donc de découper mon texte en petits morceaux (en phrases, ou en sections de texte), et
d&amp;rsquo;enregistrer chaque morceau et ses vecteurs dans une base de données adaptée.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/./RAG-Vector.-index.svg&#34; alt=&#34;Indexation vectorielle&#34;&gt;&lt;/p&gt;
&lt;p&gt;Cela veut dire que, si j&amp;rsquo;ai une &amp;ldquo;question&amp;rdquo; à poser, je peux la transformer en un vecteur et chercher dans la base tous
les bouts de texte qui ont un sens proche. Alors une fois de plus, on a des outils pour ça, on ne va pas faire de maths
bien énervés nous-même. Reposons-nous sur le travail fait par des gens qui nous ont facilité la tâche.&lt;/p&gt;
&lt;p&gt;Maintenant, comprenez l&amp;rsquo;intérêt dans notre exemple. On va pouvoir faire ce genre de choses :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;chercher les propos de chaque candidat dans une base pour un sujet donné, une question, etc.&lt;/li&gt;
&lt;li&gt;construire une question à donner à un modèle de génération de texte pour avoir une réponse &amp;ldquo;naturelle&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est ce que l&amp;rsquo;on appelle, le &lt;strong&gt;RAG (Retrieval Augmented Generation).&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Je réponds à une question qu&amp;rsquo;on m&amp;rsquo;a posé dernièrement :&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais pourquoi passer par des vecteurs tout compliqués alors que j&amp;rsquo;ai un moteur de base de données qui sait faire de la
recherche &amp;ldquo;full text&amp;rdquo; (recherche de mots) ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Eh bien parce que le but n&amp;rsquo;est pas de trouver des textes qui contiennent des &amp;ldquo;mots clefs&amp;rdquo; trouvés dans la question. Le
but est de trouver des bouts de textes qui ont un sens proche de la question. Cela veut dire que, si je pose une
question &amp;ldquo;Trouves moi un truc à me mettre sur la tête&amp;rdquo;, je ne veux pas trouver les textes qui parlent de &amp;ldquo;porter&amp;rdquo; et
&amp;ldquo;tête&amp;rdquo;. Je veux qu&amp;rsquo;il me trouve des textes qui parlent de bonnets, de chapeaux ou de couronnes. Même si je n&amp;rsquo;ai pas
utilisé ces mots. Seule la sémantique, le sens, compte. Et la vectorisation de texte est la seule technique qui
permet de faire ça efficacement.&lt;/p&gt;
&lt;p&gt;En fait, on pourrait se passer de la vectorisation, mais les résultats seraient moins précis. On aurait des résultats
parasites, des réponses ambivalentes.&lt;/p&gt;
&lt;p&gt;Ah, une précision tout de même, on pourrait utiliser Elastic Search ou TypeSense qui propose déjà une vectorisation des
termes indexés. Mais, à mon sens, c&amp;rsquo;est utile si vous avez besoin de la base pour d&amp;rsquo;autres choses. Nous, ici, on va
faire du &amp;ldquo;one shot&amp;rdquo;, du test ponctuels, donc on va passer par une base en mémoire, adaptée à la recherche sémantique
pour ce cas de figure.&lt;/p&gt;
&lt;h2 id=&#34;et-donc-tas-parlé-de-rag-cest-quoi-&#34;&gt;Et donc, t&amp;rsquo;as parlé de RAG, c&amp;rsquo;est quoi ?&lt;/h2&gt;
&lt;p&gt;Eh bien, c&amp;rsquo;est une &amp;ldquo;technique&amp;rdquo; qui consiste à utiliser un moteur de recherche pour extraire des informations à partir
d&amp;rsquo;une question, puis de compiler une question avec ce contexte qu&amp;rsquo;on va poser à un modèle LLM. Et par conséquent
d&amp;rsquo;avoir une réponse &amp;ldquo;naturelle&amp;rdquo; et cohérente basée sur des informations à jour dans un contexte bien déterminé.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En gros, on oriente la réponse que donne une IA en lui donnant des informations.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;C&amp;rsquo;est, en fait, assez &amp;ldquo;simple&amp;rdquo; conceptuellement parlant.&lt;/p&gt;
&lt;p&gt;La question posée par un utilisateur est utilisée deux fois :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pour trouver les bouts de texte dans la base, afin d&amp;rsquo;avoir un &amp;ldquo;contexte&amp;rdquo; d&amp;rsquo;informations&lt;/li&gt;
&lt;li&gt;pour demander à l&amp;rsquo;IA de répondre à la question &lt;strong&gt;en prenant en compte le contexte&lt;/strong&gt; qu&amp;rsquo;on lui injecte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/ia-pour-analyser-un-d%C3%A9bat/./RAG-query.svg&#34; alt=&#34;RAG création d&amp;rsquo;un prompt&#34;&gt;&lt;/p&gt;
&lt;p&gt;Cela a pour effet d&amp;rsquo;éviter les &amp;ldquo;hallucinations&amp;rdquo; des modèles, car on leur donne des informations à jour, contextualisées.&lt;/p&gt;
&lt;p&gt;Le LLM en bout de chaine, il sert à deux choses finalement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;arriver à analyser pour résumer la réponse de manière cohérente&lt;/li&gt;
&lt;li&gt;donner une réponse &amp;ldquo;naturelle&amp;rdquo; à l&amp;rsquo;utilisateur&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Comme un &amp;ldquo;LLM&amp;rdquo; est un modèle &amp;ldquo;génératif&amp;rdquo;, et qu&amp;rsquo;on &amp;ldquo;retrouve&amp;rdquo; des informations qu&amp;rsquo;on injecte à notre question,
vous comprenez que RAG (Retrieval Augmented Generation) veut bien dire &amp;ldquo;Génération Augmentée par la Recherche&amp;rdquo;.
Eh ouais &amp;#x1f604;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Dans les faits, on peut pousser le concept très loin. Par exemple en envoyant la réponse une seconde fois dans le moteur
avec d&amp;rsquo;autres informations issue d&amp;rsquo;une autre recherche basée sur la réponse. On peut ainsi affiner la réponse (&amp;ldquo;méthode
Refine&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;llama-index propose d&amp;rsquo;ailleurs plusieurs techniques de recherche, et de génération de résultat. On peut par exemple
faire de la recherche en mode &amp;ldquo;refine&amp;rdquo;, &amp;ldquo;compact&amp;rdquo; ou &amp;ldquo;summarize&amp;rdquo;, et avoir des arbres de réponses pour connaitre le
document sur lequel il trouve les réponses, etc.&lt;/p&gt;
&lt;p&gt;Pour l&amp;rsquo;heure, on fait simple. Mais vous pouvez vous amuser un peu si vous voulez.&lt;/p&gt;
&lt;p&gt;Je vous invite à lire leur documentation, vous verrez que c&amp;rsquo;est très complet. Mais &lt;strong&gt;vraiment&lt;/strong&gt; très complet.&lt;/p&gt;
&lt;h2 id=&#34;implémentation-du-rag&#34;&gt;Implémentation du RAG&lt;/h2&gt;
&lt;p&gt;Nous allons créer un second environnement virtuel parce que LLamaIndex utilise une version de &lt;code&gt;numpy&lt;/code&gt; incompatible avec
celle que nous utilisons pour &lt;code&gt;pyannote.audio&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Nous installons &lt;code&gt;chromadb&lt;/code&gt; pour indexer les documents, on va l&amp;rsquo;utiliser en mode &lt;code&gt;in memory&lt;/code&gt;, car nous n&amp;rsquo;avons pas
beaucoup de documents à indéxer, ce sera donc rapide et nous n&amp;rsquo;aurons pas à stocker tout ça sur le disque.&lt;/p&gt;
&lt;p&gt;On pourrait utiliser le système d&amp;rsquo;indexation par défaut, intégré à &lt;code&gt;llama-index&lt;/code&gt;. Cependant, &lt;code&gt;chromadb&lt;/code&gt; est plus adapté
et mieux optimisé pour les recherches sémantiques.&lt;/p&gt;
&lt;p&gt;Il nous faudra aussi quelques sous-paquets pour accéder à des modèles LLM. Dans mon cas, je vais utiliser l&amp;rsquo;API
d&amp;rsquo;inférence de HuggingFace (parce que c&amp;rsquo;est rapide, simple et efficace). Mais si vous désirez utiliser un modèle local,
vous pouvez changer un peu le code et utiliser &lt;code&gt;llama-index-llms-llamacpp&lt;/code&gt; pour charger un modèle au format &lt;code&gt;GGUF&lt;/code&gt; par
exemple.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# quittez l&#39;environnement virtuel précédent
deactivate

# créez un nouvel environnement virtuel
python -m venv venv-llama
source venv-llama/bin/activate

# installez les dépendances
pip install chromadb
pip install llama-index llama-index-embeddings-huggingface llama-index-llms-huggingface llama-index-vector-stores-chroma
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le code suivant est à peu près clair.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nous créons une base chroma en mémoire&lt;/li&gt;
&lt;li&gt;Pour chaque candidat, je crée une collection séparée (comme ça je peux trouver les réponses pour un candidat)&lt;/li&gt;
&lt;li&gt;Ensuite, je boucle sur les candidats :
&lt;ul&gt;
&lt;li&gt;J&amp;rsquo;indexe les propos.&lt;/li&gt;
&lt;li&gt;Je stocke les vecteurs dans la collection du candidat.&lt;/li&gt;
&lt;li&gt;Je garde le moteur de requête pour pouvoir poser des questions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il me reste enfin à avoir une base de questions. Pour chaque question à poser, je la pose à chaque candidat.&lt;/p&gt;
&lt;p&gt;Lisez les commentaires dans le code, c&amp;rsquo;est important si vous voulez comprendre ce que je fais, et comment l&amp;rsquo;adapter à
vos besoins.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from llama_index.llms.huggingface import HuggingFaceInferenceAPI
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import (
    SimpleDirectoryReader,
    StorageContext,
    VectorStoreIndex,
    PromptTemplate,
)
import chromadb

# une base ChromaDB épheémère, en mémoire
chroma_client = chromadb.EphemeralClient()

# Une lsite de candidats avec leur collection associée. Bien entendu,
# j&#39;aurais pu automatisé en listant les répertoires, mais pour vous clarifier
# le code... je fais comme ça
collections: dict[str, dict] = {
    &amp;quot;Jordan Bardella&amp;quot;: {
        &amp;quot;chroma&amp;quot;: chroma_client.create_collection(&amp;quot;bardella&amp;quot;),
        &amp;quot;query&amp;quot;: None,
    },
    &amp;quot;Manuel Bompard&amp;quot;: {
        &amp;quot;chroma&amp;quot;: chroma_client.create_collection(&amp;quot;bompard&amp;quot;),
        &amp;quot;query&amp;quot;: None,
    },
    &amp;quot;Gabriel Attal&amp;quot;: {
        &amp;quot;chroma&amp;quot;: chroma_client.create_collection(&amp;quot;attal&amp;quot;),
        &amp;quot;query&amp;quot;: None,
    },
}

# le prompt &amp;quot;de base&amp;quot; pour contextualiser la manière dont me répondra le LLM.
# Je force un peu la main pour avoir des réponses en français et contextualisées sur
# le domaine politique.
system_prompt = &amp;quot; &amp;quot;.join(
    [
        &amp;quot;Tu est un électeur qui cherche des réponses à des question sur les candidats à l&#39;élection legislative.&amp;quot;,
        &amp;quot;Tu réponds en français.&amp;quot;,
    ]
)

# LLM à utiliser. J&#39;utilise ici l&#39;API de HuggingFace, mais vous pouvez
# passer par OpenAI, ou charger un modèle avec LLamaCPP. La doc est assez
# fournie
# model = &amp;quot;mistralai/Mistral-7B-Instruct-v0.3&amp;quot; # peu précis et contradictoire
model = &amp;quot;HuggingFaceH4/zephyr-7b-alpha&amp;quot; # le meilleur durant mes tests
# model = &amp;quot;HuggingFaceH4/zephyr-7b-beta&amp;quot; # me donne souvent des textes vides...
llm = HuggingFaceInferenceAPI(
    model_name=model,
    system_prompt=system_prompt,
)
# Voir la note dans l&#39;article, plus bas, pour OpenAI ou LLamaCPP

# le moteur de vectorisation de texte. J&#39;utilise ici un modèle léger, largement suffisant
# pour ce genre de tâche (il tournera en local, donc il va être téléchargé)
embeddings = HuggingFaceEmbedding(model_name=&amp;quot;BAAI/bge-base-en-v1.5&amp;quot;)

# les templates pour le LLM. Parce que je veux des réponses en francais, je dois
# adaper les templates. Si vous voulez bosser en anglais, c&#39;est inutile. llama-index
# a déjà des templates utilisés par défaut.
text_qa_template_str = (
    &amp;quot;Le contexte de l&#39;information est le suivant :&amp;quot;
    &amp;quot;\n---------------------\n{context_str}\n---------------------\n&amp;quot;
    &amp;quot;En utilisant à la fois ces informations de contexte et tes propres connaissances, &amp;quot;
    &amp;quot;réponds à la question suivante : {query_str}\n&amp;quot;
    &amp;quot;Si le contexte n&#39;est pas utile, tu peux également répondre à la question par toi-même.\n&amp;quot;
)
text_qa_template = PromptTemplate(text_qa_template_str)

# seulement utile si on veut changer le mode de réponse en mode &amp;quot;refine&amp;quot;
refine_template_str = (
    &amp;quot;La question originale est la suivante : {query_str}\nNous avons fourni une&amp;quot;
    &amp;quot; réponse existante : {existing_answer}\nNous avons la possibilité d&#39;affiner&amp;quot;
    &amp;quot; la réponse existante (uniquement si nécessaire) avec un peu plus de contexte&amp;quot;
    &amp;quot; ci-dessous.\n------------\n{context_msg}\n------------\nEn utilisant à la fois&amp;quot;
    &amp;quot; le nouveau contexte et tes propres connaissances, mets à jour ou répètes la réponse&amp;quot;
    &amp;quot; existante.\n&amp;quot;
)
refine_template = PromptTemplate(refine_template_str)

# C&#39;est parti. Puor chaque candidat, on va indexer les propos qui se trouvent dans le répertoire
# qui porte le nom du candidat.
for candidate, col in collections.items():
    path = f&amp;quot;transcriptions/{candidate}&amp;quot; # répertoire qui contient les propos du candidat
    storage = StorageContext.from_defaults(
        vector_store=ChromaVectorStore(col[&amp;quot;chroma&amp;quot;]), # collection du candidat
    )
    # on lit les documents (les propos dans le fichier texte)
    documents = SimpleDirectoryReader(path).load_data(show_progress=True)
    # on indexe les documents (vectorisation et enregistrement dans la base)
    vector_store = VectorStoreIndex.from_documents(
        documents=documents,
        storage_context=storage,
        llm=llm,
        embed_model=embeddings,
        show_progress=True,
    )
    # on crée un moteur de recherche sémanitque qui répondra avec le LLM et en utilisant
    # le ou les templates de questions
    query_engine = vector_store.as_query_engine(
        llm=llm,
        embed_model=embeddings,
        text_qa_template=text_qa_template,
        refine_template=refine_template,
    )
    col[&amp;quot;query&amp;quot;] = query_engine # on garde ce moteur pour poser des questions

# donc là on a &amp;quot;collections&amp;quot; qui contient, pour chaque candidat, un moteur de &amp;quot;requête&amp;quot;
# qui va nous permettre de poser des questions et d&#39;avoir des réponses contextualisées

# les questions à poser
questions = [
    &amp;quot;Que propose le candidat pour lutter contre le chômage ?&amp;quot;,
    &amp;quot;Que propose le candidat pour augmenter le pouvoir d&#39;achat ?&amp;quot;,
    &amp;quot;Que propose le candidat pour lutter contre le réchauffement climatique, l&#39;environnement, les énergies fossiles et renouvelables ?&amp;quot;,
    &amp;quot;Que propose le candidat pour améliorer le système de santé, les déserts médicaux, l&#39;accès aux soins ?&amp;quot;,
    &amp;quot;Que propose le candidat pour améliorer le système éducatif, scolaire, écoles ?&amp;quot;,
]

# et c&#39;est parti... on pose les questions
for question in questions:
    print(f&amp;quot;=== Question: {question} ===&amp;quot;)
    for candidate, col in collections.items(): #pour chaque candidiat on pose la question
        print(f&amp;quot;=&amp;gt; Candidat: {candidate}&amp;quot;)
        response = col[&amp;quot;query&amp;quot;].query(question)
        print(response)
        print()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En exécutant ce script, nous avons des réponses clarifiées, propres, contextualisées et plus condensées.&lt;/p&gt;
&lt;p&gt;Voici un extrait de la sortie dans le terminal, &lt;strong&gt;attention, je vous rappelle que l&amp;rsquo;IA peut se tromper, mal interpréter,
voir halluciner une réponse&lt;/strong&gt;. Ne prenez pas ces réponses pour argent comptant, ne les utilisez pas pour prendre votre
décision dimanche 30 juin. Ceci est un exemple, rien d&amp;rsquo;autre !&lt;/p&gt;
&lt;p&gt;Avec le modèle &lt;code&gt;MisralAI/Mistral-7B-Instruct-v0.3&lt;/code&gt;, voici un exemple de sortie (que je trouve moyen) :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;=== Question: Que propose le candidat pour lutter contre le réchauffement climatique, l&#39;environnement, les énergies fossiles et renouvelables ? ===

=&amp;gt; Candidat: Jordan Bardella
Le candidat propose de mettre en place un moratoire sur la construction de tout nouveau chantier éolien, car il estime
que 25% du temps, les énergies éoliennes tournent à vide et que l&#39;énergie ne se stocke pas, ce qui oblige à brader l&#39;énergie.
Il souhaite également refaire de la France un paradis énergétique en défendant le nucléaire, qui permet à la France d&#39;avoir
une énergie pas chère, décarbonée et bonne pour les factures et pour les entreprises. Il souhaite également déverrouiller
les contraintes qui pèsent sur la croissance pour les entreprises, réindustrialiser, relocaliser et simplifier l&#39;économie pour
créer les conditions d&#39;une diminution de l&#39;empreinte carbone de la France sur la planète et de celle de l&#39;Union européenne.
Il estime également que la gestion stratégique de l&#39;eau pose des enjeux très concrets pour les agriculteurs.

=&amp;gt; Candidat: Manuel Bompard
Le candidat Manuel Bompard propose de faire des investissements publics massifs pour la transition écologique, notamment
dans la rénovation thermique des logements. Il considère que la construction de nouveaux réacteurs nucléaires ne peut
pas être une solution à l&#39;urgence climatique, car les efforts de réduction des émissions de gaz à effet de serre qu&#39;il
faut faire doivent être faits dans un temps qui ne permet pas la construction de ces réacteurs nucléaires.

=&amp;gt; Candidat: Gabriel Attal
Le candidat propose de continuer à investir pour décarboner notre industrie, mais pas via la décroissance comme le propose monsieur Bonpard.
Il a engagé un plan pour relancer le nucléaire avec 14 nouveaux réacteurs et les chantiers ont déjà démarré.
En 2035, on aura des nouveaux réacteurs qui pourront entrer.
Il a également promis de continuer à investir dans les énergies renouvelables, mais il a souligné que si on veut baisser
nos émissions de CO2, si on veut être vraiment indépendant en matière d&#39;énergie, il faut continuer à installer des éoliennes.
Il a également souligné que les éoliennes nouvelles qui sont installées chaque année, c&#39;est l&#39;équivalent d&#39;un réacteur nucléaire supplémentaire.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En utilisant, cette fois, le modèle &lt;code&gt;HuggingFaceH4/zephyr-7b-alpha&lt;/code&gt;, voici un autre exemple de sortie, que je trouve plus
pertinent et cohérent :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;=== Question: Que propose le candidat pour lutter contre le réchauffement climatique, l&#39;environnement, les énergies fossiles et renouvelables ? ===

=&amp;gt; Candidat: Jordan Bardella
Le candidat propose de lutter contre le réchauffement climatique en faisant de la France un pays de producteurs
et en relocalisant les industries. Il souhaite refaire de la France un paradis énergétique en soutenant
le nucléaire, qui est un atout incontesté pour la France et l&#39;Union européenne.
Il pense que les énergies renouvelables doivent être sécurisées, mais il souhaite également arrêter les éoliennes
et mettre en place un moratoire sur la construction de nouveaux chantiers éoliennes en raison de leur faible efficacité
énergétique. Il est opposé à l&#39;importation de véhicules électriques trop chers et à leur coût de réparation trop élevé
, qui rendraient les véhicules électriques inaccessibles pour les classes populaires et moyennes. Il souhaite
donc simplifier l&#39;économie et déverrouiller les contraintes qui pèsent sur la croissance pour les entreprises.

=&amp;gt; Candidat: Manuel Bompard
Le candidat Manuel Bompard propose de faire des dépenses significatives, très importantes, de plusieurs dizaines de milliards
d&#39;euros pour la transition écologique, parce que c&#39;est le défi du siècle, et que ce défi du siècle, on ne pourra y répondre
que par des investissements publics massifs.
Il considère que face à l&#39;urgence climatique que nous avons devant nous, c&#39;est une très mauvaise décision de faire des coupes budgétaires
 sur le budget de la transition écologique et en particulier sur la rénovation thermique des logements. Il estime que
pour lutter contre le réchauffement climatique, il faut réduire massivement les émissions de gaz à effet de serre d&#39;ici à 2030,
d&#39;ici à 2035, d&#39;ici à 2040. Il estime que présenter la construction de nouveaux réacteurs nucléaires comme une solution à
l&#39;urgence climatique est tout simplement faux et inexact.

=&amp;gt; Candidat: Gabriel Attal
Le candidat propose de continuer à investir pour décarboner notre industrie et non pas via la décroissance comme le
propose le candidat de l&#39;autre parti. Il soutient la construction de 14 nouveaux réacteurs nucléaire
s et les chantiers ont déjà démarré. Il a également promulgué une loi de fermeture centrale nucléaire, mais il affirme
que les premières centrales nucléaires seront en place en 2035. Il soulève la question de savoir
comment les Français pourront répondre à la demande croissante d&#39;électricité si les éoliennes sont arrêtées alors que les
nouveaux réacteurs nucléaires ne seront pas en place avant 2035. Il affirme que les éoliennes
nouvelles qui sont installées chaque année, c&#39;est l&#39;équivalent d&#39;un réacteur nucléaire supplémentaire. Les parcs actuels
arrivent à échéance dans combien de temps ?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En fonction du modèle, nous avons des divergences de résultats. Pour autant, le sens reste à peu près le même et les
informations extraites sont assez cohérentes à ce que j&amp;rsquo;ai pu entendre en direct.&lt;/p&gt;
&lt;p&gt;Mais je le répète : &lt;strong&gt;je n&amp;rsquo;ai pas nettoyé les données d&amp;rsquo;extraction audio, et il est possible d&amp;rsquo;ailleurs que certains
propos ne soient pas attribués à la bonne personne.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Il faudrait aussi que j&amp;rsquo;adapte les prompts pour les rendre plus précis, plus ouverts, ou au contraire plus strict sur la
réponse attendue.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais, est-ce que cela est pertinent ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Alors&amp;hellip; si je pars &amp;ldquo;en connaissance de cause&amp;rdquo;, en sachant qu&amp;rsquo;il manque certainement des informations, que la
transcription demande des corrections, etc. Oui, c&amp;rsquo;est pertinent. Oui cela a bien résumé les propos, et oui, je trouve
à peu près ce que je cherche dans le débat qui a duré plus d&amp;rsquo;une heure et demie.&lt;/p&gt;
&lt;p&gt;Aussi, je retrouve à peu près les mêmes informations que j&amp;rsquo;ai pu entendre. Clairement, avec peu de moyens et de temps,
j&amp;rsquo;ai déjà un résultat qui est assez satisfaisant.&lt;/p&gt;
&lt;p&gt;Oui, cela est utile, oui, on peut s&amp;rsquo;en servir. Mais il est &lt;strong&gt;impératif&lt;/strong&gt; de prendre du recul et de &lt;strong&gt;vérifier&lt;/strong&gt; les propos
extraits par l&amp;rsquo;IA. Car clairement, elle se trompe de temps en temps.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;La règle est claire : doutez de l&amp;rsquo;IA. Elle est comme un pote à qui vous parlez, elle sort un avis, basé sur ses
compétences d&amp;rsquo;analyse, mais elle a des biais, et en plus vos données sont potentiellement cassées.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;note-à-propos-de-openai-ou-llamacpp&#34;&gt;Note à propos de OpenAI ou LLAmaCPP&lt;/h2&gt;
&lt;p&gt;Si vous ne voulez pas utiliser l&amp;rsquo;API de HuggingFace, vous pouvez utiliser OpenAI ou LLamaCPP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OpenAI&lt;/strong&gt;, installez le paquet &lt;code&gt;llama-index-llms-openai&lt;/code&gt; et utilisez le modèle &lt;code&gt;OpenAI/gpt-3.5-turbo&lt;/code&gt;. Remplacez la
variable &lt;code&gt;llm&lt;/code&gt; par :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;llm = OpenAI(
    system_prompt=system_prompt,
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;LLamaCPP&lt;/strong&gt;, vous devez installer, en premier lieu, le package &lt;a href=&#34;https://github.com/abetlen/llama-cpp-python&#34;&gt;LamaCPP-python&lt;/a&gt; en précisant le &amp;ldquo;backend&amp;rdquo; que vous voulez.&lt;/p&gt;
&lt;p&gt;Pour ma part, j&amp;rsquo;en ai marre d&amp;rsquo;utiliser CUDA, donc je passe par Vulkan :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;CMAKE_ARGS=&amp;quot;-DLLAMA_VULKAN=on&amp;quot; pip install llama-cpp-python
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puis, installez le paquet &lt;code&gt;lla-index-llms-llamacpp&lt;/code&gt; et utilisez le modèle &lt;code&gt;GGUF&lt;/code&gt;. Remplacez la variable &lt;code&gt;llm&lt;/code&gt; par :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;llm = LLamaCPP(
    model_url=&amp;quot;ici l&#39;url d&#39;un modèle GGUF&amp;quot;,
    system_prompt=system_prompt,
    # model_kwargs={&amp;quot;n_gpu_layers&amp;quot;: 28}, # si vous avez un GPU avec peu de mémoire
    # model_kwargs={&amp;quot;n_gpu_layers&amp;quot;: -1}, # si vous voulez tout charger dans le GPU
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&amp;rsquo;intérêt de LLamaCPP, c&amp;rsquo;est que vous pouvez charger des modèles GGUF qui débordent de la VRAM sur la RAM du système et
de répartir le travail en partie sur le CPU. On appelle cela du &amp;ldquo;offloading&amp;rdquo;. C&amp;rsquo;est très utile si vous avez un GPU trop
léger. Par contre, &lt;strong&gt;c&amp;rsquo;est plus lent&lt;/strong&gt;. Pour une RTX 3070 avec 12 Go de VRAM, je suis obligé de réduire le nombre de
couches (28 sur les 31 de Qwen2) à charger dans la carte. Cela demande donc au CPU de gérer 2 couches. Ça reste
utilisable, mais on entend les ventilateurs tourner&amp;hellip;&lt;/p&gt;
&lt;p&gt;Par exemple, pour un modèle Qwen2-7B-Instruct-GGUF, j&amp;rsquo;utilise &lt;code&gt;https://huggingface.co/Qwen/Qwen2-7B-Instruct-GGUF/resolve/main/qwen2-7b-instruct-q4_k_m.gguf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Le compte &amp;ldquo;The Block&amp;rdquo; propose un grand nombre de modèles GGUF, vous pouvez les tester : &lt;a href=&#34;https://huggingface.co/TheBloke&#34;&gt;https://huggingface.co/TheBloke&lt;/a&gt;,
par exemple les modèles Llama2 en 7B se trouvent ici : &lt;a href=&#34;https://huggingface.co/TheBloke/Llama-2-7B-GGUF/tree/main&#34;&gt;https://huggingface.co/TheBloke/Llama-2-7B-GGUF/tree/main&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;encore-plus-loin&#34;&gt;Encore plus loin&lt;/h2&gt;
&lt;p&gt;Je ne vous colle pas le code ici, car l&amp;rsquo;article devient long. Mais j&amp;rsquo;ai ajouté pas mal de traitements supplémentaires
à mon script :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pour chaque réponse, je fais demande à l&amp;rsquo;IA de me sortir une requête que je pourrais donner à un moteur de recherche,&lt;/li&gt;
&lt;li&gt;cette requête, je l&amp;rsquo;envoie à Google (ou autre) pour trouver des pages d&amp;rsquo;information,&lt;/li&gt;
&lt;li&gt;ces pages, je les indexe&lt;/li&gt;
&lt;li&gt;ensuite, je lance une recherche sémantique sur ces pages pour avoir des informations,&lt;/li&gt;
&lt;li&gt;je demande au LLM de me dire si le candidat a dit vrai ou faux, en partie ou totalement&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eh bien, sans surprise, les trois candidats ont beaucoup (mais alors, beaucoup hein) raconté de choses ambigües,
ont forcé le trait ou menti. Un candidat a même excellé dans les contre-vérités et l&amp;rsquo;imprécision (en répondant &amp;ldquo;en
dehors de la question&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai aussi réussi, tant bien que mal, à analyser la &amp;ldquo;teneur des propos&amp;rdquo;, et deux candidats sont perçus comme
&amp;ldquo;agressifs&amp;rdquo;, un est souvent sur la défensive, et un autre est souvent &amp;ldquo;dans le flou&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Alors, encore une fois, tout sort de ce que l&amp;rsquo;IA &amp;ldquo;semble comprendre&amp;rdquo;. Et c&amp;rsquo;est pour cela que je ne vous montre pas ces
résultats. Ils pourraient être mal interprétés, et utilisés à mauvais escient. Mais sachez que, franchement, j&amp;rsquo;ai trouvé
l&amp;rsquo;analyse assez proche de mon ressenti.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pour information, elle m&amp;rsquo;a même démontré que j&amp;rsquo;avais tort sur un sujet. J&amp;rsquo;ai vérifié son propos, j&amp;rsquo;ai revu le passage
du débat en question et cherché sur le net&amp;hellip; et bon sang ! Elle avait raison.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Je me dis que, avec un peu de travail (de temps, d&amp;rsquo;argent&amp;hellip;) ce genre de travaux pourraient aider les analystes, et à
terme aider des gens à faire leur choix. Mais pour cela, il faudrait fiabiliser et rendre plus factuel les réponses. Et
malheureusement, c&amp;rsquo;est à ce jour une tâche trop lourde pour le temps que j&amp;rsquo;ai à disposition.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Je n&amp;rsquo;ai pas passé beaucoup de temps à coder tout ça. J&amp;rsquo;ai dû passer, en gros, 2 heures de mon temps, le soir, au lieu de
regarder un film. Donc, &lt;strong&gt;évidemment que le résultat n&amp;rsquo;est pas parfait, loin de là&lt;/strong&gt;. Cependant, cela montre à quel point
nous avons sous la main des outils d&amp;rsquo;une puissance incroyable.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est avant tout un test, un amusement. Impossible à ce jour, avec le matériel que j&amp;rsquo;ai à disposition et surtout le
manque de temps que je peux consacrer à ces travaux, de faire un travail plus abouti.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai déjà travaillé sur d&amp;rsquo;autres implémentations RAG avant cela, et ce fut souvent plus précis, plus pertinent. Pour cet
exemple, le résultat est moins fiable à cause de nombre de transformations à effectuer sur les données qui conduisent
forcément à un paquet d&amp;rsquo;erreurs. Il faudrait relire, corriger, adapter…&lt;/p&gt;
&lt;p&gt;Mais une chose est certaine : la transcription automatique avec &lt;code&gt;pyannote&lt;/code&gt; et &lt;code&gt;whisper&lt;/code&gt; combinée, afin de détecter
l&amp;rsquo;interlocuteur et ses propos fonctionne plutôt bien. &lt;code&gt;whisper-openai&lt;/code&gt; est clairement un bel outil qui fonctionne vite
et bien. &lt;code&gt;llama-index&lt;/code&gt; est un outil qui permet de faire de la recherche sémantique plus simplement, et simplifie le
requêtage des modèles LLM.&lt;/p&gt;
&lt;p&gt;ChromaDB et &lt;code&gt;llama-index&lt;/code&gt; se marient bien. D&amp;rsquo;ailleurs, ayant aussi testé le RAG avec des PDF, en utilisant les plugins
intégrés à &lt;code&gt;llama-index&lt;/code&gt;, je vous assure que cela donne de très bons résultats.&lt;/p&gt;
&lt;p&gt;Le RAG, c&amp;rsquo;est clairement l&amp;rsquo;avenir de l&amp;rsquo;IA dans beaucoup de domaines.&lt;/p&gt;
&lt;p&gt;Tout ce que je vous ai montré utilise des outils opensource. Merci encore à la communauté.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Il serait temps que les librairies de Machine Learning suivent un peu le besoin et passent à OpenCL ou Vulkan.
Pour l&amp;rsquo;heure, seul LLamaCPP propose de changer de backend facilement.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;j&amp;rsquo;ai un bébé, un travail, pas mal de fatigue.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>Apt, Dnf, Utilisons PackageKit</title>
      <link>https://www.metal3d.org/blog/2024/apt-dnf-utilisons-packagekit/</link>
      <pubDate>Mon, 15 Apr 2024 23:19:03 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2024/apt-dnf-utilisons-packagekit/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2024/apt-dnf-utilisons-packagekit/cover.svg&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Si vous vous prenez la tête à toujours proposer des commandes &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;dnf&lt;/code&gt;, &lt;code&gt;zypper&lt;/code&gt;, &lt;code&gt;pacman&lt;/code&gt; ou &lt;code&gt;emerge&lt;/code&gt; dans vos articles, vos scripts, Makefile ou autres, on va discuter de PackageKit.&lt;/p&gt;
&lt;p&gt;Je suis le premier à faire ce genre de truc :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je fais un article, je veux que les gens puissent installer un outil, un logiciel, blablabla,&lt;/li&gt;
&lt;li&gt;je suis sur Fedora, donc je connais le package à installer avec &lt;code&gt;dnf&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;et je me dis &amp;ldquo;ha zut, il y a des gens sur Ubuntu, Debian, Arch Linux, openSUSE, Gentoo, etc.&amp;rdquo;,&lt;/li&gt;
&lt;li&gt;donc je me dis que, par respect, je vais leur proposer une ligne de commande pour &amp;ldquo;la plupart des distributions&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et, à la fin, soit je me trompe de nom de package, soit le package a différentes variantes, soit j&amp;rsquo;oublie une distribution. Et en plus, je me démarre un conteneur (avec Podman) pour vérifier.&lt;/p&gt;
&lt;p&gt;Idem quand je veux créer un script d&amp;rsquo;installation pour des utilisateurs finaux, ou un Makefile pour un truc général&amp;hellip; je dois détecter la distribution, et proposer des commandes différentes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et pourtant, je le sais hein, je le sais que &lt;a href=&#34;https://www.freedesktop.org/software/PackageKit/&#34;&gt;PackageKit&lt;/a&gt; existe, et que c&amp;rsquo;est fait pour ça.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et j&amp;rsquo;oublie de l&amp;rsquo;utiliser. Donc, je vais vous en parler, et vous expliquer pourquoi je vais commencer à l&amp;rsquo;utiliser plus fréquemment. Je vais aussi vous dire quand ne pas l&amp;rsquo;utiliser.&lt;/p&gt;
&lt;h2 id=&#34;dabord-cest-quoi-ça-&#34;&gt;D&amp;rsquo;abord, c&amp;rsquo;est quoi ça ?&lt;/h2&gt;
&lt;p&gt;PackageKit est un système de gestion de paquets qui abstrait les détails des gestionnaires de paquets. Il permet de proposer une interface commune pour installer, mettre à jour, supprimer des paquets, et ce, quelle que soit la distribution Linux utilisée.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Oui, c&amp;rsquo;est &amp;ldquo;universel&amp;rdquo; (enfin, presque).&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;En fait, &lt;a href=&#34;https://www.freedesktop.org/software/PackageKit/pk-users.html&#34;&gt;&amp;ldquo;Gnome Software&amp;rdquo;, &amp;ldquo;Discover&amp;rdquo;, &amp;ldquo;Apper&amp;rdquo;, etc.&lt;/a&gt; utilisent PackageKit pour gérer les paquets. Il y a que les vieux barbus dans mon genre, ou les vieilles barbues (ouais, inclusion, tout ça) qui utilisent encore les commandes &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;dnf&lt;/code&gt;, &lt;code&gt;zypper&lt;/code&gt;, &lt;code&gt;pacman&lt;/code&gt; ou &lt;code&gt;emerge&lt;/code&gt; directement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bon, attendez&amp;hellip; on va se calmer.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Certes, PackageKit est un outil pratique, qui installe proprement les packages, et qui n&amp;rsquo;entre pas en conflit avec votre gestionnaire de paquet habituel (parce qu&amp;rsquo;il l&amp;rsquo;utilise). Le truc, c&amp;rsquo;est que c&amp;rsquo;est un outil &amp;ldquo;haut niveau&amp;rdquo;, qui ne permet pas de faire des choses spécifiques à une distribution.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est un outil, comment dire, &amp;ldquo;bureautique&amp;rdquo; ? Si vous devez faire un truc plus technique, avec des options particulières ou adapté à une distribution spécifique, vous devrez utiliser les commandes spécifiques à la distribution. Donc, ne pas utiliser PackageKit mais &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;dnf&lt;/code&gt;, &lt;code&gt;zypper&lt;/code&gt;, &lt;code&gt;pacman&lt;/code&gt; ou &lt;code&gt;emerge&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Par contre, si vous êtes un rédacteur d&amp;rsquo;article, un développeur d&amp;rsquo;application qui demande d&amp;rsquo;ajouter des packages ou un éditeur qui est parfaitement au courant que son outil est dans les dépôts : &lt;strong&gt;PackageKit&lt;/strong&gt;. Sérieux, faites un effort, il faut juste remplacer le mot &amp;ldquo;apt&amp;rdquo; par &amp;ldquo;pkcon&amp;rdquo;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Parce que, m*@!#* à la fin ! J&amp;rsquo;en ai plus que marre de voir des gens n&amp;rsquo;écrire des commandes que pour Ubuntu/Debian et se foutre royalement des autres distributions. Je suis utilisateur de Fedora et je fais l&amp;rsquo;effort de toujours proposer des commandes &lt;code&gt;apt&lt;/code&gt; et &lt;code&gt;dnf&lt;/code&gt; - c&amp;rsquo;est le minimum syndical.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais même en faisant cet effort, je l&amp;rsquo;avoue, j&amp;rsquo;oublie Arch Linux, openSUSE, Gentoo, etc. Et je ne parle même pas des distributions plus exotiques.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Va falloir que ça devienne un réflexe.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Donc : &lt;strong&gt;PackageKit&lt;/strong&gt; et la commande &lt;code&gt;pkcon&lt;/code&gt; !&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;en-mode-graphique-&#34;&gt;En mode graphique ?&lt;/h2&gt;
&lt;p&gt;Eh bien si vous avez un environnement de bureau, vous avez sûrement un gestionnaire de paquets qui utilise PackageKit. Par exemple, sous Fedora, vous avez &amp;ldquo;Gnome Software&amp;rdquo;. Sous Ubuntu, vous avez &amp;ldquo;Discover&amp;rdquo;. Sous openSUSE, vous avez &amp;ldquo;Apper&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Tous utilisent déjà (en partie ou totalement) PackageKit pour gérer les paquets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ce que j&amp;rsquo;essaie de vous faire comprendre, c&amp;rsquo;est que si vous avez utilisé un gestionnaire de paquets graphique, vous avez déjà utilisé PackageKit.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais ce qui est cool, c&amp;rsquo;est que vous pouvez l&amp;rsquo;utiliser en ligne de commande, et ça, peu de gens le font.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;packagekit-en-ligne-de-commande&#34;&gt;PackageKit en ligne de commande&lt;/h2&gt;
&lt;p&gt;Linux, ça s&amp;rsquo;utilise graphiquement et c&amp;rsquo;est bien. Mais je ne connais pas un utilisateur qui, après peu de temps, n&amp;rsquo;ouvre pas un terminal pour utiliser son ordinateur plus &amp;ldquo;rapidement&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;La ligne de commande principale, c&amp;rsquo;est &amp;ldquo;&lt;code&gt;pkcon&lt;/code&gt;&amp;rdquo;. Suivi d&amp;rsquo;une &amp;ldquo;commande&amp;rdquo; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pkcon install &amp;lt;package&amp;gt;&lt;/code&gt; pour installer un paquet&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkcon remove &amp;lt;package&amp;gt;&lt;/code&gt; pour supprimer un paquet&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkcon search &amp;lt;package&amp;gt;&lt;/code&gt; pour rechercher un paquet&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pkcon info &amp;lt;package&amp;gt;&lt;/code&gt; pour obtenir des informations sur un paquet&lt;/li&gt;
&lt;li&gt;et d&amp;rsquo;autres&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Ce qui est cool, c&amp;rsquo;est qu&amp;rsquo;Ubuntu, Fedora, openSUSE, Arch Linux, etc. ont tous PackageKit d&amp;rsquo;installé par défaut. La commande : &lt;code&gt;pkcon&lt;/code&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Je veux installer, par exemple, &amp;ldquo;Firefox&amp;rdquo;. Je vais donc utiliser la commande &lt;code&gt;pkcon install firefox&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pkcon install firefox

Résolution                   [=========================]         
Chargement du cache           [=========================]         
Test des changements          [=========================]         
Terminé                      [                         ] (0%)  
Les paquets suivants doivent être installés :
 firefox-124.0.2-2.fc39.x86_64	Mozilla Firefox Web browser
 firefox-langpacks-124.0.2-2.fc39.x86_64	Firefox langpacks
 mozilla-openh264-2.4.0-2.fc39.x86_64	H.264 codec support for Mozilla browsers
Continuer avec ces changements ? [N/y] y

                             [=========================]         
Installation                  [=========================]         
Requête                      [=========================]         
Téléchargement des paquets  [=========================]         
Test des changements          [=========================]         
Installation des paquets      [=========================]         
Terminé                      [=========================]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Vous avez remarqué ? Pas besoin de &lt;code&gt;sudo&lt;/code&gt; pour installer un paquet. PackageKit va demander les droits administrateurs si nécessaire. C&amp;rsquo;est tellement plus pratique&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Par exemple, j&amp;rsquo;ai demandé une réparation (inutile sur mon système, mais pour vous montrer&amp;hellip;), j&amp;rsquo;ai donc tapé &lt;code&gt;pkcon repair&lt;/code&gt; et le système m&amp;rsquo;a demandé les droits administrateurs :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/apt-dnf-utilisons-packagekit/pkcon-password.webp&#34; alt=&#34;pkcon repair&#34;&gt;&lt;/p&gt;
&lt;p&gt;Intéressant, car il me dit ce qu&amp;rsquo;il va faire, en l&amp;rsquo;occurrence, réparer les paquets cassés. Je peux donc annuler ou accepter avec mon mot de passe. Voilà, ça, c&amp;rsquo;est bien !&lt;/p&gt;
&lt;p&gt;Et si je veux supprimer &amp;ldquo;Firefox&amp;rdquo;, je vais utiliser la commande &lt;code&gt;pkcon remove firefox&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pkcon remove firefox
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais PackageKit c&amp;rsquo;est aussi l&amp;rsquo;outil qui sait mettre à jour votre système.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pkcon get-updates

Obtention des mises à jour   [=========================]         
Attente dans la file          [=========================]         
Démarrage                    [=========================]         
Terminé                      [=========================]         
Disponible  	act-cli-0.2.61-1.fc39.x86_64 (copr:copr.fedorainfracloud.org:rubemlrm:act-cli)	Run your github workflows locally
Amélioration	alsa-sof-firmware-2024.03-2.fc39.noarch (updates)           	Firmware and topology files for Sound Open Firmware project
Bug fix     	amd-gpu-firmware-20240410-1.fc39.noarch (updates)           	Firmware for AMD GPUs
Bug fix     	amd-ucode-firmware-20240410-1.fc39.noarch (updates)         	Microcode updates for AMD CPUs
Normale     	annobin-docs-12.46-1.fc39.noarch (updates)                  	Documentation and shell scripts for use with annobin
Normale     	annobin-plugin-gcc-12.46-1.fc39.x86_64 (updates)            	annobin gcc plugin
Bug fix     	atheros-firmware-20240410-1.fc39.noarch (updates)           	Firmware for Qualcomm Atheros WiFi/Bluetooth adapters
Amélioration	b43-fwcutter-019-36.fc39.x86_64 (updates)                   	Firmware extraction tool for Broadcom wireless driver
Bug fix     	bluez-5.73-3.fc39.x86_64 (updates)                          	Bluetooth utilities
Bug fix     	bluez-cups-5.73-3.fc39.x86_64 (updates)                     	CUPS printer backend for Bluetooth printers
Bug fix     	bluez-libs-5.73-3.fc39.i686 (updates)                       	Libraries for use in Bluetooth applications
Bug fix     	bluez-libs-5.73-3.fc39.x86_64 (updates)                     	Libraries for use in Bluetooth applications
Bug fix     	bluez-obexd-5.73-3.fc39.x86_64 (updates)                    	Object Exchange daemon for sharing content
#...
#...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et par conséquent, la commande &lt;code&gt;pkcon update&lt;/code&gt; va mettre à jour les paquets. Voilà, simple, efficace, et &amp;ldquo;universel&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Je vous invite à lire la &lt;a href=&#34;https://www.freedesktop.org/software/PackageKit/pk-using.html&#34;&gt;documentation&lt;/a&gt; pour plus d&amp;rsquo;informations.&lt;/p&gt;
&lt;h2 id=&#34;mais-ça-marche-comment-&#34;&gt;Mais ça marche comment ?&lt;/h2&gt;
&lt;p&gt;PackageKit utilise des &amp;ldquo;backends&amp;rdquo; pour communiquer avec les gestionnaires de paquets. Un &amp;ldquo;daemon&amp;rdquo; nommé &lt;code&gt;packagekitd&lt;/code&gt; tourne en tâche de fond le temps nécessaire à la recherche et à l&amp;rsquo;installation et des &amp;ldquo;backends&amp;rdquo; sont utilisés pour communiquer avec les gestionnaires de paquets.&lt;/p&gt;
&lt;p&gt;Par exemple, le backend &lt;code&gt;dnf&lt;/code&gt; pour Fedora, le backend &lt;code&gt;apt&lt;/code&gt; pour Debian, le backend &lt;code&gt;zypp&lt;/code&gt; pour openSUSE, etc.&lt;/p&gt;
&lt;p&gt;Pour les développeurs, sachez que vous pouvez communiquer avec PackageKit via D-Bus, et que vous pouvez utiliser des bindings pour Python, C, Go, etc.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est donc un système très modulaire, et qui permet de gérer les paquets de manière uniforme quelle que soit la distribution Linux utilisée.&lt;/p&gt;
&lt;p&gt;Mais, le revers de la médaille, c&amp;rsquo;est justement que cela utiliser D-Bus, &lt;strong&gt;donc que ce n&amp;rsquo;est pas viable pour, par exemple, des conteneurs ou des images OCI (Docker, Podman&amp;hellip;)&lt;/strong&gt;. C&amp;rsquo;est un outil &amp;ldquo;haut niveau&amp;rdquo; pour des utilisateurs finaux.&lt;/p&gt;
&lt;p&gt;Par exemple, dans un conteneur, voilà ce qu&amp;rsquo;il se passe :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ pkcon install firefox
Failed to contact PackageKit: Could not connect: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vraiment, je vais vous le répéter, &lt;code&gt;pkcon&lt;/code&gt;, c&amp;rsquo;est pour vos &lt;strong&gt;postes de travail&lt;/strong&gt;, pas pour vos serveurs ou vos conteneurs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais, soyons honnêtes, vous, moi, on est des utilisateurs finaux, non ? Qu&amp;rsquo;on soit développeur, administrateur système, gamer, utilisateur bureautique, on est des utilisateurs finaux.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Donc, personnellement, je commence à prendre l&amp;rsquo;habitude d&amp;rsquo;utiliser &lt;code&gt;pkcon&lt;/code&gt; dans mes scripts d&amp;rsquo;installation si, et seulement si, on parle de script pour des utilisateurs finaux.&lt;/p&gt;
&lt;h2 id=&#34;mais-du-coup-on-remplace-tout-par-pkcon-&#34;&gt;Mais du coup, on remplace tout par &lt;code&gt;pkcon&lt;/code&gt; ?&lt;/h2&gt;
&lt;p&gt;Alors non.&lt;/p&gt;
&lt;p&gt;Déjà, comme je vous l&amp;rsquo;ai dit, cela ne marche pas sur des conteneurs (et tant mieux hein, on ne va pas démarrer un D-Bus pour installer des packages !).&lt;/p&gt;
&lt;p&gt;Ensuite, PackageKit n&amp;rsquo;a pas toutes les options des gestionnaires de paquets.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est, pour le moment, une commande pratique et un backend pour des outils graphiques. C&amp;rsquo;est donc très sympa pour généraliser l&amp;rsquo;installation d&amp;rsquo;outils si vous visez plusieurs utilisateurs sur plusieurs distributions. Voire pour FreeBSD. C&amp;rsquo;est aussi super bien si vous créer un outil de gestion d&amp;rsquo;installation, vous vous connectez au D-Bus et vous installez le nécessaire.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Donc, amis développeurs qui créent des &amp;ldquo;wizzard&amp;rdquo; d&amp;rsquo;installation, il serait temps d&amp;rsquo;arrêter de faire des &lt;code&gt;apt&lt;/code&gt; dans un processus - ça fait péter un câble aux utilisateurs de Fedora, ArchLinux, openSUSE, etc.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Cependant, les gestionnaires de paquets propres aux distributions ont bien plus d&amp;rsquo;options, des sources de packages parfois spécifiques et des fonctionnalités qui ne sont pas disponibles via PackageKit. Donc, si vous avez besoin de fonctionnalités spécifiques, ou si vous avez besoin de gérer des paquets spécifiques à une distribution, vous devrez utiliser les commandes spécifiques à la distribution.&lt;/p&gt;
&lt;p&gt;Vraiment, &lt;strong&gt;PackageKit c&amp;rsquo;est pour simplifier et généraliser, pas pour remplacer&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;au-final&#34;&gt;Au final&lt;/h2&gt;
&lt;p&gt;Bien que ce soit un outil pour simplifier l&amp;rsquo;installation et qui n&amp;rsquo;a pas la finesse du &amp;ldquo;vrai&amp;rdquo; gestionnaire de paquet utilisé en direct, je commence à pousser son utilisation dans mes Makefile, scripts d&amp;rsquo;installation, articles, etc.&lt;/p&gt;
&lt;p&gt;Pour deux raisons :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je n&amp;rsquo;aime pas utiliser &lt;code&gt;sudo&lt;/code&gt; dans un Makefile, je préfère l&amp;rsquo;interface proposée par PackageKit qui explique à l&amp;rsquo;utilisateur ce qu&amp;rsquo;il va se passer&lt;/li&gt;
&lt;li&gt;je veux me soulager de la charge de devoir proposer des commandes pour chaque distribution Linux&lt;/li&gt;
&lt;li&gt;je pense que c&amp;rsquo;est potentiellement plus adapté si la cible est un utilisateur final, même dans un terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On devrait toujours avoir le réflexe de parler de &lt;code&gt;pkcon&lt;/code&gt; avant de parler de parler des outils spécifiques à une distribution. J&amp;rsquo;ai un avis vraiment à contre-courant de beaucoup de spécialistes, et je l&amp;rsquo;assume complètement. Je sais, et je l&amp;rsquo;ai dit juste avant, que &lt;code&gt;dnf&lt;/code&gt; ou &lt;code&gt;apt&lt;/code&gt; (pour ne citer qu&amp;rsquo;eux) sont des bien meilleurs outils d&amp;rsquo;installation. Je le répète donc, une fois de plus, &lt;strong&gt;PackageKit c&amp;rsquo;est pour simplifier&lt;/strong&gt; et &lt;strong&gt;généraliser&lt;/strong&gt;. Je le propose(rai) dans mes articles pour éviter de discriminer les utilisateurs de distributions Linux. Et je vous invite à en faire autant.&lt;/p&gt;
&lt;p&gt;Mais, il est évident que, quotidiennement, j&amp;rsquo;utilise &lt;code&gt;dnf&lt;/code&gt; sur mes postes Fedora, et que &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;apk&lt;/code&gt;, etc. sur les autres distributions que j&amp;rsquo;utilise essentiellement dans des conteneurs ou sur les serveurs au travail (ouais, pas moyen de faire plier les admins systèmes, ils veulent du Debian &amp;#x1f626;).&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Mason, comment installer facilement des LSP, des linters, des formatteurs... sur NeoVim</title>
      <link>https://www.metal3d.org/blog/2024/mason-installer-facilement-des-lsp-sur-neovim/</link>
      <pubDate>Fri, 12 Apr 2024 06:13:53 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2024/mason-installer-facilement-des-lsp-sur-neovim/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2024/mason-installer-facilement-des-lsp-sur-neovim/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;NeoVim, le Vim du futur, sait parfaitement gérer des LSP (Language Server Protocol), des linters, des formateurs, des snippets, etc. Avec CoC, on est à deux doigts d&amp;rsquo;avoir un VSCode en mode terminal. Mais installer un LSP (Language Server Protocol), demandait de faire deux ou trois choses manuellement pour chaque langage. C&amp;rsquo;était sans compter sur &amp;ldquo;Mason&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/williamboman/mason.nvim&#34;&gt;Mason&lt;/a&gt; est un utilitaire simple et proprement pensé, avec un &lt;a href=&#34;https://mason-registry.dev/registry/list&#34;&gt;registre de langages&lt;/a&gt; bien fourni, qui permet d&amp;rsquo;installer facilement des LSP, des linters, des formateurs, des snippets, etc. pour NeoVim.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je vous l&amp;rsquo;avoue, j&amp;rsquo;ai eu un peu de mal à comprendre comment on l&amp;rsquo;initialise, d&amp;rsquo;où ce billet. Mais dans les faits, c&amp;rsquo;est super simple.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;/h2&gt;
&lt;p&gt;Il faut installer le plugin dans NeoVim. Personnellement, j&amp;rsquo;utilise &lt;a href=&#34;https://github.com/junegunn/vim-plug&#34;&gt;vim-plug&lt;/a&gt;. C&amp;rsquo;est simple et efficace, ça s&amp;rsquo;installe en deux temps trois mouvements. Et je n&amp;rsquo;ai qu&amp;rsquo;à ajouter une ligne dans mon fichier de configuration pour que le plugin soit pris en compte.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-lua&#34;&gt;Plug &#39;williamboman/mason.nvim&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On doit alors taper &lt;code&gt;:PlugInstall&lt;/code&gt; pour installer le plugin.&lt;/p&gt;
&lt;p&gt;Ensuite, il faut initialiser Mason. Pour cela, il faut ajouter une ligne dans votre fichier de configuration, plus bas, en fin de configuration si vous le voulez.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-lua&#34;&gt;lua require(&#39;mason&#39;).setup()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est cette ligne qui n&amp;rsquo;est pas dans la documentation (je suis étonné, et j&amp;rsquo;ai ouvert &lt;a href=&#34;https://github.com/williamboman/mason.nvim/issues/1679&#34;&gt;cette issue&lt;/a&gt; pour le signaler).&lt;/p&gt;
&lt;p&gt;À partir de là, le chemin d&amp;rsquo;installation des LSP et autres outils est &lt;code&gt;~/.local/share/nvim/mason/bin/&lt;/code&gt; &lt;strong&gt;au sein de NeoVim&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ce qui a plusieurs avantages :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;D&amp;rsquo;abord, cela ne va pas polluer votre &amp;ldquo;PATH&amp;rdquo; dans les terminaux. Par exemple quand je tape &amp;ldquo;&lt;code&gt;helm&lt;/code&gt;&amp;rdquo; en ligne de commande, je ne veux pas voir &lt;code&gt;helm_ls&lt;/code&gt; apparaître dans la completion&lt;/li&gt;
&lt;li&gt;D&amp;rsquo;autre part, cela ne pollue pas le système. Les LSP sont dans un répertoire personnel, facile à dégager, géré par Mason et c&amp;rsquo;est tout.&lt;/li&gt;
&lt;li&gt;Et le fait que le PATH soit &amp;ldquo;local à neovim&amp;rdquo; fait que vous n&amp;rsquo;avez pas à spécifier le chemin complet des exécutables dans vos configurations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En bref, c&amp;rsquo;est déjà bien pensé.&lt;/p&gt;
&lt;h2 id=&#34;installer-un-lsp&#34;&gt;Installer un LSP&lt;/h2&gt;
&lt;p&gt;Reste à installer un LSP. Prenons par exemple un LSP pour helm.&lt;/p&gt;
&lt;p&gt;Ouvrez NeoVim et tapez &lt;code&gt;:MasonInstall helm-ls&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Va alors s&amp;rsquo;ouvrir cette fenêtre (fermez en pressant la touche &amp;ldquo;Echap&amp;rdquo;) :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/mason-installer-facilement-des-lsp-sur-neovim/mason-install.webp&#34; alt=&#34;Installation d&amp;rsquo;un LSP avec Mason&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et voilà, c&amp;rsquo;est tout. Mason va télécharger le LSP, l&amp;rsquo;installer dans le répertoire &lt;code&gt;~/.local/share/nvim/mason/bin/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pour que NeoVim sache ce qu&amp;rsquo;est un fichier &lt;code&gt;helm&lt;/code&gt; (on parle du &amp;ldquo;type de fichier&amp;rdquo;, parce que c&amp;rsquo;est un fichier YAML mais avec des balises de template, et que NeoVim ne peut pas le comprendre tout seul), il faut ajouter un plugin (et on n&amp;rsquo;oublie pas de taper &lt;code&gt;:PlugInstall&lt;/code&gt; pour l&amp;rsquo;installer) :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-lua&#34;&gt;Plug &#39;towolf/vim-helm&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez ensuite définir comment l&amp;rsquo;utiliser. Moi, j&amp;rsquo;utilise le plugin &amp;ldquo;Coc&amp;rdquo; pour des milliers de raisons. Et une des raisons est que je peux utiliser des LSP sans me prendre la tête. Il suffit de taper &lt;code&gt;:CocConfig&lt;/code&gt; et d&amp;rsquo;ajouter une ligne dans le fichier de configuration de CoC.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{
  &amp;quot;languageserver&amp;quot;: {
    &amp;quot;helm&amp;quot;: {
      &amp;quot;command&amp;quot;: &amp;quot;helm_ls&amp;quot;,
      &amp;quot;args&amp;quot;: [&amp;quot;serve&amp;quot;],
      &amp;quot;filetypes&amp;quot;: [&amp;quot;helm&amp;quot;],
      &amp;quot;rootPatterns&amp;quot;: [&amp;quot;Chart.yaml&amp;quot;],
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà, vous avez un LSP pour helm. Il va vous engueuler quand vous faites des erreurs, vous donner des suggestions, autocompléter les valeurs en prenant en compte le fichier de &amp;ldquo;Values&amp;rdquo;, etc.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;De mon point de vue, NeoVim est quasi parfait avec une configuration simple :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nerdtree pour la navigation &lt;a href=&#34;https://github.com/preservim/nerdtree&#34;&gt;https://github.com/preservim/nerdtree&lt;/a&gt; ainsi que quelques &amp;ldquo;plugins&amp;rdquo; pour avoir les icônes, la couleur, l&amp;rsquo;état &amp;ldquo;Git&amp;rdquo;&amp;hellip;&lt;/li&gt;
&lt;li&gt;CoC pour les plugins et les LSP &lt;a href=&#34;https://github.com/neoclide/coc.nvim&#34;&gt;https://github.com/neoclide/coc.nvim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Mason pour installer les LSP &lt;a href=&#34;https://github.com/williamboman/mason.nvim&#34;&gt;https://github.com/williamboman/mason.nvim&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;ai quelques autres plugins, pour la correction grammaticale (ce qui ne m&amp;rsquo;empêche pas d&amp;rsquo;en laisser passer, je suis vraiment pas rigoureux sur ce point), pour les snippets, j&amp;rsquo;ai aussi Copilot (parce que bon sang, c&amp;rsquo;est pratique), etc. Vraiment, NeoVim permet d&amp;rsquo;avoir un IDE complet.&lt;/p&gt;
&lt;p&gt;Avec quelques astuces voilà à quoi ressemble &lt;strong&gt;mon&lt;/strong&gt; NeoVim, dans mon terminal :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/mason-installer-facilement-des-lsp-sur-neovim/neovim.webp&#34; alt=&#34;Mon NeoVim&#34;&gt;&lt;/p&gt;
&lt;p&gt;Je pense qu&amp;rsquo;un jour, je ferai un billet sur ma configuration NeoVim, voir une vidéo&amp;hellip;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>BleachBit en mode Automatique</title>
      <link>https://www.metal3d.org/blog/2024/bleachbit-automatique/</link>
      <pubDate>Fri, 05 Apr 2024 23:26:52 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2024/bleachbit-automatique/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2024/bleachbit-automatique/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;BleachBit est un outil fantastique pour nettoyer votre système. Mais, bien que l&amp;rsquo;interface graphique
soit très bien pensée, j&amp;rsquo;avais envie d&amp;rsquo;automatiser le processus. Et comme on est sous Linux, évidemment,
ça se fait super facilement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je vous préviens d&amp;rsquo;avance, c&amp;rsquo;est un article assez simple, rien de techniquement innovant. Mais je vois tellement de gens faire les choses un peu n&amp;rsquo;importe comment, que je me suis dit que ça valait le coup de l&amp;rsquo;écrire. Aucune animosité de ma part, enfin&amp;hellip; si&amp;hellip; mais c&amp;rsquo;est bon enfant. Ne le prenez pas mal &amp;#x1f604;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Cet article est un prétexte pour vous parler de systemd, et notamment les timers, en mode &amp;ldquo;utilisateur&amp;rdquo; (donc, sans &lt;code&gt;sudo&lt;/code&gt;, sans taper dans le système, sans prendre des risques de tout péter). Mais je veux aussi mettre en lumière BleachBit, un outil que j&amp;rsquo;utilise depuis des années et qui m&amp;rsquo;a toujours rendu de fiers services. Le fait de pouvoir nettoyer son système aussi efficacement, et de pouvoir automatiser le processus, ça m&amp;rsquo;a paru une bonne idée de vous montrer &amp;ldquo;ze way to do it&amp;rdquo; (en mode &amp;ldquo;je fais ça bien&amp;rdquo;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Non parce que je suis généralement en tain de grogner devant mon écran en lisant certains articles, rédigés par des gorets qui adorent claquer du &lt;code&gt;sudo&lt;/code&gt; partout. Alors que non, on ne fait pas ça. On est des gens bien, on ne touche pas au système quand il ne faut pas, on ne le casse pas, on ne le corrompt pas. &lt;strong&gt;Arrêtez de faire ça&lt;/strong&gt;. Sudo, c&amp;rsquo;est pour les opérations qui nécessitent des droits d&amp;rsquo;administration. Point barre.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installer un paquet: OUI&lt;/li&gt;
&lt;li&gt;Ajouter un timer pour un truc perso sur mon &amp;ldquo;home&amp;rdquo;: NON&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sérieux, arrêtez de faire ça. C&amp;rsquo;est pas bien.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Parlons de BleachBit, que j&amp;rsquo;utilise depuis des années.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai très souvent des problèmes de place sur mon disque dur. J&amp;rsquo;ai beau avoir 500 Go sur le laptop et utiliser des disques distants pour stocker mes données, je me retrouve souvent à court d&amp;rsquo;espace. C&amp;rsquo;est moins le cas sur mon poste &amp;ldquo;desktop&amp;rdquo; qui, lui, à 2To de stockage. Mais voilà, j&amp;rsquo;utilise mon laptop, je télécharge des modèles d&amp;rsquo;IA, je teste, je code, je fais un peu de Blender&amp;hellip; En quelques jours, voire quelques semaines si je me calme, j&amp;rsquo;atteins 80% d&amp;rsquo;occupation du disque et ça m&amp;rsquo;énerve. Et BleachBit me règle ça en quelques secondes.&lt;/p&gt;
&lt;p&gt;BleachBit est développé en Python et vous trouverez la page officielle &lt;a href=&#34;https://www.bleachbit.org/&#34;&gt;ici&lt;/a&gt;. Et les sources se trouvent sur leur GitHub &lt;a href=&#34;https://github.com/bleachbit/bleachbit&#34;&gt;ici&lt;/a&gt;. Cet outil est une vraie pépite, et je vous encourage à le tester.&lt;/p&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;/h2&gt;
&lt;p&gt;Bon, pour commencer, il faut installer BleachBit. C&amp;rsquo;est un logiciel libre, et vous pouvez le trouver dans les dépôts de votre distribution. Par exemple, pour Ubuntu, ça se fait avec la commande suivante :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo apt install bleachbit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais comme vous êtes des gens bien, vous utilisez Fedora n&amp;rsquo;est-ce pas ? Dans ce cas, vous pouvez installer BleachBit avec la commande suivante :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo dnf install bleachbit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, passons à la suite&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;premier-nettoyage&#34;&gt;Premier nettoyage&lt;/h2&gt;
&lt;p&gt;On va le faire avec l&amp;rsquo;interface graphique pour commencer. Il y a une raison pour cela : quand vous modifiez les options de nettoyage, BleachBit enregistre ces options dans un fichier de configuration. Vous le trouverez dans &lt;code&gt;~/.config/bleachbit/bleachbit.ini&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pourquoi je fais ça ? Eh bien en fait on pourrait très bien se passer de cette étape. Il faudrait alors utiliser la ligne de commande pour lancer le nettoyage avec un tas de &amp;ldquo;cleaners&amp;rdquo; dont il faut connaitre non seulement le nom, mais aussi ce que ça fait. Et franchement j&amp;rsquo;avais la flème.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;UI vous donne plein d&amp;rsquo;information sur chaque &amp;ldquo;cleaner&amp;rdquo;. Alors je vous propose de sélectionner :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pour tous les navigateurs que vous avez installés (firefox, chromium, brave, etc.), de virer le cache, et optimiser les bases de données&lt;/li&gt;
&lt;li&gt;dans la section &amp;ldquo;système&amp;rdquo;, vider le cache et les fichier temporaires. Aussi, de supprimer les &amp;ldquo;fichier corrompus&amp;rdquo; et la corbeille.&lt;/li&gt;
&lt;li&gt;et ensuite de faire le nécessaire pour les applications que vous utilisez. Lisez bien ce que ça fait (histoire de ne pas perdre un truc important, comme les mots de passe de votre navigateur par exemple).&lt;/li&gt;
&lt;li&gt;Dans analyse approfondie, j&amp;rsquo;ai personnellement coché la suppression des fichier de sauvegarde et les fichiers temporaires.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Bien entendu, ce sont mes choix, à vous de voir ce que vous voulez nettoyer. Faites juste attention à ne pas supprimer des trucs importants, j&amp;rsquo;insiste.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Aussi, dans les paramètres, j&amp;rsquo;ai ajouté des répertoires que je veux vraiment ne pas nettoyer. Par exemple, j&amp;rsquo;ai ajouté &amp;ldquo;&lt;code&gt;~/.cache/oh-my-posh/&lt;/code&gt;&amp;rdquo; (pour ne pas perdre la configuration de mon terminal qui stocke les thèmes dedans)&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Bon, normalement, vous pouvez &amp;ldquo;prévisualiser&amp;rdquo; le nettoyage pour voir ce qui va être supprimé. Si vous êtes sûr de vous, vous pouvez lancer le nettoyage.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;La première fois, j&amp;rsquo;ai récupéré près de 120Go d&amp;rsquo;espace disque. C&amp;rsquo;est pas mal&amp;hellip; allez ne faisons pas les blasés, c&amp;rsquo;est énorme !&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;automatisation&#34;&gt;Automatisation&lt;/h2&gt;
&lt;p&gt;Fermez BleachBit et regardez le fichier &lt;code&gt;~/.config/bleachbit/bleachbit.ini&lt;/code&gt;. Vous y trouverez toutes les options que vous avez sélectionnées.&lt;/p&gt;
&lt;p&gt;Chez moi, par exemple, j&amp;rsquo;ai ceci :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;# ... des trucs et puis:

[list/shred_drives]
0 = /home/metal3d/.cache
1 = /tmp

[preserve_languages]
en = True
fr = True

[tree]
brave.cache = True
brave = True
firefox.cache = True
firefox = True
google_chrome.cache = True
google_chrome = True
journald.clean = True
journald = True
system.cache = True
system = True
system.trash = True
thunderbird.cache = True
thunderbird = True
vlc.mru = True
vlc = True
brave.vacuum = True
firefox.vacuum = True
google_chrome.vacuum = True
thunderbird.vacuum = True
system.desktop_entry = True
system.tmp = True
deepscan.backup = True
deepscan = True
deepscan.tmp = True

[whitelist/paths]
0_type = folder
0_path = /home/metal3d/.cache/oh-my-posh/themes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et c&amp;rsquo;est pratique, parce que la ligne de commande suivante va reproduire votre nettoyage :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# pour nettoyer
bleachbit --preset -c

# pour prévisualiser ce que ça va faire... c&#39;est bien de le faire avant
bleachbit --preset -p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On peut donc demander de lancer cette commande tous les lundis matin à 3 h du mat&amp;rsquo; (et au pire, si le PC est éteint, ça se passera au démarrage de votre ordinateur, voir &lt;a href=&#34;https://wiki.archlinux.org/title/systemd/Timers#Realtime_timer&#34;&gt;la documentation&lt;/a&gt; de l&amp;rsquo;option &amp;ldquo;Persistent&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;Alors, non, on ne va pas utiliser le vieux &amp;ldquo;crontab&amp;rdquo;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. Non vraiment je l&amp;rsquo;adore, c&amp;rsquo;est un vieux copain, mais &lt;code&gt;systemctl&lt;/code&gt; est tellement plus adapté. Moi, je ne fais pas partie des détracteurs qui ont menacé de mort l&amp;rsquo;auteur de systemd. J&amp;rsquo;espère d&amp;rsquo;ailleurs que ces crétins aient eu ce qu&amp;rsquo;ils méritaient&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;. Bref, je m&amp;rsquo;égare.&lt;/p&gt;
&lt;p&gt;Le &amp;ldquo;souci&amp;rdquo; c&amp;rsquo;est que, quand on ne le sait pas (mais moi je sais, nananère), systemd est &amp;ldquo;de base&amp;rdquo; un daemon&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt; qui tourne via l&amp;rsquo;utilisateur &amp;ldquo;root&amp;rdquo;. Donc si vous allez claquer des fichiers avec &lt;code&gt;sudo&lt;/code&gt; dans &lt;code&gt;/etc/systemd/system/&lt;/code&gt;, vous devriez tout de suite arrêter ce que vous faites. C&amp;rsquo;est aussi grave que ceux qui vous disent de faire un &lt;code&gt;sudo pip install&lt;/code&gt; (sérieux, arrêtez de faire ça).&lt;/p&gt;
&lt;p&gt;Sauf que systemd est bien pensé, et ouais. Vous avez aussi un espace utilistateur pour y assigner vos &lt;code&gt;services&lt;/code&gt;, &lt;code&gt;timers&lt;/code&gt;, &lt;code&gt;mounts&lt;/code&gt; et &lt;code&gt;sockets&lt;/code&gt;. C&amp;rsquo;est dans &lt;code&gt;~/.config/systemd/user/&lt;/code&gt; que ça se passe. Mais j&amp;rsquo;ai tendance à oublier ce dossier.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;astuce, c&amp;rsquo;est de s&amp;rsquo;en foutre. On va utiliser une ligne de commande pour éditer les deux fichiers nécessaires.&lt;/p&gt;
&lt;p&gt;La première commande :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl --user edit --force --full bleachbit.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bim, un éditeur s&amp;rsquo;ouvre, et vous collez le contenu suivant :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;[Unit]
Description=Clean disk space with BleachBit

[Service]
Type=oneshot
ExecStart=/usr/bin/bleachbit --preset --clean

[Install]
WantedBy=default.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sauvez le fichier, et voilà. Le service existe. Maintenant, on va créer un &amp;ldquo;timer&amp;rdquo; pour lancer ce service tous les lundis matin à 3 h.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl --user edit --force --full bleachbit.timer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et encore un éditeur qui s&amp;rsquo;ouvre !&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;[Unit]
Description=Clean the disk space each monday at 3:00 AM

[Timer]
OnCalendar=Mon *-*-* 03:00:00 Europe/Paris
Persistent=true

[Install]
WantedBy=timers.target
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Le fonctionnement de systemd est d&amp;rsquo;avoir un timer qui porte le même nom que le service. C&amp;rsquo;est pour ça que je vous ai demandé de créer un service &lt;code&gt;bleachbit.service&lt;/code&gt; et un timer &lt;code&gt;bleachbit.timer&lt;/code&gt;. Dans les faits, un &amp;ldquo;timer&amp;rdquo;, execute un &amp;ldquo;service&amp;rdquo; à un temps donné.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Bon et bien allez hop, on les active et voilà !&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl --user enable --now bleachbit.service
systemctl --user enable --now bleachbit.timer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est tout, ça va marcher. Et si vous voulez vérifier que tout est bien configuré, vous pouvez lancer la commande suivante :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# voir l&#39;état des timers
systemctl --user list-timers

# voir les logs du timer
journalctl --user -u bleachbit.timer
journalctl --user -u bleachbit.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et vous pouvez lancer le nettoyage manuellement si vous le voulez:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl --user start bleachbit.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bref, c&amp;rsquo;est vraiment propre, intégré dans votre environnement, c&amp;rsquo;est centralisé (logs avec &lt;code&gt;journalctl&lt;/code&gt;, activation avec &lt;code&gt;systemctl&lt;/code&gt;)&amp;hellip; Ce n&amp;rsquo;est plus du bricolage avec des scripts qui en foutent partout. Propre. Zen.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;BleachBit c&amp;rsquo;est le pied, et systemd c&amp;rsquo;est le pied. Alors les deux ensemble,
bah, c&amp;rsquo;est les deux pieds. C&amp;rsquo;est Jean-Claude Van Damme qui me l&amp;rsquo;a dit.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;En soit ce n&amp;rsquo;est pas grave que je perde ce cache, car je peux le reconfigurer avec une ligne de commande. Mais ça m&amp;rsquo;évite de le faire tous les lundis matin&amp;hellip;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Crontab était supposé être remplacé par systemd, mais il est toujours là. Je pense que les admins système sont attachés à leurs habitudes, et je n&amp;rsquo;ai rien à redire sur le fait que crontab fonctionne comme attendu. C&amp;rsquo;est pratique, c&amp;rsquo;est un standard, et c&amp;rsquo;est bien. Mais systemd a des avantages et je pense qu&amp;rsquo;il faut savoir laisser partir crontab dans le paradis des outils qui ont droit à une mort digne et paisible.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;Voir &lt;a href=&#34;https://www.zdnet.fr/actualites/systemd-et-les-t-du-c-de-la-communaute-open-source-39807395.htm&#34;&gt;ici&lt;/a&gt;, des débiles ont trouvé pertinent de collecter des BitCoins pour engager un tueur à gage&amp;hellip; On parle d&amp;rsquo;un outil informatique, et les gens sont prêts à tuer pour ça. Je ne sais même pas comment je peux commenter ça.&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;Tiens, tu le savais que &amp;ldquo;daemon&amp;rdquo; veut dire &amp;ldquo;Disk And Execution MONitor&amp;rdquo; ?&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>Comment J&#39;ai Généré Un Dataset Avec L&#39;IA</title>
      <link>https://www.metal3d.org/blog/2024/comment-jai-g%C3%A9n%C3%A9r%C3%A9-un-dataset-avec-lia/</link>
      <pubDate>Tue, 12 Mar 2024 22:29:03 +0100</pubDate>
      
      <guid>https://www.metal3d.org/blog/2024/comment-jai-g%C3%A9n%C3%A9r%C3%A9-un-dataset-avec-lia/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2024/comment-jai-g%C3%A9n%C3%A9r%C3%A9-un-dataset-avec-lia/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;J&amp;rsquo;avais besoin d&amp;rsquo;un dataset de produits en JSON, en français, et pas moyen de trouver un truc bien sur le net (gratuitement). Alors je me suis lancé dans une folie : demander à une IA de le faire pour moi. Et &lt;strong&gt;localement !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;EDIT: allez voir &lt;a href=&#34;https://www.metal3d.org/blog/2024/une-ia-chez-soi-bye-bye-chatgpt-/&#34;&gt;le second article&lt;/a&gt;, j&amp;rsquo;ai découvert &amp;ldquo;LLama.cpp&amp;rdquo; et son backend Python, je vous conseille vivement de lire mon test.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si vous être trop pressé, allez en bas de l&amp;rsquo;article pour trouver le lien du code source. Lisez les commentaires en début de script et débrouillez-vous. Utilisez LM Studio pour lancer une API local avec un modèle Mistral 7b hein !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Non parce que bon&amp;hellip; Payer une API OpenAI ou Mistral pour un test perso, ça m&amp;rsquo;énerve un peu. Il fallait bien que trouve une solution.&lt;/p&gt;
&lt;p&gt;e me dis, donc, que ChatGPT 3.5, gratos, va me faire un truc pas mal. Et effectivement en lui demandant ce genre de contenu, il fait le job. Mais voilà :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;D&amp;rsquo;abord, il répond gentiment avant de me foutre un bloc Markdown. Il faudrait que je clique sur le bouton &amp;ldquo;copier&amp;rdquo;, puis aller coller ça dans un fichier. Pour 1000 produits, ça va être long&amp;hellip;&lt;/li&gt;
&lt;li&gt;Et on est un peu limité par la taille de sortie&lt;/li&gt;
&lt;li&gt;Et puis ce n&amp;rsquo;est pas évident pour lui de garder le contexte. Il a tendance à oublier les règles de base qu&amp;rsquo;on lui a donné (parce que l&amp;rsquo;historique est limité)&lt;/li&gt;
&lt;li&gt;Et puis j&amp;rsquo;ai envie de m&amp;rsquo;amuser un peu.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, LM Studio&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;lm-studio-&#34;&gt;LM Studio ?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://lmstudio.ai/&#34;&gt;LM Studio&lt;/a&gt; est une interface assez bien pensée qui vous permet de jouer avec des modèles LLM (Large Language Model) sur votre PC personnel. Sans trop d&amp;rsquo;effort, et de manière très intelligente (en utilisant des modèles reformatés, et en jouant avec votre matos).&lt;/p&gt;
&lt;p&gt;Grosse déception, ce n&amp;rsquo;est pas un logiciel open source. J&amp;rsquo;en demande beaucoup, mais j&amp;rsquo;aurai tellement préféré. Pour une fois, je fais l&amp;rsquo;impasse sur mes principes et j&amp;rsquo;accepte leur licence.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors, j&amp;rsquo;ai tendance à leur faire confiance. Ils garantissent ne pas récupérer de données personnelles, tout ce que vous faites reste privé, je me demande quand même comment ils gagnent leur vie&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Attention, vous devez avoir une machine récente. Que votre CPU supporte AVX2, et franchement il vous faut un GPU si vous ne voulez pas prendre des rides en attendant une réponse d&amp;rsquo;un modèle.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Bon, j&amp;rsquo;ai un laptop avec une RTX 3060, donc je pense pouvoir charger une partie du modèle dans la VRAM. Je sais que Mistral ne passe pas entièrement mais &amp;ldquo;LM Studio&amp;rdquo; utilise une méthode avec &amp;ldquo;llama cpp&amp;rdquo; qui peut scinder le modèle dans la RAM. Donc je serai en hybride &amp;ldquo;CPU/GPU&amp;rdquo; mais ça fera l&amp;rsquo;affaire.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En fait je pourrais très bien utiliser les &lt;code&gt;ctransfomers&lt;/code&gt; de HuggingFace, me battre pour trouver la configuration à donner au modèle et blablabla. Mais je veux faire un truc rapidement là, LM Studio est pas mal fichu, donc je me dis que pour tester&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Donc hop. On lance LM Studio, on récupère un modèle pas trop lourd, genre &amp;ldquo;mistral-7b-instruct-v0.2.Q6_K.gguf&amp;rdquo; et je me lance.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/comment-jai-g%C3%A9n%C3%A9r%C3%A9-un-dataset-avec-lia/dl-mistral.png&#34; alt=&#34;Téléchargement du modèle&#34;&gt;&lt;/p&gt;
&lt;p&gt;On lance un &amp;ldquo;chat&amp;rdquo; sur ce modèle, on tente des paramétrages&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/comment-jai-g%C3%A9n%C3%A9r%C3%A9-un-dataset-avec-lia/generating-json.png&#34; alt=&#34;Test de génération&#34;&gt;&lt;/p&gt;
&lt;p&gt;Le bot répond, c&amp;rsquo;est cool, ça marche sur ma machine&amp;hellip; Il répond en anglais, mais les produits sont en français. On va passer ce détail et se dire &amp;ldquo;bon ça ira&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Mais je me retrouve finalement dans la même situation qu&amp;rsquo;avec n&amp;rsquo;importe quel &amp;ldquo;Chat&amp;rdquo; que l&amp;rsquo;on trouve en ligne (HuggingChat, ChatGPT, Gemini, &amp;hellip;) - On est sur une &amp;ldquo;interface utilisateur&amp;rdquo;, pas un truc pour générer des fichiers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ou pas ! Parce qu&amp;rsquo;il y avait un onglet que je n&amp;rsquo;avais pas vu !!!&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2024/comment-jai-g%C3%A9n%C3%A9r%C3%A9-un-dataset-avec-lia/lm-studio-api.png&#34; alt=&#34;L&amp;rsquo;onglet LM Studio pour lancer une API&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bon sang, mais quelle idée de mettre une icône aussi foireuse !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Il suffit de lancer le serveur et &lt;strong&gt;nous voilà avec une API compatible OpenAI&lt;/strong&gt;. Entendez par là que vous pouvez utiliser le package &amp;ldquo;&lt;code&gt;openai&lt;/code&gt;&amp;rdquo; en Python (ou utiliser &lt;code&gt;curl&lt;/code&gt;, mais on n&amp;rsquo;est pas des sauvages).&lt;/p&gt;
&lt;p&gt;Donc, on se lance dans un délire !&lt;/p&gt;
&lt;h2 id=&#34;et-si&#34;&gt;Et si…&lt;/h2&gt;
&lt;p&gt;Et si, comme ça, mode bourrin, à 22 heure, devant la télé, le gamin qui dort&amp;hellip; je me lançais dans un script qui appelle l&amp;rsquo;API locale, et que je traitais les réponses pour enregistrer les données dans des fichiers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;T&amp;rsquo;as sommeil ? Non&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Je sais que je vais me confronter à pas mal d&amp;rsquo;embuches.&lt;/strong&gt; Parce que je suis habitué avec le temps. J&amp;rsquo;en ai testé des modèles, j&amp;rsquo;en ai bouffé des craquages à façonner mes requêtes pour avoir un prompt qui passe bien. Et je ne vous dis pas le nombre de fois où j&amp;rsquo;ai fait planter la machine en chargeant un peu trop fortement l&amp;rsquo;historique de conversation dans la VRAM.&lt;/p&gt;
&lt;p&gt;Donc, je sais à quoi m&amp;rsquo;en tenir, et je sais très bien, avant même de commencer, que je vais devoir :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ruser avec l&amp;rsquo;historique de conversation&lt;/li&gt;
&lt;li&gt;devoir traiter les résultats&lt;/li&gt;
&lt;li&gt;ne pas y aller trop fort, demander 10-20 produits par requête, sinon ça va foirer (non mais j&amp;rsquo;ai testé avec 100, ça fait n&amp;rsquo;importe quoi)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Allez, je me lance !&lt;/p&gt;
&lt;h2 id=&#34;rappel-de-ce-que-je-veux-faire&#34;&gt;Rappel de ce que je veux faire&lt;/h2&gt;
&lt;p&gt;Le but est d&amp;rsquo;avoir un ou plusieurs fichier JSON qui contiennent des produits. Je veux m&amp;rsquo;en servir pour alimenter une base de données de test. J&amp;rsquo;ai juste besoin de ces champs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt; : qui est un nom de produit&lt;/li&gt;
&lt;li&gt;&lt;code&gt;description&lt;/code&gt; : qui décrit le produit&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tags&lt;/code&gt; : qui est une liste de tag&lt;/li&gt;
&lt;li&gt;&lt;code&gt;categories&lt;/code&gt; : pareil, une liste de catégories, moins verbeuse,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;price&lt;/code&gt; : nombre à virgule, un prix&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je me fiche un peu de la cohérence et que les produits n&amp;rsquo;existent pas. J&amp;rsquo;ai besoin de données, c&amp;rsquo;est tout.&lt;/p&gt;
&lt;p&gt;Et je les veux en français ! C&amp;rsquo;est tout le problème qui fait que je n&amp;rsquo;ai rien trouvé de viable sur le net.&lt;/p&gt;
&lt;h2 id=&#34;passons-à-python&#34;&gt;Passons à Python&lt;/h2&gt;
&lt;p&gt;Bon, j&amp;rsquo;adore &lt;code&gt;pipenv&lt;/code&gt;, donc :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;mkdir -p ~/Projects/ML/product-generator
cd ~/Projects/ML/product-generator
pipenv install openai
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour bien comprendre comment ça se passe, il faut faire un petit test.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from openai import OpenAI

API_URL=&amp;quot;http://127.0.0.1:1234/v1&amp;quot; # LM Studio API
API_KEY=&amp;quot;not-needed&amp;quot;

client = OpenAI(base_url=API_URL, api_key=API_KEY)

history = [
    {
        &amp;quot;role&amp;quot;: &amp;quot;system&amp;quot;,
        &amp;quot;content&amp;quot;: &amp;quot;Ici, une description claire pour donner une condition à l&#39;IA&amp;quot;,
    },
    {
        &amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;,
        &amp;quot;content&amp;quot;: &amp;quot;Là, une question à poser&amp;quot;
    }
]

response = client.chat.completions.create(
    model=MODEL_NAME,  # this field is currently unused if you use LM Studio
    messages=history,  # pyright: ignore
    temperature=0.7,
    stream=False,
)

# on affiche le résultat
print(response)

# en fait c&#39;est plutôt
print(response.choices[0].message.content)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Là, on a une prémisse de ce qu&amp;rsquo;il faut faire. Reste donc à bien formater le &amp;ldquo;prompt&amp;rdquo; et comprendre le rôle de l&amp;rsquo;historique.&lt;/p&gt;
&lt;h2 id=&#34;le-rôle-de-lhistorique-et-le-problème-de-taille&#34;&gt;Le rôle de l&amp;rsquo;historique et le problème de &amp;ldquo;taille&amp;rdquo;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais de quel historique il parle ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;On parle de l&amp;rsquo;historique de conversation. En fait, les modèles conversationnels font des réponses &amp;ldquo;one shot&amp;rdquo;, entendez par là qu&amp;rsquo;il ne gardent pas en mémoire ce que vous lui avez dit, et ce qu&amp;rsquo;il vous a répondu. &lt;strong&gt;Il faut lui rappeler tout ça à chaque fois que vous relancez un prompt.&lt;/strong&gt; C&amp;rsquo;est un historique de conversation.&lt;/p&gt;
&lt;p&gt;Rappelez-vous qu&amp;rsquo;un modèle n&amp;rsquo;est pas un logiciel, pas un service. Il est figé.&lt;/p&gt;
&lt;p&gt;En général, les LLM ont deux modes. Il y a des modèles de &amp;ldquo;completion&amp;rdquo; (qui continuent un début de phrase), et des modèle &amp;ldquo;instructions&amp;rdquo; qui, eux, peuvent prendre en compte des contextes.&lt;/p&gt;
&lt;p&gt;La liste &lt;code&gt;history&lt;/code&gt; est donc un tableau qui représente la &lt;strong&gt;conversation&lt;/strong&gt; avec le &amp;ldquo;bot&amp;rdquo;. C&amp;rsquo;est un &amp;ldquo;contexte&amp;rdquo; pour qu&amp;rsquo;il continue la conversation.&lt;/p&gt;
&lt;p&gt;Et un contexte, c&amp;rsquo;est en général une liste de prompts. Ces prompts ont pour la plupart 3 rôles différents :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;&lt;code&gt;system&lt;/code&gt;&amp;rdquo; pour donner un contexte &lt;strong&gt;initial&lt;/strong&gt; (on lui donne en début de conversation et une seule fois),&lt;/li&gt;
&lt;li&gt;&amp;ldquo;&lt;code&gt;user&lt;/code&gt;&amp;rdquo; qui correspond à vos messages,&lt;/li&gt;
&lt;li&gt;et &amp;ldquo;&lt;code&gt;assistant&lt;/code&gt;&amp;rdquo; qui sont les réponses données par le modèle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On doit bien les mettre dans l&amp;rsquo;ordre (contexte initial, puis questions et réponses dans l&amp;rsquo;ordre chronologique) afin d&amp;rsquo;avoir une logique.&lt;/p&gt;
&lt;p&gt;Donc, si vous voulez créer une conversation, à chaque fois il faudra prendre la réponse du modèle, l&amp;rsquo;ajouter dans cette liste, ajouter une nouvelle phrase ou question venant de vous, et rappeler l&amp;rsquo;API.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vous voyez le souci&amp;hellip; si je veux générer des centaines de produits au format JSON, avec des descriptions, ça va être problématique&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ça veut dire qu&amp;rsquo;à un moment donné, mon historique va être énorme. Et ma machine ne va pas supporter que je donne au modèle plusieurs miliers de lignes de texte générées.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Si je dois renvoyer les réponses au modèle pour qu&amp;rsquo;il évite les doublons, et qu&amp;rsquo;il &amp;ldquo;continue&amp;rdquo; à générer des produits au fur et à mesure&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je vais taper dans la limite que supporte une requête HTTP&lt;/li&gt;
&lt;li&gt;je vais saturer la RAM / VRAM, qui doit déjà charger les 4 Go de modèle (avec 20 couches, pour avoir un peu de place)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, il va falloir ruser&amp;hellip; C&amp;rsquo;est tout le travail du &amp;ldquo;prompt engineering&amp;rdquo;. Notez que ce domaine est très mal compris par beaucoup de corps de métiers en informatique. Beaucoup pense que c&amp;rsquo;est juste &amp;ldquo;taper des prompts malins&amp;rdquo;, mais en réalité, quand on commence à pousser un peu dans le domaine, c&amp;rsquo;est une tâche qui peut s&amp;rsquo;avérer très complexe. Et dans notre cas, ça ne l&amp;rsquo;est pas tellement, pourtant il m&amp;rsquo;a fallu faire pas mal de tests pour que ça soit bon. Donc, je défends les &amp;ldquo;prompt engineer&amp;rdquo; pour ma part.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;La ruse de sioux&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Je me suis dit :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Attend mais&amp;hellip; on s&amp;rsquo;en fout de lui fournir les JSON, ou même la conversation. Parce que le contexte, je peux le contrôler moi même.&lt;/p&gt;
&lt;p&gt;On va juste lui donner la liste de noms des produits qu&amp;rsquo;il a générés&amp;rdquo;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et oui, on va être clair et lui dire qu&amp;rsquo;on a déjà &amp;ldquo;ces produits&amp;rdquo;, c&amp;rsquo;est tout ! Et comme c&amp;rsquo;est une IA, que le &amp;ldquo;I&amp;rdquo; veut dire &amp;ldquo;Intelligence&amp;rdquo;, il ne devrait pas être trop débile.&lt;/p&gt;
&lt;h2 id=&#34;le-bon-prompt&#34;&gt;Le bon prompt&lt;/h2&gt;
&lt;p&gt;Donc, en mode &amp;ldquo;chatbot&amp;rdquo; dans LM Studio, j&amp;rsquo;ai testé quelques prompts + contextes initiaux pour trouver la bonne formule, pour générer du JSON pas trop mauvais.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dans &amp;ldquo;&lt;code&gt;system&lt;/code&gt;&amp;rdquo;, je dois être très explicite. Je dois lui dire qu&amp;rsquo;il est un expert en développement, qu&amp;rsquo;il est francophone et ne répond qu&amp;rsquo;en français. Qu&amp;rsquo;il est capable de générer du JSON valide, et blablabla&lt;/li&gt;
&lt;li&gt;dans ma question, je dois lui donner beaucoup de détails, et notamment &lt;strong&gt;le schéma JSON&lt;/strong&gt; que je veux qu&amp;rsquo;il respecte. Le nombre de produits à générer (à partir de 20 il gueule un peu en disant qu&amp;rsquo;il va déborder, donc on se limite à 10 pour le moment)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notons que le markdown est accepté, ce qui donne encore un peu plus de sens à nos prompts.&lt;/p&gt;
&lt;p&gt;Donc, voilà ce que j&amp;rsquo;ai fait dans mon script :&lt;/p&gt;
&lt;pre&gt;
&lt;code class=&#34;language-python&#34;&gt;
SCHEMA = &#34;&#34;&#34;
```json
[
    {
        &#34;name&#34;: &#34;string&#34;,
        &#34;description&#34;: &#34;string&#34;,
        &#34;price&#34;: 0.0,
        &#34;categories&#34;: [&#34;string&#34;]
    }, {...}
}
```
&#34;&#34;&#34;

NUM_PRODUCTS_PER_REQUEST = 5  # The number of products you want to generate
INIT_HISTORY = [
    # the system role is used to set the language and the role of the model. We exxplicitly
    # tell that the model must respond in French - change the message to your target language
    {
        &#34;role&#34;: &#34;system&#34;,
        &#34;content&#34;: (
            &#34;Tu es un développeur français et ne répond qu&#39;en français. &#34;
            &#34;Tu peux proposer des solutions à des problèmes de programmation &#34;
            &#34;et générer du contenu technique.&#34;
        ),
    },
    # here we ask the model to generate a list of products in JSON format
    # with the given schema
    {
        &#34;role&#34;: &#34;user&#34;,
        &#34;content&#34;: (
            &#34;J&#39;ai besoin que tu me génères, en JSON,  une liste de &#34;
            f&#34;{NUM_PRODUCTS_PER_REQUEST} produits avec name, &#34;
            &#34;description, price et categories. Le nom doit être pertinent et unique, &#34;
            &#34;la description claire, et de 2 à 5 catégories. &#34;
            &#34;Le contenu doit être en français. &#34;
            &#34;Retourne un tableau d&#39;objets JSON. &#34;
            &#34;Le schema doit être conforme à : &#34; + SCHEMA
        ),
    },
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ces variables me servent de configuration. Ce n&amp;rsquo;est pas super joli, mais ça a le mérite de rester en haut de mon script.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Reste un sérieux souci&amp;hellip; le JSON n&amp;rsquo;est pas forcément donné directement, il est souvent imbriqué dans du Markdown parce que le modèle est fait pour répondre avec de la mise en page.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;un-modèle-trop-humain-&#34;&gt;Un modèle trop humain ?&lt;/h2&gt;
&lt;p&gt;Ou disons plutôt &amp;ldquo;trop orienté sur l&amp;rsquo;imitation d&amp;rsquo;un humain&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Le modèle Mistral 7b est vraiment bien foutu, pour faire un chatbot. Il est franchement intéressant pour &lt;strong&gt;discuter&lt;/strong&gt;. Mais moi je veux me servir du modèle pour générer de la donnée. Je n&amp;rsquo;ai pas envie de discuter avec lui.&lt;/p&gt;
&lt;p&gt;On doit éliminer les phrases de gentillesse. Parce que voilà, il va vous répondre, de temps en temps, un truc du genre :&lt;/p&gt;
&lt;pre&gt;
&lt;code class=&#34;language-markdown&#34;&gt;
Voilà 5 nouveaux produits, j&#39;espère que cette réponse vous convien !
```json
        le contenu json ici
```

N&#39;hésitez pas à me demander plus de trucs...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais parfois, il répond avec du JSON, directement, sans même un bonjour&amp;hellip;&lt;/p&gt;
&lt;p&gt;On a donc besoin de tenter de charger la réponse &amp;ldquo;telle quelle&amp;rdquo;, si ça plante on passe un une extraction Markdown. Je n&amp;rsquo;ai pas mieux pour le moment :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;
def extract_json_from_markdown(content):
    &#34;&#34;&#34; Extract the JSON content from the response inside a markdown block &#34;&#34;&#34; &#34;&#34;
    # The response is a string, there is a json inside &#34;```&#34; and &#34;```&#34;, we extract it
    # it is possible that the model starts the json with a newline, so we need to remove it
    json_start = content.find(&#34;```&#34;) + 3
    json_end = content.rfind(&#34;```&#34;)
    json_content = content[json_start:json_end]
    if json_content.startswith(&#34;json&#34;):
        json_content = json_content[4:]
    try:
        loaded = json.loads(json_content)
    except Exception:  # pyright: ignore pylint: disable=broad-except
        logging.info(&#34;JSON content not found as Markdown&#34;)
        return None
    return loaded


def extract_json_from_content(content):
    &#34;&#34;&#34;Extract the JSON content from the response&#34;&#34;&#34;

    # if the response is not warp in brackets, add them
    content = content.strip()
    if not content.startswith(&#34;[&#34;):
        content = f&#34;[{content}]&#34;

    try:
        json_content = json.loads(content)
    except Exception:  # pyright: ignore pylint: disable=broad-except
        logging.info(&#34;JSON content not found as full response&#34;)
        return None
    return json_content


def extract_json_content(content):
    &#34;&#34;&#34;Extract the JSON content from the response&#34;&#34;&#34;

    content = content.strip()
    loaded = extract_json_from_content(content)
    if loaded is None:
        loaded = extract_json_from_markdown(content)

    if loaded is None:
        logging.error(&#34;Error parsing JSON content, no content&#34;)
        return None

    # save known products name
    KNOWN_PRODUCTS.extend([product[&#34;name&#34;] for product in loaded])

    return loaded
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous remarquez que je stocke le nom des produits généré pour les redonner à manger au modèle afin de ne pas avoir de doublons.&lt;/p&gt;
&lt;h2 id=&#34;on-fait-en-sorte-de-continuer&#34;&gt;On fait en sorte de &amp;ldquo;continuer&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;Je vous passe les détails, vous verrez le code final dans le lien en bas de l&amp;rsquo;article, mais je sauve les JSON dans des fichiers numérotés. Et je me débrouille pour les charger au démarrage du script si besoin.&lt;/p&gt;
&lt;p&gt;Passons ce détail, maintenant je veux gérer l&amp;rsquo;historique proprement. En résumé :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;si je suis sur la première itération, je demande ma liste de produit&lt;/li&gt;
&lt;li&gt;ensuite, si je &amp;ldquo;continue&amp;rdquo; la génération pour avoir d&amp;rsquo;autres produits, et bien je veux ajouter l&amp;rsquo;information qui donne la liste des produits que j&amp;rsquo;ai déjà&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, on se garde une variable &amp;ldquo;&lt;code&gt;KNOWN_PRODUCTS&lt;/code&gt;&amp;rdquo; dans une globale bien dégueulasse, et on se génère une liste à la Markdown. Parce que le modèle comprend la &amp;ldquo;mise en forme&amp;rdquo;. Comme ça je peux lui dire que ne ne veut pas les produits que j&amp;rsquo;ai déjà stocké.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;APPEND_PROMPT = &amp;quot;&amp;quot;&amp;quot;
J&#39;ai déjà ces produits :
&amp;quot;&amp;quot;&amp;quot;

KNOWN_PRODUCTS = []

def list_of_products():
    &amp;quot;&amp;quot;&amp;quot;Generate a list of products in markdown format&amp;quot;&amp;quot;&amp;quot;
    return APPEND_PROMPT.strip() + &amp;quot;\n&amp;quot;.join([f&amp;quot;- {p}&amp;quot; for p in KNOWN_PRODUCTS])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et je ponds une fonction pour générer le prompt dans le cas initial et le cas &amp;ldquo;continue&amp;rdquo; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def create_completion(continuation=False):
    &amp;quot;&amp;quot;&amp;quot;Create a completion using the conversation history&amp;quot;&amp;quot;&amp;quot;

    # force a copy of the history
    history = list(INIT_HISTORY)

    # add the continuation prompt
    if continuation:
        history.append(
            {
                &amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;,
                &amp;quot;content&amp;quot;: list_of_products(),
            }
        )

    logging.info(&amp;quot;History: %s&amp;quot;, history)

    return CLIENT.chat.completions.create(
        model=MODEL_NAME,  # this field is currently unused if you use LM Studio
        messages=history,  # pyright: ignore
        temperature=0.7,
        stream=False,
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela va ajouter &amp;ldquo;j&amp;rsquo;ai déjà ces produits :&amp;rdquo; avec la liste des noms de produits déjà générés si je passe &amp;ldquo;&lt;code&gt;True&lt;/code&gt;&amp;rdquo; en argument.&lt;/p&gt;
&lt;h2 id=&#34;un-exemple-de-résultat-&#34;&gt;Un exemple de résultat ?&lt;/h2&gt;
&lt;p&gt;Allez&amp;hellip; je vous montre un bout de fichier que me génère mon script (ne paniquez pas, je vous donne le lien du code en bas de l&amp;rsquo;article):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;[
  {
    &amp;quot;name&amp;quot;: &amp;quot;Samsung Galaxy S21 Ultra - Nouvelle G\u00e9n\u00e9ration&amp;quot;,
    &amp;quot;description&amp;quot;: &amp;quot;Smartphone avec \u00e9cran OLED dynamic AMOLED 2X, processeur Exynos 2100 ou Snapdragon 888 et une batterie de 5000mAh.&amp;quot;,
    &amp;quot;price&amp;quot;: 1299.99,
    &amp;quot;categories&amp;quot;: [
      &amp;quot;Smartphones&amp;quot;,
      &amp;quot;Electronique&amp;quot;
    ]
  },
  {
    &amp;quot;name&amp;quot;: &amp;quot;Apple iPad Pro 12,9 pouces (5e g\u00e9n\u00e9ration)&amp;quot;,
    &amp;quot;description&amp;quot;: &amp;quot;Tablette tactile dot\u00e9e d&#39;un \u00e9cran Liquid Retina XDR avec M1 Chip et une batterie de 30.42 w-h.&amp;quot;,
    &amp;quot;price&amp;quot;: 1119.0,
    &amp;quot;categories&amp;quot;: [
      &amp;quot;Tablettes&amp;quot;,
      &amp;quot;Electronique&amp;quot;
    ]
  },
  {
    &amp;quot;name&amp;quot;: &amp;quot;Canon EOS R5 - Hybride RF Mount&amp;quot;,
    &amp;quot;description&amp;quot;: &amp;quot;Appareil photo reflex num\u00e9rique \u00e0 plein format avec capteur CMOS de 45 Mpx et autofocus Dual Pixel.&amp;quot;,
    &amp;quot;price&amp;quot;: 3899.0,
    &amp;quot;categories&amp;quot;: [
      &amp;quot;Appareils photos&amp;quot;,
      &amp;quot;\u00c9lectronique&amp;quot;
    ]
  },
  {
    &amp;quot;name&amp;quot;: &amp;quot;Logitech MX Master 3 - Souris sans fil wireless&amp;quot;,
    &amp;quot;description&amp;quot;: &amp;quot;Souris haut de gamme avec une ergonomie adapt\u00e9e \u00e0 la main, des boutons programmables et une batterie interne rechargeable.&amp;quot;,
    &amp;quot;price&amp;quot;: 99.99,
    &amp;quot;categories&amp;quot;: [
      &amp;quot;Souris informatique&amp;quot;,
      &amp;quot;\u00c9lectronique&amp;quot;
    ]
  },
  {
    &amp;quot;name&amp;quot;: &amp;quot;Dell XPS 15 - 9510&amp;quot;,
    &amp;quot;description&amp;quot;: &amp;quot;Ordinateur de bureau portatif \u00e9quip\u00e9 d&#39;un \u00e9cran OLED 15.6 inches, un processeur Intel Core i7 et une batterie de 86 Wh.&amp;quot;,
    &amp;quot;price&amp;quot;: 2349.0,
    &amp;quot;categories&amp;quot;: [
      &amp;quot;Ordinateurs portatifs&amp;quot;,
      &amp;quot;\u00c9lectronique&amp;quot;
    ]
  }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et j&amp;rsquo;en ai généré 1500, scindés en pleins de fichiers.&lt;/p&gt;
&lt;p&gt;Ça a pris un peu de temps, soyons honnête. Ma machine est limitée (c&amp;rsquo;est un portable hein, donc les RTX sont bien bridée), et mon vieux coucou ne comprend pas les instructions AVX2 (donc, j&amp;rsquo;ai 24 Go de RAM, une RTX 3070, un corei7, mais je ne peux pas l&amp;rsquo;utiliser avec Mistral avec LM Studio&amp;hellip;)&lt;/p&gt;
&lt;p&gt;Mais clairement, Python ici est fabuleux. Ce script n&amp;rsquo;est pas un truc de prod, c&amp;rsquo;est un gadget qui me génère de la data. J&amp;rsquo;ai codé ce machin en 20 minutes environ.&lt;/p&gt;
&lt;h2 id=&#34;le-code-final&#34;&gt;Le code final&lt;/h2&gt;
&lt;p&gt;Bien entendu, je n&amp;rsquo;ai pas tout décrit dans cet article. Le code complet est dans un &amp;ldquo;gist&amp;rdquo; :&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://gist.github.com/metal3d/448da715534baf686c1fffc685483296&#34;&gt;https://gist.github.com/metal3d/448da715534baf686c1fffc685483296&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Le script génère des fichiers, sans écraser les anciens. Il sait relire les données déjà générées pour continuer la génération de JSON.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ce n&amp;rsquo;est pas du grand art&lt;/strong&gt;, je le sais. C&amp;rsquo;est codé à l&amp;rsquo;arrache, et ça mérite potentiellement de créer un &lt;strong&gt;vrai&lt;/strong&gt; projet. Voir une interface graphique, etc.&lt;/p&gt;
&lt;p&gt;Mais là, pour l&amp;rsquo;heure, j&amp;rsquo;ai bien tous mes fichiers générés.&lt;/p&gt;
&lt;p&gt;Vous pouvez adapter le script, et notamment le prompt, pour changer la langue, forcer un domaine de produits, etc.&lt;/p&gt;
&lt;p&gt;Amusez-vous !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Podman et Nvidia, ou comment avoir accès à son GPU dans un conteneur</title>
      <link>https://www.metal3d.org/blog/2023/podman-et-nvidia/</link>
      <pubDate>Thu, 09 Nov 2023 21:15:02 +0100</pubDate>
      
      <guid>https://www.metal3d.org/blog/2023/podman-et-nvidia/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2023/podman-et-nvidia/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Partager sa carte graphique avec un conteneur est très intéressant. Cela évite d&amp;rsquo;une part d&amp;rsquo;avoir à installer localement toute une batterie de packages et de librairie (surtout pour CUDA qui est une plaie), de rendre jetable un test, de partager une application utilisant le Machine-Learning simplement, etc.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bref, c&amp;rsquo;est utile et ça soulage.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Mais ce n&amp;rsquo;est pas évident à activer. La conteneurisation sert à isoler l&amp;rsquo;application du système, et là on veut absolument lui partager un périphérique. Et pas n&amp;rsquo;importe lequel, pas une imprimante USB ou une caméra&amp;hellip; non&amp;hellip; là on parle d&amp;rsquo;une carte graphique. Heureusement, les derniers efforts de Nvidia à proposer de la documentation et leur ouverture de certaines parties de leur travail permet de s&amp;rsquo;en sortir avec un peu plus de confort.&lt;/p&gt;
&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Pour les pressés, voici les étapes rapides :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# installer le dépot de nvidia container toolkit
curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | \
  sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo
# puis les outils
sudo dnf install nvidia-container-toolkit

# et on commence par créer le fichier de configuration
sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml

# on corrige les soucis SELinux
nvidia-container-cli -k list | sudo restorecon -v -f -
sudo restorecon -Rv /dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si vous voulez utiliser Podman en &amp;ldquo;rootless&amp;rdquo;, il faut modifier avec sudo, le fichier &lt;code&gt;/etc/nvidia-container-runtime/config.toml&lt;/code&gt;,
on trouve les deux lignes qui nous intéresse :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-toml&#34;&gt;[nvidia-container-cli]
#no-cgroups = false
no-cgroups = true

[nvidia-container-runtime]
#debug = &amp;quot;/var/log/nvidia-container-runtime.log&amp;quot;
debug = &amp;quot;~/.local/nvidia-container-runtime.log&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et on peut tester, il faut bien ajouter &lt;code&gt;--device nvidia.com/gpu=all&lt;/code&gt; et &lt;code&gt;--security-opt=label=disable&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;podman run --rm \
    --device nvidia.com/gpu=all \
    --security-opt=label=disable \
    ubuntu nvidia-smi -L

# ça doit vous donner un truc de ce genre:
GPU 0: NVIDIA GeForce RTX 3070 (UUID: GPU-f7187582-1105-9022-2d77-ac16ed3b10ca)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, c&amp;rsquo;est tout&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;en-détail&#34;&gt;En détail&amp;hellip;&lt;/h2&gt;
&lt;p&gt;Bon, on va expliquer pas à pas ce qu&amp;rsquo;il faut faire.&lt;/p&gt;
&lt;h3 id=&#34;installer-les-outils-de-nvidia&#34;&gt;Installer les outils de Nvidia&lt;/h3&gt;
&lt;p&gt;Nvidia propose des outils assez bien foutus pour permettre à Docker et Podman (mais aussi à Kubernetes, etc&amp;hellip;) de partager votre carte graphique avec des conteneurs OCI.
Le fait est que depuis quelque temps, et ce grâce à l&amp;rsquo;explosion des travaux en Machine-Learning, Nvidia est aux avant-postes. Du coup, ils ont mis les bouchées doubles, ont ouvert une partie de leur code, et ils se sont mis à documenter correctement tout leur boxon.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pour le coup, on peut souligner les efforts et leur dire merci.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Bref, &lt;a href=&#34;https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html&#34;&gt;la documentation&lt;/a&gt; est assez complète et les outils assez &amp;ldquo;simples&amp;rdquo; à utiliser.&lt;/p&gt;
&lt;p&gt;Je suis sur Fedora, donc je ne vais pas tortiller pendant des heures avec Ubuntu. L&amp;rsquo;installation commence par ajouter le dépôt officiel pour Fedora:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | \
  sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce dépôt vous propose quelques outils, mais c&amp;rsquo;est &lt;code&gt;nvidia-ctk&lt;/code&gt; et &lt;code&gt;nvidia-container-cli&lt;/code&gt; qui nous intéressent. Et ces outils se trouvent dans le package &lt;code&gt;nvidia-container-toolkit&lt;/code&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo dnf install nvidia-container-toolkit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;À partir de là, on est paré pour la suite.&lt;/p&gt;
&lt;h2 id=&#34;configurer-pour-podman&#34;&gt;Configurer pour Podman&lt;/h2&gt;
&lt;p&gt;La doc est assez claire, mais elle va vous vous guider, ensuite, vers une autre page pour Podman.&lt;/p&gt;
&lt;p&gt;Nvidia vous demande d&amp;rsquo;utiliser plutôt &amp;ldquo;CDI&amp;rdquo; (Container Devide Interface) pour configurer l&amp;rsquo;accès à votre périphérique. Et &lt;a href=&#34;https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/cdi-support.html&#34;&gt;cette page&lt;/a&gt; vous demande de faire deux trois bricoles.&lt;/p&gt;
&lt;p&gt;La première commande va générer un fichier &lt;code&gt;yaml&lt;/code&gt; qui liste les &amp;ldquo;devices&amp;rdquo; et les &amp;ldquo;hooks&amp;rdquo; à effectuer quand le conteneur va demander l&amp;rsquo;accès à ces périphériques. Le faire à la main aurait été une plaie, là, c&amp;rsquo;est simple :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# enlevez --output=... pour voir ce qu&#39;il va générer
sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Après cela, vous pouvez contrôler la liste des périphériques qu&amp;rsquo;il a configuré :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;nvidia-ctk cdi list
# il doit vous sortir un truc du genre
# nvidia.com/gpu=all
# nvidia.com/gpu=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ces lignes, ce sont les options à &lt;code&gt;--device&lt;/code&gt; qu&amp;rsquo;on pourra donner à Podman. Vous allez voir ça juste après.&lt;/p&gt;
&lt;p&gt;Si cette étape s&amp;rsquo;est bien passé, on va corriger les attributs SELinux &lt;em&gt;au cas où&amp;hellip;&lt;/em&gt; (en réalité je pense que ce n&amp;rsquo;est pas nécessaire, mais dans le doute&amp;hellip;)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;nvidia-container-cli -k list | sudo restorecon -v -f -
sudo restorecon -Rv /dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il reste un dernier point, et pas des moindres, c&amp;rsquo;est &lt;strong&gt;de corriger la manière dont on va permettre à Podman d&amp;rsquo;écrire des logs et d&amp;rsquo;utiliser les &lt;code&gt;cgroups&lt;/code&gt;&lt;/strong&gt; et ce, &lt;strong&gt;seulement si vous voulez utiliser le mode &amp;ldquo;rootless&amp;rdquo;&lt;/strong&gt; de Podman.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est tout de même l&amp;rsquo;un des points forts de Podman, dont on ne s&amp;rsquo;en privera pas. Je vous conseille donc de le faire !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ouvrez, avec les droits d&amp;rsquo;admin (donc &lt;code&gt;sudo vim&lt;/code&gt; ou &lt;code&gt;sudo nano&lt;/code&gt;) le fichier &lt;code&gt;/etc/nvidia-container-runtime/config.toml&lt;/code&gt; pour toucher deux lignes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La première dans le groupe &lt;code&gt;nvidia-container-cli&lt;/code&gt; pour lui dire de se passer des &lt;code&gt;cgroups&lt;/code&gt; (ouais bah&amp;hellip; bon&amp;hellip;)&lt;/li&gt;
&lt;li&gt;L&amp;rsquo;autre dans le groupe &lt;code&gt;nvidia-container-runtime&lt;/code&gt; pour lui changer le chemin des logs car, de base, il va taper dans &lt;code&gt;/var/log&lt;/code&gt; et vous, en tant qu&amp;rsquo;utilisateur, vous ne pourrez pas le faire.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc en gros, trouvez les lignes (normalement commentées), copiez-les en dessous et changez la valeur. &lt;strong&gt;Surtout, gardez les autres lignes telles quelles&lt;/strong&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-toml&#34;&gt;[nvidia-container-cli]
#no-cgroups = false
no-cgroups = true

[nvidia-container-runtime]
#debug = &amp;quot;/var/log/nvidia-container-runtime.log&amp;quot;
debug = &amp;quot;~/.local/nvidia-container-runtime.log&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour référence, voici mon fichier à moi :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-toml&#34;&gt;
#accept-nvidia-visible-devices-as-volume-mounts = false
#accept-nvidia-visible-devices-envvar-when-unprivileged = true
disable-require = false
supported-driver-capabilities = &amp;quot;compat32,compute,display,graphics,ngx,utility,video&amp;quot;
#swarm-resource = &amp;quot;DOCKER_RESOURCE_GPU&amp;quot;

[nvidia-container-cli]
#debug = &amp;quot;/var/log/nvidia-container-toolkit.log&amp;quot;
environment = []
#ldcache = &amp;quot;/etc/ld.so.cache&amp;quot;
ldconfig = &amp;quot;@/sbin/ldconfig&amp;quot;
load-kmods = true
#no-cgroups = false
no-cgroups = true
#path = &amp;quot;/usr/bin/nvidia-container-cli&amp;quot;
#root = &amp;quot;/run/nvidia/driver&amp;quot;
#user = &amp;quot;root:video&amp;quot;

[nvidia-container-runtime]
#debug = &amp;quot;/var/log/nvidia-container-runtime.log&amp;quot;
debug = &amp;quot;~/.local/nvidia-container-runtime.log&amp;quot;
log-level = &amp;quot;info&amp;quot;
mode = &amp;quot;auto&amp;quot;
runtimes = [&amp;quot;docker-runc&amp;quot;, &amp;quot;runc&amp;quot;]

[nvidia-container-runtime.modes]

[nvidia-container-runtime.modes.cdi]
annotation-prefixes = [&amp;quot;cdi.k8s.io/&amp;quot;]
default-kind = &amp;quot;nvidia.com/gpu&amp;quot;
spec-dirs = [&amp;quot;/etc/cdi&amp;quot;, &amp;quot;/var/run/cdi&amp;quot;]

[nvidia-container-runtime.modes.csv]
mount-spec-path = &amp;quot;/etc/nvidia-container-runtime/host-files-for-container.d&amp;quot;

[nvidia-container-runtime-hook]
path = &amp;quot;nvidia-container-runtime-hook&amp;quot;
skip-mode-detection = false

[nvidia-ctk]
path = &amp;quot;nvidia-ctk&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Attention, quand vous mettez à jour vos packages, ce fichier est écrasé. Ou pas&amp;hellip; en fait il y aura un fichier &lt;code&gt;config.toml.rpmsave&lt;/code&gt; dans lequel il y aura vos anciennes modifications. Mais je vous conseille d&amp;rsquo;utiliser le fichier mis à jour, qui généralement à des modifications importantes, et de simplement remettre d&amp;rsquo;aplomb les lignes qu&amp;rsquo;on a changées.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Voilà&amp;hellip; on a fini. Reste à tester :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;podman run --rm \
    --device nvidia.com/gpu=all \
    --security-opt=label=disable \
    ubuntu nvidia-smi -L

# doit vous donner la même ligne que `nvidia-smi` en local
# pour ma part :
GPU 0: NVIDIA GeForce RTX 3060 Laptop GPU (UUID: GPU-21c9319b-4c2d-84db-f028-cd065f9d9e8f)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;important&#34;&gt;Important&lt;/h2&gt;
&lt;p&gt;Vous allez certainement tester des outils qui ne parlent que de Docker, alors que ça marche très bien avec Podman. La seule chose à penser c&amp;rsquo;est de ne pas utiliser le paramètre &lt;code&gt;--gpus&lt;/code&gt; (qui ne marche pas sur Podman et je ne sais pas pourquoi), mais d&amp;rsquo;utiliser &lt;code&gt;--device nvidia.com/gpu=all&lt;/code&gt; (ou l&amp;rsquo;index de carte à la place de &lt;code&gt;all&lt;/code&gt;, par exemple &lt;code&gt;nvidia.com/gpu=0&lt;/code&gt;) et de forcer la non-labellisation avec &lt;code&gt;--security-opt=label=diable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Sans cela, vous allez vous prendre des erreurs dans la poire sans rien comprendre.&lt;/p&gt;
&lt;p&gt;Pour ma part j&amp;rsquo;ai ajouté un alias dans mon environnement :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;alias podman-gpu=&amp;quot;podman run --device nvidia.com/gpu=all --security-opt=label=disable&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et c&amp;rsquo;est beaucoup plus simple &amp;#x1f604;&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas évident, je vous l&amp;rsquo;accorde. Mais ce n&amp;rsquo;est pas insurmontable en réalité. La seule chose à bien penser est de refaire la modification du fichier &lt;code&gt;toml&lt;/code&gt; pour les &lt;code&gt;cgroups&lt;/code&gt; et le chemin des logs quand les packages sont mis à jour.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est d&amp;rsquo;ailleurs dommage que nous ne pouvions pas utiliser un fichier d&amp;rsquo;override (c&amp;rsquo;est peut-être possible, si c&amp;rsquo;est le cas je modifierai l&amp;rsquo;article), mais bon&amp;hellip; ça me prend moins d&amp;rsquo;une minute à faire tout ça.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;intérêt est de pouvoir lancer des inférences, des entrainements, des outils utilisant le GPU sans installer tout ça localement. C&amp;rsquo;est aussi intéressant de pouvoir tester plusieurs versions de CUDA sans les installer à l&amp;rsquo;arrache.&lt;/p&gt;
&lt;p&gt;Podman est devenu mon seul et unique runtime de conteneur depuis des mois, du moins sur mon poste Linux (car je suis forcé d&amp;rsquo;utiliser un poste Windows dans mon nouveau Job).&lt;/p&gt;
&lt;h2 id=&#34;références&#34;&gt;Références&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html&#34;&gt;https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/cdi-support.html&#34;&gt;https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/cdi-support.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </description>
    </item>
    
    <item>
      <title>Bien Configurer Son Terminal Bash</title>
      <link>https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/</link>
      <pubDate>Wed, 07 Jun 2023 09:44:46 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;C&amp;rsquo;est bien plus agréable de travailler avec un terminal qui en jette et qui soit simple à configurer. Alors voilà comment je m&amp;rsquo;y prends.&lt;/p&gt;
&lt;p&gt;Les anciens qui utilisent Windows n&amp;rsquo;arrivent pas à nous comprendre, nous, Linuxiens et/ou Unixiens (parce que Mac OSX n&amp;rsquo;est pas en reste). On adore le terminal de commande. Quand on est un débutant, on a l&amp;rsquo;impression d&amp;rsquo;être un hacker, et quand on a de l&amp;rsquo;expérience, on fait à peu près tout et n&amp;rsquo;importe quoi dessus. On s&amp;rsquo;abstrait d&amp;rsquo;énormément d&amp;rsquo;actions rébarbatives, on automatise des trucs, on se fait nos outils en Bash, etc. En bref, le terminal de commande est incroyable.&lt;/p&gt;
&lt;p&gt;Le terminal date des années 60, rendez-vous compte. Une fenêtre généralement noire avec du texte en blanc et voilà, on peut contrôler une machine. Pas besoin de souris (mais en fait on peut), pas besoin d&amp;rsquo;interface à 200 onglets dans 80 menus&amp;hellip; tout est là. On tape une commande et on a ce qu&amp;rsquo;il faut.&lt;/p&gt;
&lt;p&gt;Sauf que voilà, un terminal avec &amp;ldquo;bash&amp;rdquo; de base, ça donne pas envie à un novice.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/images/terminal-base.webp&#34; alt=&#34;Terminal de base&#34;&gt;&lt;/p&gt;
&lt;p&gt;Heureusement, Bash (mais aussi Zsh, fish etc.) permet de faire plein de choses pour rendre cette interface un peu plus sexy !&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Le souci, c&amp;rsquo;est que l&amp;rsquo;on trouve pas mal de mauvaises pratiques sur internet en ce qui concerne la configuration. Donc, on va y aller proprement, de manière à ne pas gâcher le plaisirs.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Faisons les choses dans l&amp;rsquo;ordre.&lt;/p&gt;
&lt;h2 id=&#34;on-commence-par-avoir-une-configuration-de-base-claire&#34;&gt;On commence par avoir une configuration de base claire&lt;/h2&gt;
&lt;p&gt;Normalement, vous devriez avoir un fichier &lt;code&gt;.bashrc&lt;/code&gt; à la racine de votre répertoire utilisateur.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pour la suite, je vais utiliser le symbole &amp;ldquo;&lt;code&gt;~&lt;/code&gt;&amp;rdquo; qui est interprété par &amp;ldquo;bash&amp;rdquo; comme étant votre répertoire utilisateur. Ainsi, &lt;code&gt;~/.bashrc&lt;/code&gt; est le fichier &lt;code&gt;.bashrc&lt;/code&gt; dans votre &amp;ldquo;HOME&amp;rdquo;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Dans ce fichier, si vous n&amp;rsquo;avez rien fait, voilà ce qu&amp;rsquo;il doit y avoir:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
	. /etc/bashrc
fi

# User specific environment
if ! [[ &amp;quot;$PATH&amp;quot; =~ &amp;quot;$HOME/.local/bin:$HOME/bin:&amp;quot; ]]
then
    PATH=&amp;quot;$HOME/.local/bin:$HOME/bin:$PATH&amp;quot;
fi
export PATH

# Uncomment the following line if you don&#39;t like systemctl&#39;s auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions
if [ -d ~/.bashrc.d ]; then
	for rc in ~/.bashrc.d/*; do
		if [ -f &amp;quot;$rc&amp;quot; ]; then
			. &amp;quot;$rc&amp;quot;
		fi
	done
fi

unset rc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si ce n&amp;rsquo;est pas le cas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vous êtes sur Ubuntu, et ils ne sont pas du tout un modèle de vertue&lt;/li&gt;
&lt;li&gt;Vous avez fait une bêtise&lt;/li&gt;
&lt;li&gt;Vous n&amp;rsquo;utilisez pas bash&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans tous les cas, il serait idéal que ce fichier soit tel quel et &lt;strong&gt;de ne rien ajouter dans ce fichier&lt;/strong&gt;. Car en effet, il est largement suffisant, regardez bien.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et oui, la partie &amp;ldquo;User specific aliases and functions&amp;rdquo; est &lt;strong&gt;LA&lt;/strong&gt; bonne pratique.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Vous l&amp;rsquo;aurez compris, on peut désormais ajouter des fichier dans un répertoire pour activer, modifier, changer la configuration de notre shell. Cool !&lt;/p&gt;
&lt;p&gt;Créez le répertoire &lt;code&gt;~/.bashrc.d&lt;/code&gt; dans lequel on va créer des scripts de configuration.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et si vous voyez un ami, collègue, membre de la famille, qui n&amp;rsquo;a pas fait ça, il vous est permis de prendre des airs suffisants et de dire de manière condescendante qu&amp;rsquo;ils font les choses comme des débutants.&lt;/p&gt;&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir -p ~/.bashrc.d
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;on-active-la-completion&#34;&gt;On active la &amp;ldquo;completion&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;La completion est un confort d&amp;rsquo;utilisation sans précédent. La touche &amp;ldquo;tabulation&amp;rdquo; (celle qui est à gauche du clavier, représentée par deux flèches qui vont dans les deux sens) va &amp;ldquo;compléter&amp;rdquo; la commande ou les options. C&amp;rsquo;est optionnel, mais je ne connais personne d&amp;rsquo;humain qui ne l&amp;rsquo;utilise pas.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/images/tabkey.webp&#34; alt=&#34;Touche tabulation&#34;&gt;&lt;/p&gt;
&lt;p&gt;Donc, on va installer le paquet qui active la &amp;ldquo;complétion&amp;rdquo; bash.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Fedora, Red Hat, etc...
sudo dnf install bash-completion

# Ubuntu, Debian...
sudo apt install bash-completion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et maintenant, on est prêt pour l&amp;rsquo;aventure.&lt;/p&gt;
&lt;h2 id=&#34;on-crée-le-répertoire-local-&#34;&gt;On crée le répertoire local !&lt;/h2&gt;
&lt;p&gt;Ça me rend assez dingue cette histoire. Je vois encore des gens utiliser les répertoires systèmes pour installer des outils &amp;ldquo;personnels&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.freedesktop.org/&#34;&gt;Freedesktop&lt;/a&gt; recommande depuis des lustres d&amp;rsquo;utiliser un répertoire &lt;strong&gt;dans le HOME utilisteur&lt;/strong&gt; pour les outils que vous installez hors package de distribution. En l&amp;rsquo;occurence, on va installer des trucs qui ne sont pas &amp;ldquo;officiels&amp;rdquo; (mais pas risqué), et il est inadmissible de voir encore des outils qui demande accès à &lt;code&gt;/usr&lt;/code&gt; ou &lt;code&gt;/usr/local&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Le répertoire dont nous avons besoin est &lt;code&gt;~/.local&lt;/code&gt;, dans lequel seront créé des répertoire &amp;ldquo;bin&amp;rdquo;, &amp;ldquo;share&amp;rdquo;, &amp;ldquo;cache&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Donc:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir -p ~/.local/bin
source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et désormais, le répertoire local &amp;ldquo;bin&amp;rdquo; est dans notre &amp;ldquo;PATH&amp;rdquo;, cela veut dire que si vous installez une commande &amp;ldquo;foo&amp;rdquo; dans ce répertoire, alors la commande &amp;ldquo;foo&amp;rdquo; existera (sans demander de taper tout le chemin).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Franchement, en 2023, ça me scie les nerfs&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;on-se-crée-des-alias&#34;&gt;On se crée des alias&lt;/h2&gt;
&lt;p&gt;Les alias sont des command qui permettent d&amp;rsquo;avoir des &amp;ldquo;raccourcis&amp;rdquo;. Par exemple, la commande &amp;ldquo;&lt;code&gt;ls -l&lt;/code&gt;&amp;rdquo; pourrait avoir un alias &amp;ldquo;&lt;code&gt;ll&lt;/code&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Alors, voici mes aliases, à vous d&amp;rsquo;en ajouter si vous le désirez. Ce fichier est placé en &lt;code&gt;~/.bashrc.d/aliases-and-path.sh&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# J&#39;utilise nvim, mais vous pouvez mettre
# gedit, nano, ce que vous voulez
export EDITOR=nvim

# aliases
alias ll=&#39;ls -l&#39;
alias la=&#39;ls -A&#39;
alias l=&#39;ls -CF&#39;
alias k=kubectl
alias h=helm
alias see=&amp;quot;highlight -n -O ansi --force=text&amp;quot;

# append go bin to path
export PATH=&amp;quot;$HOME/.local/share/go/bin:$PATH&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notez:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;see&lt;/code&gt; est un alias à &amp;ldquo;highlight&amp;rdquo;, il colore le fichier en sortie sur le terminal (c&amp;rsquo;est pratique pour voir du code source)&lt;/li&gt;
&lt;li&gt;kubernetes et helm sont des outils que j&amp;rsquo;utilise, supprimez ces alias si vous n&amp;rsquo;en avez pas besoin&lt;/li&gt;
&lt;li&gt;pareil pour &amp;ldquo;go&amp;rdquo;, je préfère placer les binaires à un endroit spécifique, et j&amp;rsquo;ajoute ce répertoire à &lt;code&gt;$PATH&lt;/code&gt;, mais si vous n&amp;rsquo;en avez pas besoin, vous pouvez supprimer la ligne.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour le moment, si vous rechargez votre shell (&lt;code&gt;source ~/.bashrc&lt;/code&gt;) alors les alias doivent fonctionner. Tapez la commande &lt;code&gt;l&lt;/code&gt;, &lt;code&gt;ll&lt;/code&gt;, ou &lt;code&gt;la&lt;/code&gt; pour vérifier.&lt;/p&gt;
&lt;h2 id=&#34;oh-my-posh&#34;&gt;Oh My Posh&lt;/h2&gt;
&lt;p&gt;C&amp;rsquo;est optionnel, mais j&amp;rsquo;adore avoir un &amp;ldquo;prompt&amp;rdquo; plus moderne.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ohmyposh.dev/&#34;&gt;Oh-My-Posh&lt;/a&gt; est un outil qui va décorer et ajouter des fonctionnalités à votre shell (ça marche avec pas mal de shell). On l&amp;rsquo;installe assez facilement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;curl -s https://ohmyposh.dev/install.sh | bash -s -- -d ~/.local/bin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, créez un fichier &lt;code&gt;~/.bashrc.d/oh-my-posh.sh&lt;/code&gt; et ajoutez:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;eval &amp;quot;$(~/.local/bin/oh-my-posh init bash --config ~/.cache/oh-my-posh/themes/bubblesline.omp.json)&amp;quot;

# an alias to update
alias update-oh-my-posh=&amp;quot;curl -s https://ohmyposh.dev/install.sh | bash -s&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parfois, il y aura une notification dans votre terminal pour vous dire qu&amp;rsquo;une nouvelle version de oh-my-posh est disponible. Dans ce cas, tapez la commande (l&amp;rsquo;alias) &lt;code&gt;update-oh-my-posh&lt;/code&gt; et le tour est joué.&lt;/p&gt;
&lt;h2 id=&#34;on-va-rendre-le-terminal-un-poil-plus-ergonomique&#34;&gt;On va rendre le terminal un poil plus ergonomique&lt;/h2&gt;
&lt;p&gt;D&amp;rsquo;abord, cette fameuse &amp;ldquo;completion&amp;rdquo;, elle marche bien mais elle pose des contraintes. Elle est sensible à la casse (ça veut dire qu&amp;rsquo;elle prend en compte les majuscules et minuscules), et elle a tendance à faire défiler le terminal pour rien. Par exemple:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/images/tab.gif&#34; alt=&#34;Tabulation scroll&#34;&gt;&lt;/p&gt;
&lt;p&gt;Dans l&amp;rsquo;image ci-dessus, je presse la touche &amp;ldquo;tabulation&amp;rdquo; pour essayer de compléter le chemin, mais comme il existe un paquet de fhciers&amp;hellip; il me propose toutes les possibilités dans la sortie.&lt;/p&gt;
&lt;p&gt;On peut faire mieux !&lt;/p&gt;
&lt;p&gt;On commence par créer un fichier &lt;code&gt;~/.inputrc&lt;/code&gt; dans lequel on ajoute:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;set completion-ignore-case on
set show-all-if-ambiguous on
set show-all-if-unmodified on
set visible-stats on
TAB: menu-complete
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Là il faut vraiment fermer votre terminal et le rouvrir. Maintenant:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/images/tab2.gif&#34; alt=&#34;Tabulation avec menu-complete&#34;&gt;&lt;/p&gt;
&lt;p&gt;Ça évite énormément de lignes inutiles en sortie.&lt;/p&gt;
&lt;h2 id=&#34;blesh&#34;&gt;Ble.sh&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/akinomyoga/ble.sh&#34;&gt;Ble.sh&lt;/a&gt; est un outil qui va &lt;strong&gt;fortement modifier l&amp;rsquo;interaction dans votre shell&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Il rend le mode &amp;ldquo;menu&amp;rdquo; plus convivial, en permettant de sélectionner avec les touches de direction le fichier ou l&amp;rsquo;option que vous voulez (en completion)&lt;/li&gt;
&lt;li&gt;Il fait de la coloration syntaxique avec contrôle, donc les mots clef &amp;ldquo;for, while, if&amp;hellip;&amp;rdquo; sont colorés, ainsi que les commandes&lt;/li&gt;
&lt;li&gt;Si la commande ou le contrôle n&amp;rsquo;existe pas, il le surligne&lt;/li&gt;
&lt;li&gt;Il permet l&amp;rsquo;édition multiligne, par exemple en tapant une boucle&lt;/li&gt;
&lt;li&gt;Et plein d&amp;rsquo;autres choses&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est un peu gadget, mais j&amp;rsquo;adore.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;On commence par installer l&amp;rsquo;outil. Mais on va le faire &amp;ldquo;à ma manière&amp;rdquo;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;git clone --recursive --depth 1 --shallow-submodules https://github.com/akinomyoga/ble.sh.git
make -C ble.sh install PREFIX=~/.local
rm -rf ble.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et on crée le fichier &lt;code&gt;~/.bashrc.d/ble.sh&lt;/code&gt; qui contient:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# initialize ble.sh
source ~/.local/share/blesh/ble.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Idem que pour oh-my-posh, vous pouvez mettre à jour avec la comamnde &lt;code&gt;ble update&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Reste que les couleurs de base sont, non mais n&amp;rsquo;ayons pas peur de le dire, affreuses. Alors personnellement j&amp;rsquo;ai reconfiguré quelques variables.&lt;/p&gt;
&lt;p&gt;Voici mon fichier &lt;code&gt;~/.blerc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;ble-face command_function=fg=205
ble-face command_directory=fg=004,underline
ble-face filename_directory=fg=004
ble-face filename_directory_sticky=fg=103
ble-face auto_complete=fg=15,bg=000
ble-face region_insert=fg=252,bg=000
ble-face syntax_function_name=fg=105,bold
ble-face varname_export=fg=135,bold
ble-face filename_other=
bleopt complete_auto_complete=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela correspond mieux à mes préférences. Vous pouvez utiliser la commande &lt;code&gt;ble-face&lt;/code&gt; pour voir toutes les variables et les modifier ensuite dans votre &lt;code&gt;~/.blerc&lt;/code&gt; personnel. Franchement c&amp;rsquo;est pas le truc le plus rigolo que j&amp;rsquo;ai fait dans ma vie. C&amp;rsquo;est un peu ostère comme syntaxe, et ça demande des tests. Mais bon, on ne le fait pas tous les jours.&lt;/p&gt;
&lt;p&gt;Voilà le résultat:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/images/ble.gif&#34; alt=&#34;Ble en action&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;les-raccourcis-qui-vont-bien&#34;&gt;Les raccourcis qui vont bien&lt;/h2&gt;
&lt;p&gt;Bash permet pas mal de raccourcis dont certains me paraissent indispensables.&lt;/p&gt;
&lt;p&gt;Voici ceux que j&amp;rsquo;utilise très souvent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl+c&lt;/code&gt;: très connu mais sait-on jamai&amp;hellip; cela &amp;ldquo;stoppe&amp;rdquo; la commande en cours&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+r&lt;/code&gt; ou &lt;code&gt;Ctrl-Maj-r&lt;/code&gt;: permet de retrouver une commande qu&amp;rsquo;on a tapé&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+x Ctrl+e&lt;/code&gt;: édite la ligne en cours dans un éditeur de texte (celui configuré dans la variable EDITOR). Astuce, gardez la touche &lt;code&gt;Ctrl&lt;/code&gt; enfoncé et pressez successivement &lt;code&gt;x&lt;/code&gt; puis &lt;code&gt;e&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl+l&lt;/code&gt;: nettoie le terminal, &amp;ldquo;clear&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Alt+b&lt;/code&gt; et &lt;code&gt;Alt+f&lt;/code&gt;: reculer ou avancer d&amp;rsquo;un &amp;ldquo;mot&amp;rdquo; dans la commande (f=forwar, b=backward)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Alt+d&lt;/code&gt;: supprime le mot sous le curseur&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Un autre raccourcis que j&amp;rsquo;utilise énormément est &lt;code&gt;Ctrl+z&lt;/code&gt;, il met en &amp;ldquo;tâche de fond&amp;rdquo; la commande en cours. Pour revenir à cette tache, il suffit de taper &lt;code&gt;fg&lt;/code&gt;. Mais à quoi ça sert me direz vous ?&lt;/p&gt;
&lt;p&gt;Et bien, j&amp;rsquo;utilise &amp;ldquo;neovim&amp;rdquo; (un vim amélioré), et parfois j&amp;rsquo;ai envie de taper une commande sans avoir à ouvrir un nouveau terminal. Du coup, je presse &lt;code&gt;Ctrl+z&lt;/code&gt; dans vim, ça le met en tâche de fond. Je peux alors taper des commandes, puis taper &lt;code&gt;fg&lt;/code&gt; et je reviens à vim.&lt;/p&gt;
&lt;h2 id=&#34;mais-la-ligne-de-commande-cest-aussi-lié-à-un-terminal&#34;&gt;Mais la ligne de commande c&amp;rsquo;est aussi lié à un terminal&lt;/h2&gt;
&lt;p&gt;Bash est un shell, un outil qui interprète et utilise des commandes. Cela passe par un télétype, ou un émulateur de terminal. Dans 99% des cas, c&amp;rsquo;est un émulateur que vous allez utiliser.&lt;/p&gt;
&lt;p&gt;Par exemple, gnome-terminal, terminator ou xterm.&lt;/p&gt;
&lt;p&gt;Pour ma part, j&amp;rsquo;ai choisi un autre terminal. Certes, un peu plus lourd en mémoire mais qui a des options qui, à mon sens, sont primordiales quand on travaille énormément sur le terminal.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mon choix se porte, à ce jour, sur &lt;a href=&#34;https://sw.kovidgoyal.net/kitty/&#34;&gt;Kitty-Terminal&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Pour plusieurs raisons, mais celles que je mets en avant sont:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;permet de splitter le terminal (comme le fait terminator) et de changer les raccourcis&lt;/li&gt;
&lt;li&gt;permet l&amp;rsquo;utilisation de polices avec ligatures (comme Fira Code, ou Jetbrain Mono Nerd Font)&lt;/li&gt;
&lt;li&gt;permet d&amp;rsquo;afficher des images dans le terminal (notamment avec &lt;code&gt;ranger&lt;/code&gt;, un navigateur de fichier terminal)&lt;/li&gt;
&lt;li&gt;de scripter des comportements en python (par exemple j&amp;rsquo;ai un bout de code qui me permet de &amp;ldquo;zoomer&amp;rdquo; sur une partie de mon terminal quand il est splitté)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kitty est dans les paquets de Fedora, et je vous invite à le tester, à le configurer et de ne pas hésiter à aller lire la documentation.&lt;/p&gt;
&lt;p&gt;Depuis les dernières versions, &lt;code&gt;kitten&lt;/code&gt; est déjà présent, donc &lt;code&gt;kitten icat&lt;/code&gt; fonctionne bien. Mais vous pouvez ajouter cet alias dans &lt;code&gt;~/.bashrc.d/kitty.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# see image in terminal
alias icat=`kitten icat`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et ça fait son effet:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/images/icat.webp&#34; alt=&#34;icat&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2023/bien-configurer-son-terminal-bash/images/ranger-icat.webp&#34; alt=&#34;ranger&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;que-dire-de-plus&#34;&gt;Que dire de plus&amp;hellip;&lt;/h2&gt;
&lt;p&gt;Il existe tellement de possibilités de configuration de terminal, et de votre shell, qu&amp;rsquo;un simple article ne suffit bien évidemment pas.&lt;/p&gt;
&lt;p&gt;Je vous conseille d&amp;rsquo;installer et configurer &lt;code&gt;ranger&lt;/code&gt; qui est un superbe navigateur de fichiers terminal. Associé à &lt;code&gt;kitty&lt;/code&gt; c&amp;rsquo;est un outil fabuleux. Il ne faut pas hésiter à aller chercher un peu comment changer les thèmes. Que ce soit sur Kitty ou ranger.&lt;/p&gt;
&lt;p&gt;Aussi, au lieu de &lt;code&gt;top&lt;/code&gt; ou &lt;code&gt;htop&lt;/code&gt;, testez l&amp;rsquo;expérience &lt;code&gt;bpytop&lt;/code&gt; qui est, une fois encore, une preuve que le terminal est loin d&amp;rsquo;être un machin austère.&lt;/p&gt;
&lt;p&gt;Et en ce qui concerne les outils de développements, rien ne vaut &lt;a href=&#34;https://github.com/neoclide/coc.nvim&#34;&gt;Coc&lt;/a&gt; dans vim/neovim. Avec quelques plugins, votre éditeur devient un IDE proche de ce qu&amp;rsquo;on voit sur VSCode.&lt;/p&gt;
&lt;p&gt;Je pense en faire un article complet un jour.&lt;/p&gt;
&lt;p&gt;Bref, éclatez vous !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Comment récupérer une version ou un tag git efficace dans un Makefile</title>
      <link>https://www.metal3d.org/blog/2021/version-git-dans-un-makefile/</link>
      <pubDate>Mon, 06 Dec 2021 09:26:15 +0100</pubDate>
      
      <guid>https://www.metal3d.org/blog/2021/version-git-dans-un-makefile/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2021/version-git-dans-un-makefile/git-version-makefile-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;J&amp;rsquo;adore &amp;ldquo;versionner&amp;rdquo; mes paquets ou un executable avec le tag git ou la version &amp;ldquo;sha&amp;rdquo; du dépôt en cours, alors comment on peut rendre ça &lt;strong&gt;vraiment&lt;/strong&gt; automatique ?&lt;/p&gt;
&lt;p&gt;Ce billet sera court, c&amp;rsquo;est juste une astuce que j&amp;rsquo;ai dans un coin qui va vous permettre de faire une opération qui est généralement bien lourde à gérer. Cette petite technique, elle marche dans un Makefile, mais libre à vous de l&amp;rsquo;adapter pour d&amp;rsquo;autres systèmes de build.&lt;/p&gt;
&lt;p&gt;Je vous expose le problème. Imaginons que je décide que mon Makefile doive faire un &lt;em&gt;tarball&lt;/em&gt; de mon application. Et bien entendu je veux avoir la version dans le nom. En général on fait comme ça:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-Makefile&#34;&gt;# ! non ne faites PAS ça
VERSION=1.1.O

exemple:
    tar caf  foo-$(VERSION).tgz ./src

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et vous allez bien entendu vouloir utiliser un outil CI/CD pour lancer la commande qui va déposer le paquet quelque part. Bravo, c&amp;rsquo;est pas idiot, mais il y a un hic. Et un sérieux hic en fait: vous allez forcément oublier de changer la variable &lt;code&gt;VERSION&lt;/code&gt; dans le Makefile.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Non mais&amp;hellip; je vous connais&amp;hellip; et puis je fais pareil&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Alors vous allez me dire:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;oui mais moi mon CI/CD il va créer le tag, puis passer la version en argument etc&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Oui&amp;hellip; pourquoi pas, et puis c&amp;rsquo;est vrai que tout le monde utilise un outil CI/CD 🙄, et puis il va falloir aussi penser à &amp;ldquo;puller&amp;rdquo; le dépôt sur votre poste, sans compter que parfois &amp;ldquo;ça foire&amp;rdquo; sur l&amp;rsquo;outil CI/CD&amp;hellip; Allons allons 😌&lt;/p&gt;
&lt;p&gt;Il existe une façon de faire bien plus effcicace, un poil plus simple (si si) et surtout qui est &lt;strong&gt;sûre et contrôlable&lt;/strong&gt;. J&amp;rsquo;entends par ces termes que vous allez simplement taguer (ou pas) votre version, et le Makefile trouvera tout seul l&amp;rsquo;état, et donc la version, de votre dépôt.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée est simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;si j&amp;rsquo;ai un tag et que mon dépôt est bien à ce tag &lt;strong&gt;précisément&lt;/strong&gt; alors je veux que la version  soit égale à ce tag&lt;/li&gt;
&lt;li&gt;sinon je veux le nom de la branche + le &amp;ldquo;SHA&amp;rdquo; du commit actuel.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Trois commandes suffisent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git branch --show-current&lt;/code&gt; pour avoir le nom de la branche en cours, dans le cas où le dépôt n&amp;rsquo;est pas positionné sur un tag&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git log --pretty=&amp;quot;%h&amp;quot;&lt;/code&gt; va donner l&amp;rsquo;empreinte courte du commit en cours&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git describe --exact-match --tags SHA&lt;/code&gt; (avec SHA qui est le résultat de la commande du dessus), va me donner le tag si et seulement si le SHA correspond au commit du tag. Sinon il ne retourne rien.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et donc, dans votre Makefile, faites juste ça:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-Makefile&#34;&gt;CUR_SHA=$(shell git log -n1 --pretty=&#39;%h&#39;)
CUR_BRANCH=$(shell git branch --show-current)
VERSION=$(shell git describe --exact-match --tags $(CUR_SHA) 2&amp;gt;/dev/null || echo $(CUR_BRANCH)-$(CUR_SHA))

exemple:
    tar caf foo-$(VERSION).tgz ./src
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;si je viens de taguer, et que je n&amp;rsquo;ai rien touché, &lt;code&gt;VERSION&lt;/code&gt; contient le nom du tag&lt;/li&gt;
&lt;li&gt;sinon, &lt;code&gt;VERSION&lt;/code&gt; vaut &amp;ldquo;nom-de-la-branche-SHA&amp;rdquo; (par exemple &lt;code&gt;master-34ge12f&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Un exemple avec Go, pour builder avec la version du binaire en variable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-Makefile&#34;&gt;CUR_SHA=$(shell git log -n1 --pretty=&#39;%h&#39;)
CUR_BRANCH=$(shell git branch --show-current)
VERSION=$(shell git describe --exact-match --tags $(CUR_SHA) 2&amp;gt;/dev/null || echo $(CUR_BRANCH)-$(CUR_SHA))

build:
    go build -ldflags=&amp;quot;-X &#39;main.Version=$(VERSION)&#39;&amp;quot; .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, c&amp;rsquo;est tout bête mais je voulais vous le partager 😗&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Créer une application Web TypeScript / JS en un rien de temps</title>
      <link>https://www.metal3d.org/blog/2021/cr%C3%A9er-un-projet-typescript-ou-javascript-avec-parcel/</link>
      <pubDate>Mon, 01 Nov 2021 15:45:52 +0100</pubDate>
      
      <guid>https://www.metal3d.org/blog/2021/cr%C3%A9er-un-projet-typescript-ou-javascript-avec-parcel/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2021/cr%C3%A9er-un-projet-typescript-ou-javascript-avec-parcel/parcel-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;TypeScript c&amp;rsquo;est le pied, mais pas &amp;ldquo;from scratch&amp;rdquo;. On aime ce que propose Vue, Angular ou encore React en mode &amp;ldquo;dev&amp;rdquo; (live reload, HMR, etc&amp;hellip;) - alors comment on fait sans framework ? Et bien parlons de &amp;ldquo;Parcel&amp;rdquo; qui vient de sortir en version 2 et qui déchire de la licorne (et on parlera aussi de Vite ou SnowPack).&lt;/p&gt;
&lt;p&gt;Vous avez déjà codé une application &amp;ldquo;from scratch&amp;rdquo; en TypeScript et sans outil particulier ? Si oui, vous connaissez certainement le souci dont je vais vous parler: TypeScript ne remplace pas les &amp;ldquo;imports&amp;rdquo; - du coup, votre serveur Web ne trouve pas les ressources. Il faut faire la manipulation d&amp;rsquo;ajout d&amp;rsquo;extension. Ça c&amp;rsquo;est donc le premier souci.&lt;/p&gt;
&lt;p&gt;Et puis, soyons honnête, presser &lt;code&gt;F5&lt;/code&gt; pour rafraîchir la page à chaque modification, c&amp;rsquo;est pas compliqué mais c&amp;rsquo;est pas confortable.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Allons-y franchement, j&amp;rsquo;ai envie d&amp;rsquo;un truc qui ne me pose pas de question, qui me serve ma page pendant que je développe, qui compile correctement le TypeScript, le SCSS, qui me donne quelques possibilités pour les images etc&amp;hellip; et &lt;strong&gt;ne me prenne pas la tête&lt;/strong&gt; - ça doit rouler tout seul.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;J&amp;rsquo;ai déjà assez à faire avec mes bugs, pas envie de passer des heures à configurer la construction et/ou l&amp;rsquo;environnement de développement.&lt;/p&gt;
&lt;p&gt;Et c&amp;rsquo;est pour ça que, pour le moment, je ne pense pas à WebPack. Il est très puissant et configurable, mais moi j&amp;rsquo;ai pas besoin de ça. Franchement, chapeau bas à ceux et celles qui arrivent à configurer WebPack du premier coup.&lt;/p&gt;
&lt;p&gt;Et quid des dépendances à ajouter à &lt;code&gt;package.json&lt;/code&gt; pour faire du SCSS, à devoir gérer les versions, et potentiellement devoir adapter les &amp;ldquo;scripts&amp;rdquo; pour que tout soit compilé correctement pour avoir une version en développement et en production.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bref, c&amp;rsquo;est souvent pour cela que je passe directement par Vue ou Angular, pour m&amp;rsquo;éviter de devoir me coltiner ces tracas.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Mais j&amp;rsquo;aime bien coder &amp;ldquo;from scratch&amp;rdquo;, parce que je dois juste coder une librairie standard, parce que je veux tester un truc rapidement, bref j&amp;rsquo;ai pas tout le temps besoin d&amp;rsquo;avoir un framework complet.&lt;/p&gt;
&lt;p&gt;Autre truc, pour une petite application, j&amp;rsquo;ai des fragments de HTML que je veux utiliser dynamiquement, donc j&amp;rsquo;ai envie de les importer (à la manière de React, même si je déteste ce Framework) et de les placer dans le DOM. Et ce sans passer par &lt;code&gt;fetch()&lt;/code&gt;, juste &lt;code&gt;import &amp;quot;blabla.html&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Et dernier point non négligeable, j&amp;rsquo;ai envie d&amp;rsquo;utiliser &amp;ldquo;&lt;a href=&#34;https://getbootstrap.com/&#34;&gt;Bootstrap&lt;/a&gt;&amp;rdquo;, ou &amp;ldquo;&lt;a href=&#34;https://bulma.io/&#34;&gt;Bulma&lt;/a&gt;&amp;rdquo; - mais à chaque fois je me demande comment je vais faire pour avoir le droit de toucher à des variables, compiler correctement le bouzin et l&amp;rsquo;intégrer dans le projet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tout ça, tout ce dont je vous parle, c&amp;rsquo;est ni plus ni moins qu&amp;rsquo;un besoin de simplification: ne pas utiliser de framework, juste coder, éliminer les dépendances.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et bien depuis quelques années, une famille d&amp;rsquo;outil est arrivé pour ça: les &lt;em&gt;Front Build Tools&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ce que proposent ces outils:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un serveur de développement, &lt;strong&gt;qui se recharge tout seul lors des modifications&lt;/strong&gt;, et recharge la page lors de l&amp;rsquo;injection de module (HMR, Hot Module Replacement)&lt;/li&gt;
&lt;li&gt;construction de l&amp;rsquo;application &lt;strong&gt;quelque soit la ou les technologies choisies&lt;/strong&gt; (TypeScript, SCSS, JS, CSS, Less&amp;hellip;)&lt;/li&gt;
&lt;li&gt;transforme les ressources non code en ressources de code (une image, une page HTML, une feuille de style, vous pouvez l&amp;rsquo;importer et la gérer)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il en existe tellement, &lt;a href=&#34;https://vitejs.dev&#34;&gt;ViteJS&lt;/a&gt;, &lt;a href=&#34;https://www.snowpack.dev/&#34;&gt;SnowPack&lt;/a&gt; ou encore, celui qui m&amp;rsquo;intéresse le plus et dont on va parler: &lt;a href=&#34;https://parceljs.org&#34;&gt;Parcel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Je vous expliquerai pourquoi j&amp;rsquo;utilise Parcel et non pas Vite ou SnowPack. Mais vous verrez que ce choix n&amp;rsquo;est pas figé, et que vous aurez tout à fait raison d&amp;rsquo;utiliser les autres.&lt;/p&gt;
&lt;p&gt;Mais d&amp;rsquo;abord, commençons par regarder Parcel.&lt;/p&gt;
&lt;h1 id=&#34;bon-alors-parcel&#34;&gt;Bon alors Parcel&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Voilà en gros ce que je fais pour commencer à travailler:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# je crée un répertoire de travail
mkdir -p ~/Projects/test-parcel
cd !$

# je crée le projet, avec yarn, mais vous pourriez utiliser npm
yarn init -y

# on installe Parcel en dépendance de développement
yarn add -D parcel

# je remplace dans `package.json` la ligne 
# &amp;quot;main&amp;quot;: &amp;quot;index.js&amp;quot;
# par:
# &amp;quot;source&amp;quot;: &amp;quot;src/index.html&amp;quot;
sed -i &#39;s,&amp;quot;main&amp;quot;: &amp;quot;index.js&amp;quot;,&amp;quot;source&amp;quot;: &amp;quot;src/index.html&amp;quot;,&#39; package.json

# je créée la page
mkdir src
echo &#39;&amp;lt;p&amp;gt;Yop&amp;lt;/p&amp;gt;&#39; &amp;gt; src/index.html

# et je lane le service de dev
yarn parcel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, allez à la page http://localhost:1234 et vous avez votre page qui tourne.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;La moindre modification de vos sources va rafraîchir la vue. Déjà ça c&amp;rsquo;est bon !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et donc, pour le TypeScript ? Facile&amp;hellip; Ajoutez un fichier &lt;code&gt;src/main.ts&lt;/code&gt; par exemple, et tapez:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;class Foo (){
    constructor(private name: string) {}

    hello() {
        console.log(this.name);
    }
}

const f = new Foo(&amp;quot;Youpi ?&amp;quot;);
f.hello();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et dans index.html:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;script type=&amp;quot;module&amp;quot; src=&amp;quot;main.ts&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà, c&amp;rsquo;est tout&amp;hellip; Parcel va utiliser un &lt;em&gt;transpiler&lt;/em&gt; pour vous, tout va bien. La page est rafraîchie et dans la console (du navigateur) vous devriez voir apparaître un &amp;ldquo;Youpi ?&amp;rdquo;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pas une ligne de configuration, rien, que dalle. Ça marche et c&amp;rsquo;est tout ce qu&amp;rsquo;on voulait.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;non-mais-en-vrai-on-configure-un-truc-ou-deux-non-&#34;&gt;Non mais en vrai, on configure un truc ou deux non ?&lt;/h1&gt;
&lt;p&gt;Oui alors&amp;hellip; Parcel n&amp;rsquo;est pas parfait. Il balance tout dans un répertoire nommé &amp;ldquo;dist&amp;rdquo; et je trouve ça pas super pertinent. Parce qu&amp;rsquo;en plus il ne nettoie pas sa chambre ce bougre. Donc on va améliorer un peu en touchant &lt;code&gt;package.json&lt;/code&gt;, voici ce que je fais par habitude:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;&amp;quot;scripts&amp;quot; : {
    &amp;quot;predev&amp;quot;: &amp;quot;rm -rf .tmp&amp;quot;,
    &amp;quot;dev&amp;quot; : &amp;quot;parcel --dist-dir .tmp&amp;quot;,
    &amp;quot;prebuild&amp;quot;: &amp;quot;rm -rf dist&amp;quot;,
    &amp;quot;build&amp;quot;: &amp;quot;parcel build&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, comme ça je tape juste &lt;code&gt;yarn dev&lt;/code&gt; ou &lt;code&gt;yarn build&lt;/code&gt; et tout est propre.&lt;/p&gt;
&lt;h1 id=&#34;et-pour-le-scss-&#34;&gt;Et pour le SCSS ?&lt;/h1&gt;
&lt;p&gt;Parcel n&amp;rsquo;est pas qu&amp;rsquo;un bundler ou un serveur de dev. Comme ses confrères il a quelques tours dans sa manche.&lt;/p&gt;
&lt;p&gt;Partons d&amp;rsquo;un exemple simple, j&amp;rsquo;ai envie d&amp;rsquo;utiliser &amp;ldquo;Bootstrap&amp;rdquo;, parce que&amp;hellip; je l&amp;rsquo;ai décidé. Tout va aller très vite:&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord j&amp;rsquo;ajoute bootstrap en dépendance de développement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;yarn add -D bootstrap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là vous avez deux solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;soit vous codez salement comme dans React&lt;/li&gt;
&lt;li&gt;soit vous décidez de faire les choses plus proprement&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, pour les gorets, dans &lt;code&gt;main.ts&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;import &#39;../node_modules/bootstrap/scss/bootstrap.scss&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et sinon pour les vrais professionnels, vous créez un fichier &lt;code&gt;styles.scss&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-css&#34;&gt;@import &#39;../node_modules/bootstrap/scss/bootstrap.scss&#39;
/* Et là vous pouvez mettre vos adaptations, changements de couleurs etc... */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et vous ajoutez cela dans &lt;code&gt;index.html&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;styles.scss&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans les deux cas, il va se passer un truc &amp;ldquo;étrange&amp;rdquo;, mais tellement génial.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Parcel va, tout seul, ajouter une &amp;ldquo;devDependencies&amp;rdquo; dans &lt;code&gt;package.json&lt;/code&gt; - un plugin qui va compiler le SCSS et le placer où il faut.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Sans redémarrer le service, sans rien faire de spécial, ça va marcher.&lt;/p&gt;
&lt;p&gt;Et bien entendu, en TypeScrip, vous pouvez utiliser &amp;ldquo;bootstrap&amp;rdquo; pour injecter des alertes, des &amp;ldquo;toast&amp;rdquo;, etc. D&amp;rsquo;ailleurs, &lt;a href=&#34;https://getbootstrap.com/docs/5.0/getting-started/parcel/&#34;&gt;Bootstrap propose une petite page de documentation pour utiliser Parcel&lt;/a&gt; et je vous conseille bien entendu d&amp;rsquo;aller la lire !&lt;/p&gt;
&lt;h1 id=&#34;et-tu-parlais-dimport-de-ressources-&#34;&gt;&amp;ldquo;Et tu parlais d&amp;rsquo;import de ressources ?&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;C&amp;rsquo;est un truc qui marche aussi dans les autres build tools, mais chez Parcel j&amp;rsquo;aime beaucoup comment on s&amp;rsquo;en sert.&lt;/p&gt;
&lt;p&gt;Faire un fichier &lt;code&gt;config.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yml&#34;&gt;mode: prod
title: &amp;quot;[DEV] Exemple de code&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et importer ça comme ça dans du TypeScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;import config from &amp;quot;config.yaml&amp;quot;;

if (config.mode == &amp;quot;prod&amp;quot;) {
    // et faire des choses ici
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En gros, les ressources qui ne sont pas des fichier JS/TS sont quand mêmes utilisables.&lt;/p&gt;
&lt;p&gt;Quand j&amp;rsquo;ai besoin de faire des fragments de code HTML, j&amp;rsquo;avoue que l&amp;rsquo;import est plus tordu. Prenons un exemple, dans mon &lt;code&gt;index.html&lt;/code&gt; je vais faire ça:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et j&amp;rsquo;ajoute un fichier &lt;code&gt;content.html&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;p&amp;gt;Et voilà le contenu !&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans le &lt;code&gt;main.ts&lt;/code&gt; voilà ce que je peux faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;// comme je veux le texte, je demande un import &amp;quot;bundle-text&amp;quot;
import  content from &amp;quot;bundle-text:./content.html&amp;quot;;

document.querySelector(&amp;quot;#app&amp;quot;).innerHTML = content
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tout est expliqué &lt;a href=&#34;https://parceljs.org/features/bundle-inlining/&#34;&gt;dans la page qui parle de bundle inline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Il faut utiliser ça avec parcimonie, n&amp;rsquo;est-ce pas chers amis fans de React&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;et-quand-on-foire-&#34;&gt;Et quand on foire ?&lt;/h1&gt;
&lt;p&gt;Pas de panique, que ce soit dans la console ou le navigateur, l&amp;rsquo;erreur apparaît et vous aide beaucoup à corriger.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2021/cr%C3%A9er-un-projet-typescript-ou-javascript-avec-parcel/Parcel-error.webp&#34; alt=&#34;Exemple d&amp;rsquo;erreur qui apparait dans le navigateur&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;mais-vraiment-pour-typescript-ajoutez-des-dépendances&#34;&gt;Mais vraiment, pour Typescript, ajoutez des dépendances&lt;/h1&gt;
&lt;p&gt;Parcel s&amp;rsquo;occupe de construire et empaqueter, mais il ne paramètre pas votre IDE. C&amp;rsquo;est pourquoi j&amp;rsquo;ajoute quand même deux dépendances:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;yarn add -D eslint@^7 typescript

# et je génère les fichiers de conf
yarn tsc --init
yarn eslint --init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aux questions de &lt;code&gt;eslint&lt;/code&gt; je fais en sorte de prendre en compte TypeScript, je &amp;ldquo;réponds aux question de style&amp;rdquo; pour forcer les &amp;ldquo;double-quote&amp;rdquo; et les point-virgules, et supprime finalement la partie &amp;ldquo;indent&amp;rdquo; du fichier &lt;code&gt;.eslintrc.json&lt;/code&gt; - parce que finalement, 2 espaces c&amp;rsquo;est pas si mal&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;En utilisant &amp;ldquo;NeoVim&amp;rdquo; + &amp;ldquo;Coc&amp;rdquo; (&lt;a href=&#34;https://github.com/neoclide/coc.nvim&#34;&gt;https://github.com/neoclide/coc.nvim&lt;/a&gt;) j&amp;rsquo;ai de quoi coder, avec des imports automatiques, des correction automatiques, documentation et autocompletion, le tout dans le terminal&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2021/cr%C3%A9er-un-projet-typescript-ou-javascript-avec-parcel/./nvim1.webp&#34; alt=&#34;NeoVim TS error&#34;&gt;
&lt;img src=&#34;https://www.metal3d.org/blog/2021/cr%C3%A9er-un-projet-typescript-ou-javascript-avec-parcel/./nvim2.webp&#34; alt=&#34;NeoVim TS documentation&#34;&gt;&lt;/p&gt;
&lt;p&gt;Bref, zero-conf oui, mais faut pas non plus faire la feignasse tout le long, avoir de quoi bosser proprement ça demande un peu de paramétrage, soyons sérieux.&lt;/p&gt;
&lt;h1 id=&#34;alors-donc-pourquoi-pas-vite-ou-snowpack-&#34;&gt;Alors donc, pourquoi pas Vite ou SnowPack ?&lt;/h1&gt;
&lt;p&gt;&amp;ldquo;Vite&amp;rdquo; et &amp;ldquo;SnowPack&amp;rdquo; sont excellents. Je ne vais pas dire le contraire et je pousse les gens à regarder les trois outils.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Vite&amp;rdquo; est d&amp;rsquo;ailleurs bien plus populaire. Pour des raisons certainement très défendables.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors, calmez vous les fans de Vite et SnowPack, je ne dis pas qu&amp;rsquo;ils sont moins bons - attendez de lire ce qui suit.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;SnowPack ne correspond pas à mon besoin, il demande une structure et je dois passer par une phase &amp;ldquo;d&amp;rsquo;init&amp;rdquo; (&lt;code&gt;yarn snowpack init&lt;/code&gt;, changer le &amp;ldquo;root&amp;rdquo; etc&amp;hellip;) - il est certes puissant et très performant mais je cherche avant tout, justement, à me passer de la configuration. SnowPack a un autre objectif et dans le cas que je présente ici, ce n&amp;rsquo;est pas le plus adapté.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Vite&amp;rdquo; était à deux doigts de me convenir, mais&amp;hellip; il ne prend pas la base de code par défaut en passant par &lt;code&gt;package.json&lt;/code&gt;, et du coup les &amp;ldquo;imports&amp;rdquo; de ressources typescript, ainsi que SCSS ou autres, ne sont pas &amp;ldquo;logiques&amp;rdquo; à mes yeux. Le souci est que &amp;ldquo;Vite&amp;rdquo; prend la base de code à la racine du projet, et que vous devez donc forcer le chemin au démarrage du serveur web. Et bon bha c&amp;rsquo;est pas aussi facile et rapide à utiliser au final.&lt;/p&gt;
&lt;p&gt;Alors que dire ? qu&amp;rsquo;il ne faut pas les utiliser ? &lt;strong&gt;Certainement pas !&lt;/strong&gt; - ils sont au moins aussi bon que Parcel, mais ils sont adaptés à d&amp;rsquo;autres situations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pour être exact, j&amp;rsquo;utilise aussi &amp;ldquo;Vite&amp;rdquo; ou &amp;ldquo;SnowPack&amp;rdquo;, mais dans des conditions différentes. Par exemple j&amp;rsquo;adore les templates de &amp;ldquo;Vite&amp;rdquo; pour VueJS. Vous ne devez en aucun cas ignorer les autres outils.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Bref, Parcel n&amp;rsquo;est peut-être pas ce dont &lt;strong&gt;vous&lt;/strong&gt; avez besoin et vous auriez tout à fait raison de passer par un autre outil zero-conf.&lt;/p&gt;
&lt;p&gt;Parcel est, à mon sens, parfait pour le prototypage et le &amp;ldquo;build simplifié&amp;rdquo; - il est pratique pour sa qualité de HMR et sa gestion super intelligente des imports de ressources. Mais il a ses défaut (plus lent, plus lourd, difficile de sortir de l&amp;rsquo;obscurification, etc&amp;hellip;) - je n&amp;rsquo;abandonne donc pas ses confrères (je n&amp;rsquo;utilise pas le mot &amp;ldquo;concurrent&amp;rdquo;, car ils ne le sont clairement pas), j&amp;rsquo;utilise juste le bon outil pour le bon besoin.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;allez-y, trollez sur les indentations !&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;allez-y, trollez sur vim !&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>Kind - Kubernetes local, sans Docker ? avec Podman !</title>
      <link>https://www.metal3d.org/blog/2021/kind-avec-podman/</link>
      <pubDate>Tue, 16 Feb 2021 13:45:53 +0100</pubDate>
      
      <guid>https://www.metal3d.org/blog/2021/kind-avec-podman/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2021/kind-avec-podman/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Kind (Kubernetes in Docker) avec Podman au lieu de Docker, avec un utilisateur &amp;ldquo;normal&amp;rdquo; - disons, en évitant au maximum de passer par &amp;ldquo;sudo&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Soyons clairs, &lt;strong&gt;je n&amp;rsquo;ai rien contre Docker&lt;/strong&gt;. Mais Docker, c&amp;rsquo;est &amp;ldquo;une méthode parmis d&amp;rsquo;autres&amp;rdquo; pour créer des conteneurs. Et force est de constater que depuis quelques temps je me détourne de cette manière de faire pour diverses raisons.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;L&amp;rsquo;une d&amp;rsquo;entre elles est le fait qu&amp;rsquo;il en bave avec les nouvelles gestions de hiérarchie unifiée cgroup&amp;hellip; Depuis Fedora 33, nous sommes forcés de dégrader le fonctionnement en injectant un paramètre à la ligne de démarrage du noyau Linux. Et ça, çe me gène.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Il existe d&amp;rsquo;autres &amp;ldquo;runners&amp;rdquo;, comme &amp;ldquo;CRI-O&amp;rdquo;, &amp;ldquo;runc&amp;rdquo;&amp;hellip; et bien que nous avons une mouture de Docker maison nommée &amp;ldquo;moby-engine&amp;rdquo; sur Fedora, je me range de plus en plus du côté de &lt;strong&gt;podman&lt;/strong&gt;. J&amp;rsquo;adore le principe.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pas de service qui tourne en fond&lt;/li&gt;
&lt;li&gt;Gestion des images locales à l&amp;rsquo;utilisateur (dans &lt;code&gt;~/.local/share/containers&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Donc&amp;hellip; pas de groupe à gérer (avec Docker il faut être dans le groupe &lt;code&gt;docker&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;podman-compose&lt;/code&gt; très compatible à &lt;code&gt;docker-compose&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est propre.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et, bien entendu, c&amp;rsquo;est compatible avec Docker - les pulls d&amp;rsquo;images fonctionnent, les conteneurs ont la même tête et les commandes de démarrage sont très proches.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;La seule vraie grosse différence, et elle fait un peu mal, c&amp;rsquo;est que vos conteneurs n&amp;rsquo;auront plus d&amp;rsquo;IP à proprement parler (si vous les démarrez en tant qu&amp;rsquo;uitilisateur &amp;ldquo;normal&amp;rdquo;). Aussi, ils sont injectés dans des &lt;strong&gt;Pods&lt;/strong&gt;, tout comme sur Kubernetes - et donc on se familarise plus vite à cette notion. L&amp;rsquo;avantage est que les conteneurs au sein d&amp;rsquo;un même pod communiquent avec l&amp;rsquo;adresse &amp;ldquo;localhost&amp;rdquo;, mais ça surprend un peu de ne pas pouvoir utiliser un vieux dnsmasq de derrière les bottes de foins pour taper sur le conteneur. Rien de bien méchant mais ça peut être une contrainte.&lt;/p&gt;
&lt;p&gt;OK donc, je passe à Podman, et je suis content. Mais je bosse beaucoup sur Kubernetes&amp;hellip; et donc j&amp;rsquo;ai besoin de tester localement mes déploiements.&lt;/p&gt;
&lt;h1 id=&#34;kind-&#34;&gt;Kind ?&lt;/h1&gt;
&lt;p&gt;Si vous ne connaissez pas &lt;a href=&#34;https://kind.sigs.k8s.io/&#34;&gt;kind&lt;/a&gt;, je vous invite à aller voir la page de documentation. Si vous n&amp;rsquo;avez pas &amp;ldquo;Go&amp;rdquo;, pas de panique, &lt;a href=&#34;https://github.com/kubernetes-sigs/kind/releases&#34;&gt;passez par la page Github&lt;/a&gt; pour prendre le binaire.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ce binaire, placez le dans un répertoire accessible dans &lt;code&gt;$PATH&lt;/code&gt;. J&amp;rsquo;utilise personnellement &lt;code&gt;~/.local/bin&lt;/code&gt;, mais je sais que certaines distributions (Ubu&amp;hellip;) ont du mal avec les standards, donc débrouillez vous car dans l&amp;rsquo;article, je vais utiliser la commande &amp;ldquo;kind&amp;rdquo; en partant du principe qu&amp;rsquo;elle est accessible sans donner le chemin.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Bref, en gros, Kind propose de démarrer un cluster Kubernetes en quelques secondes, sans VM, via&amp;hellip; Docker&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ouais&amp;hellip; Docker&amp;hellip; Kind veut Docker, ou du moins il &amp;ldquo;préfère&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Pas de panique, &lt;strong&gt;Kind supporte podman&lt;/strong&gt; de manière &amp;ldquo;expérimentale&amp;rdquo;. Alors bref, je me dis &amp;ldquo;bon, dégageons Docker et on y va&amp;rdquo;. (Oui j&amp;rsquo;ai vraiment supprimé le paquet &amp;ldquo;moby-engine&amp;rdquo; et/ou &amp;ldquo;docker&amp;rdquo; de mon poste)&lt;/p&gt;
&lt;p&gt;Et là, le drame:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ kind create cluster
enabling experimental podman provider
Creating cluster &amp;quot;kind&amp;quot; ...
podman provider does not work properly in rootless mode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Arrgghhhh oui, zut&amp;hellip; Kind veut créer un CNI du coup, et il a aucun droit car je ne suis pas &amp;ldquo;root&amp;rdquo;. La raison est simple, Kind a besoin d&amp;rsquo;une IP pour le ou les conteneurs qu&amp;rsquo;il va créer pour simuler les &amp;ldquo;masters/nodes&amp;rdquo;, et donc il faut que Podman puisse créer la ressource CNI (ou utiliser celle qui existe pour les conteneurs non &amp;ldquo;&lt;em&gt;rootless&lt;/em&gt;&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;Et bien pas de panique ! On va passer un peu outre en utilisant jute un peu &amp;ldquo;sudo&amp;rdquo; mais vraiment qu&amp;rsquo;un peu.&lt;/p&gt;
&lt;h1 id=&#34;dabord-on-démarre-le-cluster&#34;&gt;D&amp;rsquo;abord, on démarre le cluster&lt;/h1&gt;
&lt;p&gt;Bon, on va pas y couper, il faut passer par &amp;ldquo;sudo&amp;rdquo; pour démarrer le cluster, ça c&amp;rsquo;est pas sorcier et &amp;ldquo;tant pis&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo /home/metal3d/.local/share/go/bin/kind create cluster
enabling experimental podman provider
Creating cluster &amp;quot;kind&amp;quot; ...
 ✓ Ensuring node image (kindest/node:v1.20.2) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to &amp;quot;kind-kind&amp;quot;
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors, pour ma part, je force l&amp;rsquo;utilisation du chemin complet pour atteindre le binaire &amp;ldquo;kind&amp;rdquo;, parce que j&amp;rsquo;ai installé depuis les sources avec &amp;ldquo;go install&amp;rdquo;, mais si vous avez posé le binaire dans un &lt;code&gt;PATH&lt;/code&gt; accessible par tout le monde, pas de souci, mettez juste &lt;code&gt;sudo kind create cluster&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Bon, jusque là tout vas bien, kind a démarré et il est accessible&amp;hellip; &lt;strong&gt;par root&amp;hellip; !?&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# ne marche pas
kubectl get nodes

# marche bien...
sudo kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Hors de question de taper &amp;ldquo;sudo&amp;rdquo; pour toutes les commandes !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On fait quoi ? On se met un bon vieux &lt;a href=&#34;https://www.youtube.com/watch?v=JJZ0wQvtl_Y&#34;&gt;Eric Bibb&lt;/a&gt; en repensant au bon vieux temps où on se disait &amp;ldquo;non mais, pas grave je vais bosser en root directement&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NON !&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;on-évite-sudo-&#34;&gt;On évite sudo ?&lt;/h1&gt;
&lt;p&gt;Et bien c&amp;rsquo;est simple, là, la seule chose qui fait que seul &amp;ldquo;root&amp;rdquo; ait accès au cluster ce n&amp;rsquo;est pas parce que le &amp;ldquo;pod&amp;rdquo; a été démarré par &amp;ldquo;root&amp;rdquo;, non non non, c&amp;rsquo;est que la configuration d&amp;rsquo;accès (le &amp;ldquo;kubeconfig&amp;rdquo;) a été écrite sur &lt;code&gt;/root&lt;/code&gt;, on va juste la récupérer !&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo cp /root/.kube/config ~/.kube/kind-config
sudo chown $(id -u):$(id -g) ~/.kube/kind-config

# Si vous savez ce que vous faites:
# ln -sf ~/.kube/kind-config ~/.kube/config
# Sinon, utilisez la variable `KUBECONFIG` pour la placer sur ce fichier:
# export KUBECONFIG=~/.kube/kind-config
# et pensez-y... tout le temps !

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OK, on y est, testons:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:40113
KubeDNS is running at https://127.0.0.1:40113/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use &#39;kubectl cluster-info dump&#39;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà&amp;hellip; C&amp;rsquo;était simple &lt;span class=&#34;emoji&#34;&gt;😇&lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Soyons précis, désormais on a accès au cluster local parce que le &amp;ldquo;Kubeconfig&amp;rdquo; est généré pour &lt;strong&gt;ce&lt;/strong&gt; cluster. Mais si vous le supprimez et que vous le recrééz, il faudra récupérer la configuration &lt;code&gt;/root/.kube/config&lt;/code&gt; pour la placer dans votre HOME. Car cette configuration contient le certificat nécessaire pour s&amp;rsquo;authentifier sur le cluster. Compris ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;on-va-un-peu-plus-loin-&#34;&gt;On va un peu plus loin ?&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Cette partie fonctionne avec Kind démarré avec Docker ou Podman donc pas de différence hormis le fait que la récupération de l&amp;rsquo;IP du noeud se fait &amp;ldquo;sans sudo&amp;rdquo; avec Docker.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Perso, j&amp;rsquo;aime pouvoir accéder à mes tests sans passer par un &amp;ldquo;port-forward&amp;rdquo; ou un NodePort. Donc, vous devez le savoir, la technique est d&amp;rsquo;utiliser un ingress-conroller et de créer des ingress.&lt;/p&gt;
&lt;p&gt;Le souci, bha&amp;hellip; on a pas de nom de domaine local. Sauf que, des gens bien intentionnés vous permettent d&amp;rsquo;utiliser un domaine particulier qui est très pratique. Je vous en cite 3 mais je suis persuadé qu&amp;rsquo;il en existe d&amp;rsquo;autres:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;xip.io&lt;/li&gt;
&lt;li&gt;nip.io&lt;/li&gt;
&lt;li&gt;sslip.io&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tous ces domaines sont très pratiques, il vous permettent de placer une IP dans le nom de domaine ainsi qu&amp;rsquo;un sous-domaine. Par exemple:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;domaine&lt;/th&gt;
          &lt;th&gt;résolution&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;10.2.3.4.xip.io&lt;/td&gt;
          &lt;td&gt;10.2.3.4&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;app.10.2.3.4.xip.io&lt;/td&gt;
          &lt;td&gt;10.2.3.4&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;mon.app.10.2.3.4.xip.io&lt;/td&gt;
          &lt;td&gt;10.2.3.4&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Bon vous avez compris&amp;hellip; on met ce qu&amp;rsquo;on veut en IP dans le domaine, et ça répond avec cette IP. Pratique !&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ouais sauf que&amp;hellip; Free et SFR ont une facheuse tendance à foutre la merde avec les DNS, et j&amp;rsquo;ai donc pas mal de collègues qui en bavent. En gros, avec Free et SFR vous avez une chance sur dix que ça passe. Et même en forçant les DNS c&amp;rsquo;est le boxon (SFR filtre le port 53 il parait). Donc, si vous êtes chez ces FAI, vous allez potentiellement en baver un peu.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Bon, on installe un &amp;ldquo;ingress-controller&amp;rdquo;, par exemple l&amp;rsquo;excellent &amp;ldquo;ingress-nginx&amp;rdquo; que je trouve au poil:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# On ajoute le dépot qui va bien
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

# on installe ça dans un namespace nommé &amp;quot;infra&amp;quot; (valeur modifiable)
NS=&amp;quot;_change_infra&amp;quot;
helm install ingress-controller ingress-nginx/ingress-nginx \
    --set controller.hostNetwork=true \
    --namespace=$NS --create-namespace
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et après quelques secondes l&amp;rsquo;ingress-controller est présent, reste à avoir l&amp;rsquo;IP du noeud prinicpal (ou d&amp;rsquo;un des noeuds worker si vous avez démarré plusieurs workers, donc faites un choix):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo podman inspect kind-control-plane -f &#39;{{.NetworkSettings.Networks.kind.IPAddress}}&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;L&amp;rsquo;ip qui apparait, vous la notez et vous la gardez dans un coin.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;À partir de maintenant, quand vous paramétrez un ingress, vous donner en hostname &amp;ldquo;ce-que-vous-voulez.ip-trouvée.xip.io&amp;rdquo;, et c&amp;rsquo;est gagné.&lt;/p&gt;
&lt;p&gt;Par exemple, pour tester, on va se déployer un petit serveur nginx:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Récupération de l&#39;ip si vous ne l&#39;avez pas encore
KIND_IP=$(sudo podman inspect kind-control-plane \
    -f &#39;{{.NetworkSettings.Networks.kind.IPAddress}}&#39;)

# ajout du dépot de bitnami (bon sang que je les aime)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# le sous-domaine voulu, préfix de xip
DOMAIN=&amp;quot;_change_example&amp;quot;

# Namespace pour tester
NS=&amp;quot;_change_testapp&amp;quot;

# on installe nginx en indiquant une adresse
helm install _change_mon-application bitnami/nginx \
    --namespace=$NS --create-namespace \
    --set ingress.enabled=true \
    --set ingress.hostname=&amp;quot;$DOMAIN.$KIND_IP.xip.io&amp;quot;

echo &amp;quot;Visitez http://$DOMAIN.$KIND_IP.xip.io&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&amp;rsquo;adresse apparait donc à la fin, et vous devriez pouvoir visiter la page web indiquée. Si tout va bien, après quelques secondes/minutes d&amp;rsquo;installation selon la vitesse de votre machine, vous devriez avoir une belle page de &amp;ldquo;Welcome&amp;rdquo; de la part de nginx. C&amp;rsquo;est tout bon !&lt;/p&gt;
&lt;h1 id=&#34;alors-podman-&#34;&gt;Alors, podman ?&lt;/h1&gt;
&lt;p&gt;Honnêtement, podman est un bonheur. Aujourd&amp;rsquo;hui je construis mes images avec, j&amp;rsquo;utilise &amp;ldquo;podman-compose&amp;rdquo; et je n&amp;rsquo;ai ressenti que très peu de vraies grosses différences hormis le fait que je n&amp;rsquo;ai pas d&amp;rsquo;IP direct. Mais&amp;hellip; Je me suis surtout rendu compte que je n&amp;rsquo;ai pas tant besoin que ça d&amp;rsquo;un IP de conteneur. Mais je ne dis pas non plus que c&amp;rsquo;est inutile, disnons que j&amp;rsquo;ai appris à m&amp;rsquo;en passer.&lt;/p&gt;
&lt;p&gt;Kind version 0.10.x fonctionne vraiment bien avec podman. Je n&amp;rsquo;ai à ce jour aucun souci. Maintenant, clairement, au travail j&amp;rsquo;utilise toujours docker pour des raisons évidentes de possibles manques de compatibilité avec mes coéquipiers. Cela-dit, je pourrais m&amp;rsquo;en passer&amp;hellip; J&amp;rsquo;expérimente en ce moment.&lt;/p&gt;
&lt;p&gt;Podman ne permet pas pour le moment d&amp;rsquo;avoir des IP en mode &amp;ldquo;rootless&amp;rdquo; (c&amp;rsquo;est-à-dire si vous démarrez des conteneurs avec votre utilisateur, sans sudo), c&amp;rsquo;est donc pour cela qu&amp;rsquo;il faut passer par &amp;ldquo;sudo&amp;rdquo; pour démarrer le cluster local. Mais en déportant la configuration de kubectl sur votre répertoire personnel, vous n&amp;rsquo;aurez pas de souci.&lt;/p&gt;
&lt;p&gt;Donc, en somme, I &lt;span class=&#34;emoji&#34;&gt;💙&lt;/span&gt; Podman et Kind.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Python, Jouez Avec Les Annotations de Classes</title>
      <link>https://www.metal3d.org/blog/2021/python-jouez-avec-les-annotations/</link>
      <pubDate>Sun, 24 Jan 2021 11:30:51 +0100</pubDate>
      
      <guid>https://www.metal3d.org/blog/2021/python-jouez-avec-les-annotations/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2021/python-jouez-avec-les-annotations/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Et si on se servait des annotations pour faire autre chose que du simple &amp;ldquo;type checking&amp;rdquo; dans les IDE ? Par exemple, si on rendait la sérialisation JSON ou la création d&amp;rsquo;un système de modèle pour BDD plus élégant ?&lt;/p&gt;
&lt;p&gt;Les annotations, on s&amp;rsquo;en sert de plus en plus en Python, ça aide la compréhension du code, et les IDE adorent ça pour vous proposer de l&amp;rsquo;autocompletion efficacement. Mais je trouve qu&amp;rsquo;on ne se sert pas assez des annotations pour &amp;ldquo;tout le reste&amp;rdquo; - et notamment pour rendre la configuration de classe bien plus pratique. Je vais d&amp;rsquo;abord vous expliquer rapidement l&amp;rsquo;état des lieux, puis je vais vous montrer comment on peut utiliser les annotations pour &amp;ldquo;configurer une classe&amp;rdquo;. Cela peut rendre vos projets bien plus jolis à lire, à modifier, et surtout à contrôler.&lt;/p&gt;
&lt;h1 id=&#34;ce-que-vous-faisiez-avant&#34;&gt;Ce que vous faisiez avant&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Imaginons que nous devions gérer un ensemble  d&amp;rsquo;objets très hétérogène, c&amp;rsquo;est souvent le cas qu&amp;rsquo;on veut travailler avec des objets à stocker en base de données. Par exemple,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;un &amp;ldquo;User&amp;rdquo; a un nom, un prénom, un email, une date de naissance&amp;hellip; Il faut contrôler que les types sont bons, mais aussi ne pas exporter les propriétés inutiles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mais vous aurez aussi un autre type d&amp;rsquo;objet. Par exemple un &amp;ldquo;Projet&amp;rdquo; a un nom, une description, mais surtout un responsable qui est de type &amp;ldquo;&lt;strong&gt;User&lt;/strong&gt;&amp;rdquo; (ou autre) , etc&amp;hellip;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Du coup vous allez composer les objets avec d&amp;rsquo;autres objets. C&amp;rsquo;est normal, c&amp;rsquo;est logique, mais ça pose un souci. Parce que quand vous voulez exporter ça en JSON:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TypeError: Object of type Project is not JSON serializable
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Arrrrrggggg ouais&amp;hellip;(suivi de pleurs, &lt;a href=&#34;https://www.youtube.com/watch?v=tH2w6Oxx0kQ&#34;&gt;Dust in the wind&lt;/a&gt; à fond, etc&amp;hellip;)&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Après quelques minutes à trouver pourquoi il vous insulte de la sorte, alors que vous êtes gentil hein, on vous propose dans des documentations de faire des méthodes de sérialisation, et de boucler les propriétés pour ne sortir que ce dont vous avez besoin. Et puis vous ajoutez des propriétés, et vous recommencez à devoir manipuler les méthodes et ainsi de suite. En gros, chaque classe dérive de &amp;ldquo;JsonMachin&amp;rdquo; et vous devez surcharger la méthode &amp;ldquo;&lt;code&gt;to_json()&lt;/code&gt;&amp;rdquo; pour dire quelles propriétés vous voulez exporter.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ça marche hein, on est d&amp;rsquo;accord, mais c&amp;rsquo;est fastidieux à la longue.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Le souci est que lorsqu&amp;rsquo;un objet est composé d&amp;rsquo;autres objets, cela devient le berzingue pour sortir un truc propre. Vos classes commencent à devoir gérer des propriétés publiques que vous voulez, ou non, exporter, transformer, et ça commence à vous énerver. Et je vous comprends, moi ça m&amp;rsquo;énerve aussi.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Regardons ce que font les autres&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://www.sqlalchemy.org/&#34;&gt;SQLAlchemy&lt;/a&gt; propose une idée très plaisante pour sérialiser les données à mettre en base, c&amp;rsquo;est d&amp;rsquo;utiliser des variables statiques. Par exemple dans la classe &amp;ldquo;User&amp;rdquo; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;class User(Model):
    name = StringValue(32)
    lastname = StringValue(32)
    # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est pas idiot car cela rend identifiables les propriétés avec des types qu&amp;rsquo;on connait bien. Il suffit de vérifier si les champs sont des dérivés de la classe &amp;ldquo;Column&amp;rdquo; (avec la fonction &lt;code&gt;isinstance(obj, type)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Cette méthode est super intéressante mais elle a des désagréments pour notre cas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la classe a un objet statique instancié, ça a un côut en mémoire (pas grand chose mais il est là)&lt;/li&gt;
&lt;li&gt;pour bien être certain que la propriété de l&amp;rsquo;objet est un champs à traiter, il faut absolument avoir un type qui hérite d&amp;rsquo;une classe identifiable comme telle, par exemple &amp;ldquo;Field&amp;rdquo; ou &amp;ldquo;Column&amp;rdquo; - au final on va quand même le faire, mais pour l&amp;rsquo;heure imaginons que nous voulions nous en passer&lt;/li&gt;
&lt;li&gt;et donc, il faut penser à tout&amp;hellip; tous les types !&lt;/li&gt;
&lt;li&gt;et c&amp;rsquo;est pas ce qu&amp;rsquo;on veut, on veut sérialiser en JSON des trucs simples à taper bon sang. Des chaines de caractères, des nombres, et potentiellement des objet &amp;ldquo;sérialisables&amp;rdquo; de temps en temps&lt;/li&gt;
&lt;li&gt;et quid des objets déjà tout prêts qu&amp;rsquo;on veut rendre sérialisables après coup ? Par exemple dans projet déjà bien conséquent&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Je ne suis pas en train de dire que SQLAlchemy fait mal les choses, non non. Je dis juste que dans des cas &amp;ldquo;simples&amp;rdquo;, et dans un cas &amp;ldquo;custom&amp;rdquo;, on a pas forcément l&amp;rsquo;envie de se prendre la tête définir des types spécifiques. Là pour la sérialisation JSON, c&amp;rsquo;est bien trop compliqué !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;C&amp;rsquo;est quoi la solution pour un cas plus simple ?&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;les-annotations-&#34;&gt;Les annotations !&lt;/h1&gt;
&lt;p&gt;Dans les faits vous avez peut-être déjà vu des annotations dans des déclarations de fonctions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def truc(vara: int, varb: str):
    pass
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans l&amp;rsquo;exemple du dessus, les annotations sont les &amp;ldquo;&lt;code&gt;: quelquechose&lt;/code&gt;&amp;rdquo;. &lt;strong&gt;Contrairement à ce que beaucoup pense, ce n&amp;rsquo;est pas du typage&lt;/strong&gt;, car Python se fout complètement de l&amp;rsquo;annotation. Par contre les IDE, ils s&amp;rsquo;en servent pas mal pour vous proposer l&amp;rsquo;autocompletion adéquate. Ça sert aussi à ne pas se tromper dans les arguments, et de ne pas avoir à les nommer pour faire comprendre quel type est attendu. Mais fonctionnellement, c&amp;rsquo;est pas utile à Python.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Une annotation, c&amp;rsquo;est un genre d&amp;rsquo;attribut mais il ne fait rien&amp;hellip; mais alors rien ! &lt;strong&gt;À part &amp;ldquo;taguer&amp;rdquo; des propriétés&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Par contre, chose que beaucoup en savent pas (moi je sais, nanaère), c&amp;rsquo;est que les annotations peuvent être retrouvées au sein du code. C&amp;rsquo;est donc super intéressant car vous pouvez jouer avec pour vérifier vous même les types. Et vous savez quoi ? Ça marche aussi avec des attributs de classe !&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée c&amp;rsquo;est que l&amp;rsquo;annotation c&amp;rsquo;est un genre de &amp;ldquo;tag&amp;rdquo;, qui ne peut contenir qu&amp;rsquo;une chose: un type (ou un tuple de types). Et vraiment rien d&amp;rsquo;autre&amp;hellip; Il sert de &amp;ldquo;description&amp;rdquo; d&amp;rsquo;un attribut. Il n&amp;rsquo;assigne pas de valeur.&lt;/p&gt;
&lt;p&gt;Vous sentez venir le coup ?&lt;/p&gt;
&lt;p&gt;Une annotation, comme quand on le fait pour les arguments d&amp;rsquo;une fonction, ça s&amp;rsquo;écrit &amp;ldquo;&lt;code&gt;nom: type&lt;/code&gt;&amp;rdquo;. &lt;strong&gt;Attention, pas de signe &amp;ldquo;égal&amp;rdquo;, c&amp;rsquo;est bien &amp;ldquo;deux points&amp;rdquo;&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;class User(JSONizer):
    firstname: str
    lastname: str
    email: str
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Sérieusement, vous trouvez pas ça joli ? C&amp;rsquo;est net, c&amp;rsquo;est efficace, c&amp;rsquo;est carrément compréhensible.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Mon objet hérite de JSONizer, une classe que je vais développer. Cette classe va pouvoir traiter les annotations et faire ce que je veux: Nous aurons accès à &lt;code&gt;self.__annotations__&lt;/code&gt; qui est un dictionnaire (&lt;code&gt;dict&lt;/code&gt;). On peut donc le parcourir, avoir la valeur et les clefs - ça va pas être bien compliqué.&lt;/p&gt;
&lt;p&gt;Ici je vais m&amp;rsquo;en servir pour:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vérifier que ce que m&amp;rsquo;envoi l&amp;rsquo;utilisateur dans la construction de l&amp;rsquo;objet existe dans les annotations (par exemple hein, on est pas obligé)&lt;/li&gt;
&lt;li&gt;vérifier que le type déclaré correspond au type de la valeur que je reçois en constriction (encore une fois, pas obligé, c&amp;rsquo;est pour vous montrer)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est un exemple hein, on peut s&amp;rsquo;en servir pour autre chose&amp;hellip; vous verrez après&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Pour faire ceci, ma classe mère &lt;code&gt;JSONizer&lt;/code&gt; contient:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;un constructeur, pas obligatoire mais intéressant, il va enregistrer les propriétés envoyées en argument (mais on pourrait très bien, en plus, utiliser &lt;code&gt;__setattribute__&lt;/code&gt; , en complément ou pas)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;une méthode &lt;code&gt;save()&lt;/code&gt; qui me retourne le JSON (mais elle pourrait très bien enregistrer en base de données),&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;et une méthode &lt;code&gt;todict()&lt;/code&gt; qui va me permettre de faire un peu d&amp;rsquo;introspection et sortir un dictionnaire ne contenant que les attributs qui correspondent à une annotation. Bien entendu ça pourrait très bien être fait dans la méthode &lt;code&gt;save()&lt;/code&gt; mais j&amp;rsquo;aime bien factoriser les trucs.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voilà le code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;class JSONizer:
    def __init__(self, **kwargs):
        # kwargs =&amp;gt; on prend toutes les valeurs à la bourrin
        # évidemment vous pourrez améliorer la classe hein
        # Dans votre cas, ce sera peut-être pas utile

        # Pour chaque argument nommé reçu, on va faire quelques
        # tests et les injecter dans la classe en tant que **proriété**
        for name, val in kwargs.items():

            # Par exemple l&#39;argument doit être dans les annotations
            # On peut s&#39;en passer, là c&#39;est pour vous
            # montrer comment on peut &amp;quot;forcer la déclaration&amp;quot;, si pas
            # d&#39;annotation =&amp;gt; je refuse l&#39;attribut
            if name not in self.__annotations__:
                raise Exception(f&amp;quot;Undeclared attribute {name}&amp;quot;)

            # et/ou  on vérifie que le type est bon
            if isinstance(val, self.__annotations__[name]):
                wanted = self.__annotations__[name]
                raise Exception(f&amp;quot;Bad type for {name}, want {wanted}&amp;quot;)

            # tout est bon, on assigne l&#39;attribut à l&#39;objet
            setattr(self, name, val)

    def todict(self):
        &amp;quot;&amp;quot;&amp;quot; Transforme en dictionnaire &amp;quot;&amp;quot;&amp;quot;

        # C&#39;est là que c&#39;est intéressant, je peux par exemple ne prendre
        # dans mon dictionnaire, que les valeurs qui ont une annotations
        data = {k: getattr(self, k) for k in self.__annotations__}
        for k, v in data.items():
            # et si l&#39;attribut a &amp;quot;todict&amp;quot;, on l&#39;appelle
            if hasattr(v, &amp;quot;todict&amp;quot;):
                data[k] = v.todict()

        return data

    def save(self):
        &amp;quot;&amp;quot;&amp;quot; là c&#39;est un exemple hein &amp;quot;&amp;quot;&amp;quot;
        return json.dumps(self.todict(), indent=4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez tenter de faire ceci:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;u = User(name=&amp;quot;Sarah&amp;quot;, lastname=&amp;quot;Connor&amp;quot;, email=&amp;quot;terminator@skynet&amp;quot;)

# et là ça passe
print(u.save())

# affiche:
# {
#     &amp;quot;name&amp;quot;: &amp;quot;Sarah&amp;quot;,
#     &amp;quot;lastname&amp;quot;: &amp;quot;Connor&amp;quot;,
#     &amp;quot;email&amp;quot;: &amp;quot;terminator@skynet&amp;quot;
#
# }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant faisons notre classe &amp;ldquo;Project&amp;rdquo; qui contient un &amp;ldquo;User&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;class Project(JSONizer)
    name: str
    owner: User # là
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comme l&amp;rsquo;attribut &amp;ldquo;owner&amp;rdquo; est de type &amp;ldquo;User&amp;rdquo; et que &amp;ldquo;User&amp;rdquo; hérite de &amp;ldquo;JSONizer&amp;rdquo;, on trouvera la méthode &amp;ldquo;&lt;code&gt;todict()&lt;/code&gt;&amp;rdquo; . Et comme la fonction de cet objet enfant va aussi vérifier ses propres enfants, tous les objets vont être transformés.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Parfois le développement parait magique&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et par conséquent:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;u = User(name=&amp;quot;Patrice&amp;quot;, lastname=&amp;quot;Ferlet&amp;quot;, email=&amp;quot;me@foo&amp;quot;)
print(&amp;quot;L&#39;utilisateur:&amp;quot;, u.save())

p = Project(name=&amp;quot;Website&amp;quot;, owner=u)
print(&amp;quot;Le projet:&amp;quot;, p.save())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui nous produit:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;L&#39;utilisateur: {
    &amp;quot;name&amp;quot;: &amp;quot;Patrice&amp;quot;,
    &amp;quot;lastname&amp;quot;: &amp;quot;Ferlet&amp;quot;,
    &amp;quot;email&amp;quot;: &amp;quot;me@foo&amp;quot;
}
Le projet: {
    &amp;quot;name&amp;quot;: &amp;quot;Website&amp;quot;,
    &amp;quot;owner&amp;quot;: {
        &amp;quot;name&amp;quot;: &amp;quot;Patrice&amp;quot;,
        &amp;quot;lastname&amp;quot;: &amp;quot;Ferlet&amp;quot;,
        &amp;quot;email&amp;quot;: &amp;quot;me@foo&amp;quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On voit bien que l&amp;rsquo;objet &amp;ldquo;User&amp;rdquo; est dans l&amp;rsquo;objet &amp;ldquo;Project&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Bien entendu vous faites comme vous le voulez, par exemple au lieu de coller l&amp;rsquo;objet vous pourriez prendre l&amp;rsquo;ID qui vient de la base de donnée de cet objet, ou faire une transformation qui vous amuse. Rien n&amp;rsquo;est interdit (si, mais bon vous me comprenez).&lt;/p&gt;
&lt;p&gt;Changeons donc la méthode &amp;ldquo;&lt;code&gt;todict()&lt;/code&gt;&amp;rdquo; pour illustrer mon propos, par exemple je ne vais garder que le champs &amp;ldquo;name&amp;quot;de l&amp;rsquo;utilisateur dans le Projet :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;    def todict(self):
        &amp;quot;&amp;quot;&amp;quot; Transforme en dictionnaire &amp;quot;&amp;quot;&amp;quot;
        data = {k: getattr(self, k) for k in self.__annotations__}
        for k, v in data.items():
            if hasattr(v, &amp;quot;name&amp;quot;):
                data[k] = v.name

        return data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et on aura donc en sortie:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;L&#39;utilisateur: {
    &amp;quot;name&amp;quot;: &amp;quot;John&amp;quot;,
    &amp;quot;lastname&amp;quot;: &amp;quot;Ferlet&amp;quot;,
    &amp;quot;email&amp;quot;: &amp;quot;me@foo&amp;quot;
}
Le projet: {
    &amp;quot;name&amp;quot;: &amp;quot;Website&amp;quot;,
    &amp;quot;owner&amp;quot;: &amp;quot;John&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;On a toujours le risque d&amp;rsquo;écraser une propriété parente, je ne le nie pas. Mais&amp;hellip;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ma classe &amp;ldquo;JSONizer&amp;rdquo; ne va pas garder beaucoup de propriétés, elle est faite pour &amp;ldquo;transporter&amp;rdquo; des données, le risque est minime en soit&lt;/li&gt;
&lt;li&gt;je vais de toutes manière préfixer mes propriétés par un double underscore pour les rendre privées&lt;/li&gt;
&lt;/ul&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;on-va-aller-un-peu-plus-loin&#34;&gt;On va aller un peu plus loin&lt;/h1&gt;
&lt;p&gt;Bon maintenant vous vous dites que c&amp;rsquo;est cool, mais vous aimeriez mettre d&amp;rsquo;autres annotations. Mieux encore, vous aimeriez décider quelle propriété mettre dans l&amp;rsquo;export, et ignorer les autres.&lt;/p&gt;
&lt;p&gt;Et bien c&amp;rsquo;est pas compliqué, on va commencer par fair un type (une classe) qui va nous permettre d&amp;rsquo;identifier que l&amp;rsquo;attribut annoté est une propriété à exporter en JSON.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;class JSONProp:
    pass
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reste à changer l&amp;rsquo;implémentation de la classe &lt;code&gt;JSONizer&lt;/code&gt;, on va maintenant accepter toutes les annotations de tous les types, on ne contrôle pas le contenu. Par contre, &lt;strong&gt;la méthode &lt;code&gt;todict()&lt;/code&gt;  va filtrer les propriétés annotées en tant &amp;ldquo;JSONProp&amp;rdquo;&lt;/strong&gt; - vous allez voir la souplesse du bousin.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dans vos développements, bien entendu, le constructeur qui prend en paramètre les valeurs à assigner peut être supprimé. Je l&amp;rsquo;ai mis ici pour réduire la taille des exemples et ne pas assigner les valeurs ligne par ligne.&lt;/p&gt;&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;class JSONizer:
    def __init__(self, **kwargs):
        # cette fois j&#39;accepte tout
        for name, val in kwargs.items():
            # on accepte tout, pas de contrôle de type
            setattr(self, name, val)

    def todict(self):
        annotations = self.__annotations__

        # et là je filtre
        data = {
            key: getattr(self, key)
            for key, kind in annotations.items()
            if kind is JSONProp
        }
        for k, v in data.items():
            if hasattr(v, &amp;quot;todict&amp;quot;):
                data[k] = v.todict()

        return data

    def save(self):
        return json.dumps(self.todict(), indent=4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et je vais maintenant faire en sorte que seules les propriétés &amp;ldquo;name&amp;rdquo; et &amp;ldquo;lastname&amp;rdquo; de la classe &amp;ldquo;User&amp;rdquo; soient dans mon export JSON :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;class User(JSONizer):
    name: JSONProp
    lastname: JSONProp
    email: str # Cette propriétés sera ignorée
               # On pourrait d&#39;ailleur la virer tiens...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, &lt;code&gt;u.save()&lt;/code&gt; ne me retournera pas l&amp;rsquo;email. C&amp;rsquo;est une annotation &amp;ldquo;autre&amp;rdquo; dont je me fous royalement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Heu OK, mais pour avoir plusieurs types dans une annotation ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et ouais, ce qui serait cool maintenant, c&amp;rsquo;est que l&amp;rsquo;annotation puisse contenir une liste de types, comme ça je peux dire que le champs &amp;ldquo;name&amp;rdquo; est une chaîne et en plus que c&amp;rsquo;est une propriété à exporter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;class User(JSONizer):
    name: (str, JSONProp)
    lastname: JSONProp # là on se fout du type
    email: str # et ça c&#39;est pas exporté
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et bien il suffit de changer la méthode &lt;code&gt;todict&lt;/code&gt; et remplacer le filtrage par :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;data = {
    key: getattr(self, key) # on prend l&#39;attribut
    for key, kind in annotations.items() # dans les annotations
    if kind is JSONProp or (isinstance(kind, tuple) and JSONProp in kind)
    # ↑ si le type est un JSONProp, ou que c&#39;est un tuple et que JSONProp s&#39;y trouve
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, pas plus compliqué&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et là, un truc intéressant, c&amp;rsquo;est que vous pouvez faire du type checking. Vous pouvez donc faire hériter vos classes de JSONizer qui gère les JSONProp + hériter d&amp;rsquo;une autre classe qui va vérifier les types que vous injectez. Ça peut aller très très loin !&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;ça-tas-vraiment-servi-&#34;&gt;Ça t&amp;rsquo;as vraiment servi ?&lt;/h1&gt;
&lt;p&gt;Oui. J&amp;rsquo;ai un projet qui utilise une base de données que j&amp;rsquo;adore vraiment, &lt;a href=&#34;https://rethinkdb.com/&#34;&gt;RethinkDB&lt;/a&gt;. Cette base orientée document ne prend en charge que des types &amp;ldquo;natifs&amp;rdquo; aux langages que vous utilisez (chaines, entiers, flottants, dates&amp;hellip;) - cela réduit fortement les contrôles à effectuer. Du coup j&amp;rsquo;ai créé mon propre système de modèle.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;avais commencé à utilisé la méthode de SQLAlchemy, mais quand j&amp;rsquo;ai repensé mon implémentation, et que j&amp;rsquo;ai utilisé les annotations, tout est devenu plus élégant. Dans mon cas en tout cas.&lt;/p&gt;
&lt;p&gt;Un objet injectable en base (donc qui hérite de &amp;ldquo;&lt;code&gt;Model&lt;/code&gt;&amp;rdquo;) ressemble à ça:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;class User(Model):
    username: (str, Unique, NonNull)
    email: (str, Unique, NonNull)
    password: (str, NonNull)
    verified: bool

    def login(self, username, password):
        &amp;quot;&amp;quot;&amp;quot; Needed method to log in user &amp;quot;&amp;quot;&amp;quot;
        found = self.filter({
            &amp;quot;username&amp;quot;: username,
            &amp;quot;password&amp;quot;: SHA(password),
        })
        if len(found) &amp;gt; 0:
            # return the found user
            return User(**found)

        # not found user
        return False
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;J&amp;rsquo;ai créé quelques classes dérivées du genre &amp;ldquo;Unique&amp;rdquo; qui demande un contrôle de l&amp;rsquo;existence en base de tel ou tel attribut d&amp;rsquo;un objet, et la classe de base  ajoute des champs qui me plaisent comme &amp;ldquo;createdAt, updatedAt, deletedAt&amp;rdquo; dans sa méthode &amp;ldquo;save()&amp;rdquo;. Méthode qui en plus sait faire un insert ou update en fonction de la valeur de l&amp;rsquo;ID de l&amp;rsquo;objet&amp;hellip; Bref un truc fun et &lt;strong&gt;très facile à maintenir&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Il aurait été possible, bien entendu, d&amp;rsquo;utiliser des propriétés statiques comme je vous le dis plus haut. Mais les annotations me permettent de réduire le code de contrôle des champs et de ne pas avoir à tout typer. Bref c&amp;rsquo;est très adapté à &lt;strong&gt;ma&lt;/strong&gt; façon de coder. Je préfère taper &lt;code&gt;(str, NonNull, Unique)&lt;/code&gt; que &lt;code&gt;StringValue(null=False, unique=True)&lt;/code&gt; par exemple, je trouve les annotations plus &amp;ldquo;jolies&amp;rdquo;.&lt;/p&gt;
&lt;h1 id=&#34;ce-qui-est-cool&#34;&gt;Ce qui est cool&lt;/h1&gt;
&lt;p&gt;Ce qui est cool, c&amp;rsquo;est que la classe &amp;ldquo;JSONizer&amp;rdquo; et la classe &amp;ldquo;JSONProp&amp;rdquo; sont juste deux types à utiliser sans se prendre la tête. Tous vos objets déjà existants vont pouvoir utiliser les annotations pour définir ce que vous voulez sortir en JSON, sans ajouter de méthode. Vous avez juste à annoter vos objets pour définir ce qui doit être exporté, et comment. Je vous rappelle que la méthode &amp;ldquo;&lt;code&gt;save()&lt;/code&gt;&amp;rdquo; peut être nommée comme vous le souhaitez, et que rien ne vous interdit de transformer les valeurs de l&amp;rsquo;objet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors attention, ce que je vous mets dans l&amp;rsquo;article c&amp;rsquo;est un exemple, à vous de faire un truc plus propre, plus adapté, avec les contrôles qui vous chantent.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;tes-sûr-de-toi-&#34;&gt;T&amp;rsquo;es sûr de toi ?&lt;/h1&gt;
&lt;p&gt;Les annotations servent déjà au &amp;ldquo;type checking&amp;rdquo; dans certains IDE (y compris vim quand on sait faire &lt;span class=&#34;emoji&#34;&gt;😜&lt;/span&gt;), il est donc assez naturel de les utiliser pour faire tu type checking par introspection sur les annotations.&lt;/p&gt;
&lt;p&gt;Évidemment, la méthode qu&amp;rsquo;utilise SQLAlchemy est très puissante. Les propriétés statiques permettent de ne pas avoir un type mais un objet qui contient de la configuration de champs (par exemple, donner une longueur maximale de chaîne, dire si un champs est unique, etc&amp;hellip;) - En soit, les annotations sont surtout un outils pratique, peu coûteux en mémoire et facile à appréhender.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;aime beaucoup le fait de pouvoir, par exemple, générer du JSON en définissant quelles propriétés sortir, sans avoir l&amp;rsquo;erreur &lt;code&gt;XXX is not JSON Serializable&lt;/code&gt;. Et comme je vous le disais, c&amp;rsquo;est aussi intéressant pour faire un système custom de modèle de base de données.&lt;/p&gt;
&lt;p&gt;Par contre, je ne dis pas qu&amp;rsquo;il faille les utiliser pour tout et n&amp;rsquo;importe quoi. Les annotations &lt;strong&gt;sont utiles pour du type checking et, à la limite, du filtrage&lt;/strong&gt;, elle ne peuvent contenir que des &amp;ldquo;types&amp;rdquo; ou &amp;ldquo;tuple de types&amp;rdquo;, pas des instances. Sinon, passez par des propriétés statiques !&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PS: quand tout sera bien testé, je pense vous proposer en open source mon package &amp;ldquo;RDBModel&amp;rdquo; pour Python &lt;span class=&#34;emoji&#34;&gt;😇&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Voilà !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>De Flask À Quart - pourquoi pas, surtout pour les Websockets</title>
      <link>https://www.metal3d.org/blog/2020/de-flask-%C3%A0-quart/</link>
      <pubDate>Mon, 21 Dec 2020 11:12:01 +0100</pubDate>
      
      <guid>https://www.metal3d.org/blog/2020/de-flask-%C3%A0-quart/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2020/de-flask-%C3%A0-quart/flask-quart-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Flask est certainement l&amp;rsquo;un des frameworks les plus connus dans le monde de Python, mais connaissez-vous Quart, compatible Flask, qui gère l&amp;rsquo;asychronisme ? Vous allez voir à quel point c&amp;rsquo;est bénéfique, notamment pour la gestion des WebSockets.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://flask.palletsprojects.com/en/1.1.x/&#34;&gt;Flask&lt;/a&gt; a été pour moi l&amp;rsquo;une des plus grosses révélations quand je suis passé de PHP à Python, il y a déjà fort longtemps. C&amp;rsquo;est un framework facile à apprendre, à comprendre, léger, souple, qui permet d&amp;rsquo;écrire des API REST ou un site avec un rendu &amp;ldquo;server side&amp;rdquo;. Il intègre &lt;a href=&#34;https://jinja.palletsprojects.com/en/2.11.x/&#34;&gt;Jinja2&lt;/a&gt; et vous avez un paquet de &amp;ldquo;plugins&amp;rdquo; pour gérer l&amp;rsquo;authentification, les CORS, et j&amp;rsquo;en passe. Bref, Flask, c&amp;rsquo;est le pied total. Jusqu&amp;rsquo;au moment où vous avez envie de faire des trucs &amp;ldquo;en parallèle&amp;rdquo;, par exemple gérer des ServerEvents ou des Websockets.&lt;/p&gt;
&lt;p&gt;Oui, on peut le faire avec Flask, je ne dis pas le contraire. Mais Python propose depuis quelque temps une écriture native pour faire de l&amp;rsquo;asynchrone (des coroutines), &lt;a href=&#34;https://www.metal3d.org/blog/2020/d%C3%A9mystifier-python-async/&#34;&gt;je vous en ai parlé&lt;/a&gt; et je vous invite à vraiment vous pencher sur la question.&lt;/p&gt;
&lt;p&gt;Et c&amp;rsquo;est là que je tombe sur &lt;a href=&#34;https://pgjones.gitlab.io/quart/&#34;&gt;Quart&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;oh-non-encore-un-framework-&#34;&gt;Oh non, encore un framework !&lt;/h1&gt;
&lt;p&gt;Ouais mais non. Je suis, moi aussi, peu enthousiaste à devoir passer d&amp;rsquo;un framework à l&amp;rsquo;autre. D&amp;rsquo;autant que la syntaxe de Flask est idéale pour moi. Mais justement, Quart utilise quasiment la même syntaxe. Je dis &amp;ldquo;quasiment&amp;rdquo; parce que finalement, la seule chose qui change c&amp;rsquo;est le fait de déclarer nos fonctions (nos routes) en coroutine.&lt;/p&gt;
&lt;p&gt;Je vous fais un résumé simple.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# Avec Flask
from flask import Flask

app = Flask(__name__)

@app.route(&amp;quot;/&amp;quot;)
def index():
    return &amp;quot;Hello&amp;quot;

# Avec Quart
from quart import Quart

app = Quart(__name__)

@app.route(&amp;quot;/&amp;quot;)
async def index():
    return &amp;quot;Hello&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, à part le nom du framework et un &amp;ldquo;&lt;code&gt;async&lt;/code&gt;&amp;rdquo; qui traine, rien de bien nouveau.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors, me direz-vous, ça change quoi ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et bien, de part sa nature asynchrone, Quart va vous soulager grandement pour gérer des tâches qui peuvent tourner en tâche de fond, comme pour les ServerSide Events, ou les websockets.&lt;/p&gt;
&lt;h1 id=&#34;les-websockets&#34;&gt;Les websockets&lt;/h1&gt;
&lt;p&gt;Flask, de base, nativement, ne peut pas gérer des websockets. Il vous faut un package supplémentaire (e.g. SocketIO).&lt;/p&gt;
&lt;p&gt;Quart inclue un module de websocket, et il est&amp;hellip; Comment dire&amp;hellip; Très naturel à utiliser.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors, soyons précis, je le trouve idéal car il me permet de gérer la logique moi-même, chose que beaucoup n&amp;rsquo;aime pas. Je le comprends. J&amp;rsquo;ai un plaisirs fou à coder les logiques de base, alors que d&amp;rsquo;autres aiment utiliser les implémentations clef-en-main. C&amp;rsquo;est une question de goût et d&amp;rsquo;habitude.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Donc, à mon sens, la logique de Quart est plus adaptée à ma manière de travailler. Bref, comment ça marche ?&lt;/p&gt;
&lt;p&gt;De la même manière que l&amp;rsquo;objet &amp;ldquo;request&amp;rdquo; dans Flask et Quart, il existe un objet &lt;code&gt;websocket&lt;/code&gt; qui utilise le contexte. De ce fait, &lt;code&gt;quart.websocket&lt;/code&gt; sera utilisé au sein d&amp;rsquo;une route pour:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reçevoir des messages avec &lt;code&gt;websocket.receive()&lt;/code&gt; (asynchrone)&lt;/li&gt;
&lt;li&gt;envoyer des messages avec &lt;code&gt;websocket.send(data)&lt;/code&gt; (asynchrone)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Comme je vous le disais, il faut gérer la logique de base. En général, vous voulez envoyer des données à tout moment à vos clients connectés.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;exemple donné dans &lt;a href=&#34;https://pgjones.gitlab.io/quart/tutorials/websocket_tutorial.html#websocket-tutorial&#34;&gt;la page de documentation dédiée au sujet&lt;/a&gt; est assez basique, et ne reflète pas forcément le besoin que vous aurez. Pour ma part, l&amp;rsquo;envoi et la réception d&amp;rsquo;info sont décorrélés. Donc, je fais comme ça :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je crée une route de connexion, par exemple sur &amp;ldquo;&lt;code&gt;/ws&lt;/code&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;je crée une &lt;code&gt;asyncio.Queue&lt;/code&gt; qui servira au serveur à envoyer des données à la connexion&lt;/li&gt;
&lt;li&gt;je crée deux &lt;strong&gt;coroutines&lt;/strong&gt; qui vont gére la réception et l&amp;rsquo;envoi&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vous allez voir c&amp;rsquo;est pas si compliqué :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import asyncio
import json
from datetime import datetime

from quart import Quart, copy_current_websocket_context, websocket

app = Quart(__name__)


async def manage(data, queue):
    # Ici, je gère les message reçus depuis le client.
    # Si le client m&#39;envoie une clef &amp;quot;hello&amp;quot; je lui répond
    # par un &amp;quot;hello&amp;quot;
    # Pour envoyer au client, j&#39;écris simplement dans une Queue.
    if data.get(&#39;hello&#39;):
        await queue.put({&amp;quot;response&amp;quot;: &amp;quot;Hello %s!&amp;quot; % data[&#39;hello&#39;]})
    else:
        await queue.put({&amp;quot;response&amp;quot;: &amp;quot;What ?&amp;quot;})


async def ping(queue):
    # Là c&#39;est un exemple de fonction qui peut tourner en tâche de fond
    # pour envoyer des données, de manière asynchrone, au client.
    while True:
        await asyncio.sleep(5)
        await queue.put({&amp;quot;response&amp;quot;: &amp;quot;ping %s&amp;quot; % datetime.now()})


@app.websocket(&#39;/ws&#39;)
async def ws():

    # Queue qui permet aux coroutines d&#39;envoyer des data
    # au client.
    queue = asyncio.Queue()

    @copy_current_websocket_context
    async def recv():
        # Ici, on va attendre indéfiniment que le client
        # envoie des données. Quand on en a, on la traite
        # avec la fonction &amp;quot;manage&amp;quot;.
        while True:
            message = await websocket.receive()
            await manage(json.loads(message), queue)

    @copy_current_websocket_context
    async def send():
        # Ici, on attend que des données entrent dans la queue,
        # quand on nous envoie des infos, on les envoie au client.
        while True:
            data = await queue.get()
            await websocket.send(json.dumps(data))

    # ici, je crée mes tâches concurrentes
    producer = asyncio.ensure_future(send())
    consumer = asyncio.ensure_future(recv())
    pinger = asyncio.ensure_future(ping(queue))

    # là, je vais attendre qu&#39;un coroutine se viande, si c&#39;est le cas
    # c&#39;est que le client a coupé la connexion (ou l&#39;a perdu)
    try:
        await asyncio.gather(producer, consumer, pinger)
    finally:
        producer.cancel()
        consumer.cancel()
        pinger.cancel()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour les deux coroutines qui utilisent le websocket, je dois utiliser &lt;code&gt;copy_current_websocket_context&lt;/code&gt; pour ne pas perdre le contexte, c&amp;rsquo;est un &amp;ldquo;souci&amp;rdquo; commun dans ce genre d&amp;rsquo;application, mais ce décorateur fait ce qu&amp;rsquo;il faut pour ne pas avoir à trop se prendre la tête.&lt;/p&gt;
&lt;p&gt;Si jamais vous voulez sortir les fonctions de la fonction mère, vous pouvez, mais dans la création de tâche, vous devrez utiliser le décorateur en tant que fonction, par exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;producer = asyncio.ensure_future(
    copy_current_websocket_context(producer)()
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et si vous avez un argument à passer, genre la queue, faut passer par &lt;code&gt;functools.partial&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;send_func = functools.partial(send, queue)
producer = asyncio.ensure_future(
    copy_current_websocket_context(send_func)()
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais en règle générale, je préfère créer une toute petite fonction de dispatch, comme dans l&amp;rsquo;exemple que je vous ai donné.&lt;/p&gt;
&lt;p&gt;Revenons au code de l&amp;rsquo;exemple ci-dessus, et voyez la fonction &amp;ldquo;ping&amp;rdquo; qui peut tourner en arrière-plann et qui envoie un message toutes les 5 secondes.&lt;/p&gt;
&lt;p&gt;Le plus important reste les deux coroutines &lt;code&gt;recv&lt;/code&gt; et &lt;code&gt;send&lt;/code&gt; qui gèrent, en concurrence, l&amp;rsquo;envoi et la réception de données. La &lt;code&gt;asyncio.Queue&lt;/code&gt; est très importante, mais aussi très pratique. Inutile de balader un objet de connexion de websocket, on va simplement stocker les messages dans une file d&amp;rsquo;attente qui part vers le client.&lt;/p&gt;
&lt;p&gt;Coté JS, c&amp;rsquo;est pas compliqué :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;let w = new WebSocket(&#39;ws://127.0.0.1:5000/ws&#39;);
w.onmessage = data =&amp;gt; {
    console.log(data.data)
}
w.send(JSON.stringify({
    &amp;quot;hello&amp;quot;: &amp;quot;John&amp;quot;
}))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si vous avez besoin d&amp;rsquo;un &amp;ldquo;broadcast&amp;rdquo;, c&amp;rsquo;est-à-diree une fonction qui envoie à tous les clients, l&amp;rsquo;idée est de faire ceci :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;
# en haut, en globale
QUEUES = set()


# dans la fonction &amp;quot;ws()&amp;quot; qui gère la websocket:
queue = asyncio.Queue()
QUEUES.add(queue)

# et dans le finally en fin de fonciton, quand la connexion est perdue,
# on vire la queue de la liste:
    try:
        await asyncio.gather(producer, consumer, pinger)
    finally:
        producer.cancel()
        consumer.cancel()
        pinger.cancel()
        QUEUES.remove(queue)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et donc, imaginons une fonction de broadcast :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;async def broadcast(message):
    for qeue in QUEUES:
        try:
            await queue.put(json.dumps(message))
        except:
            pass
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, tous les clients vont avoir le message.&lt;/p&gt;
&lt;h1 id=&#34;je-passe-à-quart-ou-je-reste-sur-flask-&#34;&gt;Je passe à Quart ou je reste sur Flask ?&lt;/h1&gt;
&lt;p&gt;Ça dépend. Flask est éprouvé, bénéficie d&amp;rsquo;une large communauté, et il a des plugins d&amp;rsquo;une puissance extrême. C&amp;rsquo;est facile à gérer, surtout quand on pas l&amp;rsquo;habitude (ou l&amp;rsquo;envie) de bosser avec des tâches asynchrones.&lt;/p&gt;
&lt;p&gt;Cela-dit, Quart est très proche de Flask (c&amp;rsquo;est un fork hein), et les développeurs n&amp;rsquo;entrent pas en conflit avec ceux de Flask, au contraire ils collaborent étroitement. Quart commence à se faire entendre, et il vous permet &lt;strong&gt;d&amp;rsquo;exploiter la puissance des coroutines Python&lt;/strong&gt;. Côté Websocket et ServerSide events, c&amp;rsquo;est à mon avis la meilleure approche à avoir. Pas de thread à gérer, et vous avez la maitrise de la logique métier.&lt;/p&gt;
&lt;p&gt;Je retrouve avec Quart ce que j&amp;rsquo;adore avec Go et les websocket en coroutines. C&amp;rsquo;est donc clairement un avis personnel lié à mes goûts.&lt;/p&gt;
&lt;p&gt;Alors, à mon avis, la meilleure chose à faire de votre côté est de &lt;strong&gt;tester&lt;/strong&gt;. Si vous n&amp;rsquo;aimez pas, restez sur Flask et les packages adéquats. Si vous aimez les coroutines et le fait de ne pas avoir à gérer des packages externes, peut-être allez-vous aimer Quart &lt;span class=&#34;emoji&#34;&gt;🤓&lt;/span&gt;.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Je Pars De Twitter, Et Je Rejoins Mastodon</title>
      <link>https://www.metal3d.org/blog/2020/je-pars-de-twitter-et-je-rejoins-mastodon/</link>
      <pubDate>Wed, 04 Nov 2020 13:34:24 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2020/je-pars-de-twitter-et-je-rejoins-mastodon/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2020/je-pars-de-twitter-et-je-rejoins-mastodon/twitter-out-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;C&amp;rsquo;est décidé, je quitte Twitter et je rejoins Mastodon. Je vous explique&amp;hellip;&lt;/p&gt;
&lt;p&gt;Trop long de tout lire ? Je suis désormais ici, &lt;a href=&#34;https://techlover.eu/@metal3d&#34;&gt;https://techlover.eu/@metal3d&lt;/a&gt;, et mes messages partent sur Twitter, &lt;strong&gt;mais je ne répondrai plus sur Twitter&lt;/strong&gt;. Si vous voulez créer un compte Mastodon, &lt;a href=&#34;https://joinmastodon.org/communities&#34;&gt;cherchez une communauté qui vous intéresse&lt;/a&gt; (vous pourrez vous abonner à n&amp;rsquo;importe quel compte de n&amp;rsquo;importe quelle instance) ou alors demandez moi un code d&amp;rsquo;invitation sur mon instance.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si vous voulez comprendre ce qu&amp;rsquo;est Mastodon, lisez la suite.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;dabord-pourquoi-je-quitte-twitter-&#34;&gt;D&amp;rsquo;abord, pourquoi je quitte Twitter ?&lt;/h1&gt;
&lt;p&gt;Je me suis inscrit sur Twitter en 2009. J&amp;rsquo;aimais beaucoup l&amp;rsquo;accès à des &amp;ldquo;infos rapides&amp;rdquo;, l&amp;rsquo;humour de certains messages, le fait de trouver des nouveautés, l&amp;rsquo;échange d&amp;rsquo;idées.&lt;/p&gt;
&lt;p&gt;Mais, un peu comme tout, Twitter avait déjà quelques travers.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord une limite dans le nombre de caractères pour un message qui était un poil ridicule (140 caractères au début, puis 250 aujourd&amp;rsquo;hui, et ça reste trop léger pour moi) qui était intimement liée au nombre de caractères limite des&amp;hellip; SMS&amp;hellip; Aussi, une modération quasi inexistante. Et petit à petit, l&amp;rsquo;oiseau a fait son nid (poudoum pchit), et des gens de tous bords sont arrivés pour y déverser des idées sombres, des informations tronquées ou falsifiées, des théories du complot vaseuses, et l&amp;rsquo;échange est devenu bien plus aggresif.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai tenu bon, nettoyé ma timeline (le fil d&amp;rsquo;actualité si vous voulez), placé des filtres sur certains mots clef, et j&amp;rsquo;ai blacklisté à peu près 120 personnes en 11 ans.&lt;/p&gt;
&lt;p&gt;Et là, depuis quelques mois, c&amp;rsquo;est carrément une souffrance pour moi. Des proches ou des connaissances de longues dates, des gens qui parlaient avant tout des sujets que j&amp;rsquo;apprécie (tech, 3D, ML, mais aussi films, science&amp;hellip;) ont commencé à devenir &lt;strong&gt;très très très virulents&lt;/strong&gt; et &lt;strong&gt;militants&lt;/strong&gt;. Contre le gouvernement, contre la &lt;strong&gt;science&lt;/strong&gt;, contre les médecins, contre tout&amp;hellip; Et je passe outre la missandrie et la machisme (que je place dans le même panier).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Marre&amp;hellip; vraiment j&amp;rsquo;en ai marre. Marre de voir des ministres étrangers traiter un prédident de fils de chienne, marre qu&amp;rsquo;une discussion soit systématiquement marquée comme du mansplaining, marre des VeloTaffeurs dans le dénis, marre des chasseurs qui veulent passer pour des héros, marre&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;La modération de ces plateformes, trop larges, est une catastrophe. Que ce soit Youtube, Twitter, Facebook&amp;hellip; &lt;strong&gt;Une seule entité&lt;/strong&gt; avec des millions, voir milliards d&amp;rsquo;individus à modérer, c&amp;rsquo;est comme si vous n&amp;rsquo;aviez qu&amp;rsquo;une seule caméra de suveillance pour veiller sur l&amp;rsquo;entrée, les stocks, la porte de derrière, dans le brouillard et avec des coupures de courant.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;D&amp;rsquo;où le fait de tenter l&amp;rsquo;expérience d&amp;rsquo;un système plus adapté à l&amp;rsquo;internet actuel, la décentralisation qui permet d&amp;rsquo;avoir une modération efficace car la population est groupée par instance (je vais vous expliquer, c&amp;rsquo;est super simple)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Du coup, il me fallait trouver un autre réseau social, un endroit moins occupé, clair, avec un peu plus de caractères pour m&amp;rsquo;exprimer et répondre, mais surtout &lt;strong&gt;de la modération&lt;/strong&gt;. &lt;strong&gt;Je ne veux pas de censure&lt;/strong&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, mais je veux une plateforme où des gens réactifs peuvent clairement désaturer le service et diriger les sujets en fonction de mes choix. Et j&amp;rsquo;ai opté pour &lt;a href=&#34;https://joinmastodon.org/&#34;&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;donc-je-pars-sur-mastodon&#34;&gt;Donc, je pars sur &lt;a href=&#34;https://joinmastodon.org/&#34;&gt;Mastodon&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Et je rejoins 4 millions d&amp;rsquo;utilisateurs, c&amp;rsquo;est moins que les 330 millions de Twitter, certes, mais quantité vs. qualité, tout ça&amp;hellip;&lt;/p&gt;
&lt;p&gt;Le principe est quasi identique à Twitter dans la forme (affichage, utilisation, règlement), mais avec des points qui sont chers à mon coeur:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;opensource&lt;/strong&gt; (Licence libre - AGPL-3.0)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pas de publicité&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;modération selon l&amp;rsquo;instance (on va en reparler après)&lt;/li&gt;
&lt;li&gt;500 caractères (sérieux&amp;hellip; 250 caractères chez Twitter c&amp;rsquo;est infâme)&lt;/li&gt;
&lt;li&gt;des communautés définies&lt;/li&gt;
&lt;li&gt;le contenu flouté si &amp;ldquo;sensible&amp;rdquo;&lt;/li&gt;
&lt;li&gt;ne pas être enchaîné à une plateforme, à des admins incompétents, ou à une politique de service qui ne me correspond pas - si je veux changer de communauté, je peux partir et ne pas perdre mes abonnés.&lt;/li&gt;
&lt;li&gt;et par conséquent, ne pas être obligé de maintenir mon instance si j&amp;rsquo;en ai marre - si j&amp;rsquo;en ai marre, je démanage mon compte sur une autre instance sans perdre mes abonnements/abonnés&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mastodon répond à ça: c&amp;rsquo;est décentralisé (je vais vous expliquer), ce qui permet donc à tout le monde d&amp;rsquo;avoir son propre serveur ou de s&amp;rsquo;inscrire sur un serveur existant (perso, j&amp;rsquo;ai créé mon instance &lt;a href=&#34;https://techlover.eu&#34;&gt;https://techlover.eu&lt;/a&gt;) tout en ayant accès à ceux qui sont inscrits ailleurs. C&amp;rsquo;est tout simplement génial.&lt;/p&gt;
&lt;p&gt;Pour info, comme énormément d&amp;rsquo;outils libres et/ou opensource, c&amp;rsquo;est un européen qui l&amp;rsquo;a créé (un allemand) nommé Eugen Rochko. Entre Linux, Python, PHP, et j&amp;rsquo;en passe&amp;hellip; ça en fait des projets du vieux continent qui sont dans la place hein !&lt;/p&gt;
&lt;h1 id=&#34;alors-la-décentralisation-cékoidonc-&#34;&gt;Alors la décentralisation cékoidonc ?&lt;/h1&gt;
&lt;p&gt;Twitter, c&amp;rsquo;est une seule place, un seul endroit, pour s&amp;rsquo;incrire et échanger. Donc le réseau, et les comptes, &lt;strong&gt;appartiennent à une seule entité: Twitter&lt;/strong&gt; (qui exploite en plus vos profiles à des fins commerciales). Vous n&amp;rsquo;avez pas le choix: tout le monde parle de tout, c&amp;rsquo;est un bordel infâme, on vous colle des pubs, on vous insulte sans réaction de la part des modérateurs.&lt;/p&gt;
&lt;p&gt;Mastodon (mais d&amp;rsquo;autres services existent dans le même genre) ça change pas mal.&lt;/p&gt;
&lt;p&gt;Vous pouvez vous y inscrire depuis plusieurs endroits différents, par exemple sur &lt;a href=&#34;https://mamot.fr&#34;&gt;https://mamot.fr&lt;/a&gt; qui est l&amp;rsquo;instance de la quadrature du net, mais aussi &lt;a href=&#34;https://mastodon.top&#34;&gt;https://mastodon.top&lt;/a&gt; ou &lt;a href=&#34;https://mastodon.iriseden.eu/&#34;&gt;https://mastodon.iriseden.eu/&lt;/a&gt; qui est hébergé par OVH. &lt;strong&gt;Et quelque soit l&amp;rsquo;endroit où vous vous inscrivez vous aurez accès aux autres instances et aux comptes qui s&amp;rsquo;y trouvent&lt;/strong&gt;, donc vous pouvez vous abonner à un compte, le masquer, échanger avec tout le monde, etc&amp;hellip; Le principe n&amp;rsquo;est pas de vous isoler sur une instance, mais d&amp;rsquo;avoir des contenus proposés plus en adéquation avec votre envie, &lt;strong&gt;tout en ayant accès aux autres communautés&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si Alice est chez mamot.fr, et Bob sur techlover.eu, ils peuvent s&amp;rsquo;abonner entre eux, échanger, discuter&amp;hellip; c&amp;rsquo;est le principe de la décentralisation.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Sinon, faites comme moi (si vous êtes à l&amp;rsquo;aise avec la technologie), créez votre propre instance avec votre propre règlement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Oui, cela veut dire que Mastodon appartient &lt;strong&gt;aux utilisateurs&lt;/strong&gt; et &lt;strong&gt;non pas à une entreprise&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Petite différence avec Twitter, sur une instance Mastodon, il y a deux listes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une liste &amp;ldquo;locale&amp;rdquo; de messages, qui présente le contenu de &lt;strong&gt;ce&lt;/strong&gt; serveur&lt;/li&gt;
&lt;li&gt;une liste &amp;ldquo;globale&amp;rdquo; de messages qui provient des autre serveurs, fédérés&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cette liste de messages, c&amp;rsquo;est une liste de &amp;ldquo;pouets&amp;rdquo; (&lt;em&gt;toot&lt;/em&gt; en anglais), l&amp;rsquo;équivalent d&amp;rsquo;une liste de &amp;ldquo;tweet&amp;rdquo;. C&amp;rsquo;est rigolo comme terme &lt;span class=&#34;emoji&#34;&gt;🤡&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Vous pouvez visiter librement les fils des gens, et vous abonner à l&amp;rsquo;un deux (comme sur Twitter donc). Je ne vais pas détailler trop longtemps, mais dans les faits les serveurs s&amp;rsquo;envoient les listes entre eux.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/je-pars-de-twitter-et-je-rejoins-mastodon/federation.webp&#34; alt=&#34;Fédération&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dans le fonctionnement, en tant qu&amp;rsquo;utilisateur, on est très proche de Twitter.&lt;/strong&gt; Vous pouvez &amp;ldquo;poueter&amp;rdquo; (au lieu de &amp;ldquo;twitter&amp;rdquo;), vous pouvez retransmettre, vous pouvez répondre, parler en privé, balancer des vidéos, des images, des liens&amp;hellip; C&amp;rsquo;est pareil, mais en plus joli, avec plus de caractères pour vous exprimer et c&amp;rsquo;est bien plus adapté à l&amp;rsquo;échange.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vous avez aussi le droit de déménager votre compte, donc de partir de mamot pour aller sur votre propre serveur par exemple. Et vous ne perdrez ni vos abonnés, ni vos abonnements.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Attention, une instance peut potentiellement ne rien afficher (un temps) en &amp;ldquo;globale&amp;rdquo;, tout simplement par ce que la fédération n&amp;rsquo;est pas activée, qu&amp;rsquo;elle refuse certaines instances, ou simplement qu&amp;rsquo;elle est jeune et que les messages ne sont pas encore là. &lt;strong&gt;C&amp;rsquo;est un défaut/avantage de Mastodon&lt;/strong&gt;: pour que la fédération fonctionne, il faut que des gens se connectent entre-eux d&amp;rsquo;instance en instance, et par conséquent une instance jeune n&amp;rsquo;a RIEN en globale&amp;hellip; C&amp;rsquo;est frustrant pendant un temps. Mais d&amp;rsquo;un autre côté, vous n&amp;rsquo;êtes pas noyé par des flux qui ne vous intéressent pas du tout, parce que l&amp;rsquo;instance ne liste que des contenus proches de votre préférence/communauté.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ça vous évite aussi une polution de messages bien nazes puisque généralement votre communauté (instance) colle à vos préférences.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;on-parle-de-suite-de-la-complication&#34;&gt;On parle de suite de la complication&lt;/h1&gt;
&lt;p&gt;Alors, je ne vais pas non plus vous vanter la puissance de Mastodon tout en vous cachant les points plus compliqués, parce qu&amp;rsquo;il y en a.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord, pour le commun des mortels, l&amp;rsquo;idée de s&amp;rsquo;inscrire sur une communauté ou instance, c&amp;rsquo;est pas une notion évidente. Car quand vous arrivez, par exemple, sur mamot.fr, vous voyez des contenus qui ne sont pas forcément les mêmes que sur l&amp;rsquo;instance techlover. La raison est simplement que chaque instance ne fédère une autre instance que lorsque qu&amp;rsquo;un membre s&amp;rsquo;abonne à un compte de l&amp;rsquo;instance en question. &lt;strong&gt;Et ça, c&amp;rsquo;est pas évident du tout&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Mon instance, &lt;a href=&#34;https://techlover.eu&#34;&gt;Techlover&lt;/a&gt;, est récente, je n&amp;rsquo;ai pas de membres autres que mon compte. Donc, pour que le fil &amp;ldquo;global&amp;rdquo; se remplisse, il faut que je trouve des comptes à suivre sur d&amp;rsquo;autres instances.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Si vous vous inscrivez sur une instance plus ancienne ou connue, vous n&amp;rsquo;aurez pas ce souci.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et donc, si une personne non informée de cette spécificité ne le sait pas, elle ne trouvera aucun contenu intéressant sur &lt;strong&gt;mon&lt;/strong&gt; instance. Il va devoir aller sur d&amp;rsquo;autres instance par lui-même, chercher des contenus, et suivre un compte qui lui plait. Et &lt;strong&gt;seulement à ce moment&lt;/strong&gt;, mon instance va essayer de férerer l&amp;rsquo;instance cible.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Donc, un débutant, je ne lui recommande pas de monter sa propre instance ou de s&amp;rsquo;inscrire sur une instance avec peu d&amp;rsquo;utilisateurs, mais d&amp;rsquo;aller sur une instance proche de son envie et avec beaucoup d&amp;rsquo;inscrits (puis de déménager son compte si il le souhaite)&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Mais sur une instance très utilisée, aucun souci - c&amp;rsquo;est relativement facile.&lt;/p&gt;
&lt;p&gt;Autre complication, le &amp;ldquo;déménagement de compte&amp;rdquo;, qui marche plutôt bien, mais on perd les anciens &amp;ldquo;toots&amp;rdquo; qu&amp;rsquo;on a publié&amp;hellip; Il sont bien entendu toujours consultables sur l&amp;rsquo;ancienne instance, mais je trouve ça dommage. J&amp;rsquo;avais publié des trucs sur mamot.fr, ils ne sont pas consultables sur mon instance &lt;span class=&#34;emoji&#34;&gt;😞&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Et le dernier truc, qui suit du coup les points précédents, c&amp;rsquo;est que voilà&amp;hellip; faut trouver des comptes pour s&amp;rsquo;y abonner. Si vous êtes sur une grosse instance avec plein d&amp;rsquo;inscrits, c&amp;rsquo;est très simple. Le principe de fédération va vous permettre d&amp;rsquo;avoir énormément de toots et de comptes dans le fil global. Mais si, comme moi, vous montez votre propre instance, avec très peu de comptes (genre 1 dans mon cas&amp;hellip;) alors il va falloir vous ballader un peu sur des instances connues pour trouver votre bonheur. Et comme vous n&amp;rsquo;avez pas accès à un moeur de recherche sans être inscrit&amp;hellip; ça peut devenir une quête de longue haleine.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je vous conseille, donc, si vous êtes un débutant, d&amp;rsquo;aller vous inscrire sur une instance réputée, large, et qui correspond à vos attentes. Vous pouvez en trouver ici: &lt;a href=&#34;https://joinmastodon.org/communities&#34;&gt;https://joinmastodon.org/communities&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Et ne créez une instance que si vous êtes technicien, prêts à fouiller et relativement patient.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;me-trouver-&#34;&gt;Me trouver ?&lt;/h1&gt;
&lt;p&gt;Pour voir mon profile: &lt;a href=&#34;https://techlover.eu/@metal3d&#34;&gt;https://techlover.eu/@metal3d&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;En quelques jours, j&amp;rsquo;ai déjà trouvé des gens intéressants, des gens qui sont aussi sur Twitter et que je suivais. Il y a des &amp;ldquo;bots&amp;rdquo; qui retransmettent des tweets de quelques comptes. Bref, je vais rester là bas un moment. Alors&amp;hellip; &lt;strong&gt;oui, il y a aussi des gens qui partagent des trucs avec lesquels je ne suis pas d&amp;rsquo;accord, de la virulence envers les politiques, etc&amp;hellip;&lt;/strong&gt; mais c&amp;rsquo;est beaucoup plus facile de s&amp;rsquo;en échapper, d&amp;rsquo;ignorer et/ou à signaler à la modération. Et comme maitenant j&amp;rsquo;ai mon instance à moi, je sais que je vais vraiment pouvoir me couper de ces discussions débiles sur les vaccins qui tuent des gens, la 5G pour nous contrôler, Raoult notre sauveur&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ha, aussi, je viens de découvrir &lt;a href=&#34;https://crossposter.masto.donte.com.br&#34;&gt;https://crossposter.masto.donte.com.br&lt;/a&gt; qui permet de faire du &amp;ldquo;cross post&amp;rdquo;, donc quand je &amp;ldquo;prout&amp;rdquo; sur Mastodon, ça balance un tweet aussi. Et vous pouvez bien entendu faire l&amp;rsquo;inverse. C&amp;rsquo;est paramétrable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/je-pars-de-twitter-et-je-rejoins-mastodon/crosspost.webp&#34; alt=&#34;Crosspost Mastodon vers Twitter&#34;&gt;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est assez sympathique de ne pas perdre trop de communication. Seul &amp;ldquo;hic&amp;rdquo;, on ne récupère pas les réponses de Twitter dans Mastodon (mais ce serait un beau bordel si c&amp;rsquo;était possible). Donc l&amp;rsquo;idée est de répéter ou d&amp;rsquo;épingler un tweet sur Twitter pour dire au gens &amp;ldquo;je suis plus là, mais sur Mastodon, voici l&amp;rsquo;adresse de mon profile&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Bref, si vous me trouvez sur Twitter, c&amp;rsquo;est ce qui va apparaitre.&lt;/p&gt;
&lt;h1 id=&#34;niveau-applications-mobiles-et-bureau-&#34;&gt;Niveau applications mobiles et bureau ?&lt;/h1&gt;
&lt;p&gt;Il existe bien entendu des &lt;a href=&#34;https://joinmastodon.org/apps&#34;&gt;applications mobiles&lt;/a&gt;, j&amp;rsquo;ai choisi &lt;a href=&#34;https://play.google.com/store/apps/details?id=com.keylesspalace.tusky&#34;&gt;Tusky&lt;/a&gt; sur Android, mais je vous laisse voir le lien précédent pour trouver celui qui vous convient le mieux. Et côté bureau, une simple &amp;ldquo;SSB&amp;rdquo; (ou PWA) me suffit, mais il existe &lt;a href=&#34;https://whalebird.social/&#34;&gt;Whalebird&lt;/a&gt; que je trouve très intéressant.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/je-pars-de-twitter-et-je-rejoins-mastodon/whalebird-2.webp&#34; alt=&#34;Whalebird&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;mais-je-ne-me-voile-pas-la-face&#34;&gt;Mais je ne me voile pas la face&lt;/h1&gt;
&lt;p&gt;Il y a des comptes que j&amp;rsquo;aime énormément sur Twitter, comme Scilabus, E-Penser, Defakator, et j&amp;rsquo;en passe - je ne les trouve pas sur Mastodon. C&amp;rsquo;est triste mais je vais perdre leurs actualités, et potentiellement des échanges sympas. Mais Twitter les noit dans un flot nauséabond de débilités sans nom. Viendront-ils un jour sur une instance Mastodon ? J&amp;rsquo;en sais rien, je le souhaite mais je comprends aussi que leurs abonnés sont sur des réseaux sociaux plus massivement utilisés.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai vu que Defakator et E-Penser ont des bots qui renvoient les tweets sur Mastodon, mais celui de E-Penser semble mort, et celui de Defakator est à 0 abonnés&amp;hellip; Étrange&amp;hellip;&lt;/p&gt;
&lt;p&gt;Je sais aussi que très peu de gens vont me suivre. J&amp;rsquo;avais une petite audience sur Twitter, je vais donc avoir moins d&amp;rsquo;interactions avec eux. C&amp;rsquo;est triste pour moi.&lt;/p&gt;
&lt;p&gt;Oui mais, bon sang, &lt;strong&gt;ce n&amp;rsquo;est pas une fin en soit&lt;/strong&gt;. Cette volonté de popularité ne m&amp;rsquo;interesse pas. Oui j&amp;rsquo;aime avoir des gens qui discutent avec moi et qui s&amp;rsquo;abonnent (ça veut dire que je suis, pour ces gens, une source potentiellement intéressante, et je peux pas nier que c&amp;rsquo;est gratifiant), mais mon but c&amp;rsquo;est surtout d&amp;rsquo;avoir une vraie qualité d&amp;rsquo;échange.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je préfère avoir 10 abonnés avec qui on discute clairement, que 500.000 avec qui l&amp;rsquo;échange est une joute verbale sans fin.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;(bon ok, avoir 500.000 abonnés avec qui on parle sans souci, ce serait encore mieux, mais là c&amp;rsquo;est de l&amp;rsquo;utopie totale)&lt;/p&gt;
&lt;h1 id=&#34;pour-les-techniciens&#34;&gt;Pour les techniciens&lt;/h1&gt;
&lt;p&gt;Juste un point, pour ceux qui en ont envie, puisque Mastodon est opensource et libre d&amp;rsquo;accès, vous avez tout à fait le droit de démarrer votre propre instance, avec vos propres règles, vos modifications, etc. J&amp;rsquo;ai testé l&amp;rsquo;installation avec le &amp;ldquo;helm chart&amp;rdquo; proposé dans les sources, et ça marche très bien sur Kubernetes. Mais vous pouvez démarrer votre instance &amp;ldquo;brute&amp;rdquo; ou avec Docker.&lt;/p&gt;
&lt;p&gt;Il est possible de &amp;ldquo;fermer&amp;rdquo; votre instance à toute fédération entrante ou sortante, donc de faire un réseau social interne, ou limité à certains.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Il est donc tout à fait possible d&amp;rsquo;avoir un réseau social d&amp;rsquo;entreprise, fermé au monde - et bien entendu, un réseau social pour votre club de cracheur de noyau de cerise, etc.. Vous choisissez si oui ou non on peut s&amp;rsquo;y inscire librement, par invitation&amp;hellip; &lt;strong&gt;Vous êtes LIBRE&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;J&amp;rsquo;ai monté mon instance avec un helm chart proposé par le projet Mastodon, je l&amp;rsquo;héberge sur &lt;a href=&#34;https://www.metal3d.org/blog/2020/kapsule-scaleway/&#34;&gt;Kapsule&lt;/a&gt; (de scaleway) et cela demande 3 noeuds (donc c&amp;rsquo;est pas léger hein)&lt;/p&gt;
&lt;p&gt;Mais si vous avez un serveur sous la main qui tient le choc, que ce soit dans votre entreprise ou pour votre association, votre groupe de potes, ou ce que vous voulez, c&amp;rsquo;est très simple à mettre en place. Je vous conseille quand même Kubernetes qui permet d&amp;rsquo;avoir de la haute dispo sans trop forcer, et de pouvoir &lt;em&gt;scaler&lt;/em&gt; votre instance.&lt;/p&gt;
&lt;p&gt;Petite note &lt;strong&gt;importante&lt;/strong&gt;, le gros intérêt d&amp;rsquo;avoir son instance, c&amp;rsquo;est que vous pouvez bannir un compte, ou une instance entière, pour votre communauté. Cela supprimera purement et simplement le lien de férédation entre vous et l&amp;rsquo;instance ou le compte incirminé. Certains parlent de censure, je le répète:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ne confondez pas censure et modération&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;techlover-&#34;&gt;Techlover ?&lt;/h1&gt;
&lt;p&gt;Je voulais m&amp;rsquo;éviter les messages militants, mamot.fr en a un peu trop à mon goût. Je ne trouvais pas spécialement d&amp;rsquo;instance qui collait à mes envies, alors j&amp;rsquo;ai créé mon instance &lt;a href=&#34;https://techlover.eu&#34;&gt;https://techlover.eu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai bloqué l&amp;rsquo;inscription, mais vous pouvez demander une invitation, à condition de lire les conditions d&amp;rsquo;inscription (éviter le militantisme, les messages agressifs contre la politique ou la religion, sans pour autant les interdire, je demande à ce que cette instance soit cool) mais aussi d&amp;rsquo;accepter que cette instance est un test (pour le moment). Je ne sais même pas si la procédure de demande d&amp;rsquo;invitation fonctionne correctement (j&amp;rsquo;ai pas tout testé). Pour le moment cette instance est donc limitée à moi, et potentiellement quelques élus.&lt;/p&gt;
&lt;p&gt;Mais vous pouvez me suivre depuis les autres instances (je n&amp;rsquo;arrête pas de vous dire que vous pouvez vous inscrire n&amp;rsquo;importe où et suivre les autres comptes d&amp;rsquo;autres instances), donc mon compte est là &lt;a href=&#34;https://techlover.eu/@metal3d&#34;&gt;https://techlover.eu/@metal3d&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Je vais potentiellement me faire un second compte pour mes travaux &amp;ldquo;créatifs&amp;rdquo; (Blender en grosse parite), mais le compte @metal3d est le compte principal (et administrateur de la plateforme).&lt;/p&gt;
&lt;h1 id=&#34;je-ne-vais-pas-supprimer-mon-compte-twitter&#34;&gt;Je ne vais pas supprimer mon compte Twitter&lt;/h1&gt;
&lt;p&gt;Honnêtement, je suis parfaitement conscient que Twitter est largement plus utilisé que Mastodon, et que ça va durer des années. Me couper de Twitter et donc de potentiels abonnés est une chose que je veux éviter. Alors comment je vais faire ?&lt;/p&gt;
&lt;p&gt;Pour l&amp;rsquo;heure, je laisse le &amp;ldquo;bot&amp;rdquo; pousser mes messages de Mastodon vers Twitter, et je vais régulièrement pousser un message pour dire que je ne répond plus là bas. Et je vais aussi changer mon profile Twitter pour inciter les gens à venir sur Mastodon au lieu de me suivre sur Twitter.&lt;/p&gt;
&lt;h1 id=&#34;pour-finir&#34;&gt;Pour finir&lt;/h1&gt;
&lt;p&gt;Désolé à ceux qui interragissaient avec moi de manière plaisante sur Twitter, mais ce raz-le-bol m&amp;rsquo;a poussé vers la sortie. Créez vous un compte quelque part et installez Tusky ou Whalebird pour garder le contact. Ça ne vous coute rien, c&amp;rsquo;est bien plus sécure que Twitter ou Facebook.&lt;/p&gt;
&lt;p&gt;Je ne peux que vous inciter à faire grandir le réseau Mastodon. Si votre seule raison de rester sur Twitter est d&amp;rsquo;avoir une grosse audience, je le comprends. Mais si vous voulez des échanges avec des communautés plus adaptées, plus proches de votre domaine, ou de vos passions, alors je ne vois aucune raison de ne pas vous créer un compte.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je vous rappelle que le réseau fédéré Mastodon permet de &lt;strong&gt;vraiment&lt;/strong&gt; supprimer votre compte, de démanager votre compte, de choisir ce que vous verrez, de pouvoir interragir avec la modération des instances, et que tout ça est opensource.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Merci à vous de m&amp;rsquo;avoir lu, merci à mes followers Twitter de comprendre mon choix.&lt;/p&gt;
&lt;h1 id=&#34;ressources&#34;&gt;Ressources&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://joinmastodon.org/communities&#34;&gt;Trouver une instance/communauté&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://whalebird.social/&#34;&gt;Application de bureau - Whalebird&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://play.google.com/store/apps/details?id=com.keylesspalace.tusky&#34;&gt;Application mobile Tusky&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://techlover.eu/@metal3d&#34;&gt;Mon compte&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://joinmastodon.org/apps&#34;&gt;Autres applications (iOs, Bureau&amp;hellip;)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://crossposter.masto.donte.com.br/&#34;&gt;Twitter vos messages Mastodon automatiquement&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;La censure c&amp;rsquo;est très différent de la modération. La modération exclut un &lt;strong&gt;domaine&lt;/strong&gt;, alors que la censure interdit une &lt;strong&gt;opinion&lt;/strong&gt;.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>Firefox SSB ou PWA - C&#39;est quoi et comment on s&#39;en sert ?</title>
      <link>https://www.metal3d.org/blog/2020/firefox-pwa-ssb/</link>
      <pubDate>Thu, 22 Oct 2020 12:25:32 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2020/firefox-pwa-ssb/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2020/firefox-pwa-ssb/ssb-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Chrome propose de transformer une page web en application depuis des lustres, mais Firefox permet aussi de faire à peu près pareil. C&amp;rsquo;est pas si compliqué&amp;hellip; Vous pouvez le faire ! (et pas besoin d&amp;rsquo;être développeur)&lt;/p&gt;
&lt;p&gt;Bon, cet article est aujourd&amp;rsquo;hui désuet car Firefox &lt;a href=&#34;https://bugzilla.mozilla.org/show_bug.cgi?id=1682593&#34;&gt;abandonne le support SSB&lt;/a&gt;, ce qui a pour conséquences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;de me mettre très en colère, je ne vois pas comment Firefox peut luter contre Google Chrome (et ses dériviés) si il abandonne des features qui sont supportées par tous les concurrents&lt;/li&gt;
&lt;li&gt;de me faire abandonner purement et simplement ce navigateur qui fait tout pour m&amp;rsquo;empêcher d&amp;rsquo;utiliser le Web comme je l&amp;rsquo;entends&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, cet article est toujours bon pour les informations pratiques, mais pas pour la méthode qui va tout simplement disparaitre. Donc pour ma part, et tant pis pour les détracteurs, je vous recommande &amp;ldquo;Brave&amp;rdquo;, qui est une très bonne mouture dérivée de Chromium (donc la forme open source et libre de Chrome, avec le moteur Blink), qui permet la création de SSB/PWA en deux cliques.&lt;/p&gt;
&lt;p&gt;Firefox n&amp;rsquo;est pas pour moi&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;rien-compris-explique-&#34;&gt;Rien compris, explique !&lt;/h1&gt;
&lt;p&gt;Un site web à la base, c&amp;rsquo;est en gros &lt;em&gt;des pages liées entre elles&lt;/em&gt; avec des &amp;ldquo;liens&amp;rdquo;, voilà&amp;hellip; mais les technologies WEB ont évolué à une vitesse folle. Aujourd&amp;rsquo;hui, une page peut jouer des sons, des vidéos, des animations en 3D, interagir avec des serveurs sans quiter la page (chatroom, interactions&amp;hellip;), vous pouvez même éditer des documents, des tableurs, faire du travail photo, sur une &amp;ldquo;page web&amp;rdquo;. On est donc en train de parler d&amp;rsquo;application bien plus que de site web.&lt;/p&gt;
&lt;p&gt;Mais alors elle est où la différence entre, par exemple, votre Excel et Google Sheet (tableur de Google) ? Et bien simplement que pour Excel, vous avez &amp;ldquo;installé&amp;rdquo; un logiciel, alors que pour Google Sheet, vous devez visiter un site web qui fourni, dans le &lt;strong&gt;navigateur&lt;/strong&gt; une interface qui est l&amp;rsquo;application de tableur.&lt;/p&gt;
&lt;p&gt;OK ? Bon maintenant, imaginons que je vous donne une application que vous installez sur votre PC, mais qui affiche Google Sheet sans que vous ayez à ouvrir la page internet ?&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est ce que l&amp;rsquo;on appelle une PWA (Progressive Web Application), et plus généralement un SSB (Specific Site Browser). Dans les faits, ça existe depuis des années, et on a même des outils pour générer des applications &amp;ldquo;web installables&amp;rdquo; (par exemple Electon). Bon bref&amp;hellip;&lt;/p&gt;
&lt;p&gt;Chrome a une option pas si connue que ça, elle permet de transformer n&amp;rsquo;importe quel site web en une application de bureau. En cochant bien la case &amp;ldquo;ouvrir dans une fenêtre séparée&amp;rdquo;, Chrome vous crée un racourcis sur le bureau pour ouvrir le site comme une vaie application. Par exemple avec une page wikipedia:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/firefox-pwa-ssb/pwa1.gif&#34; alt=&#34;Créer une PWA sur Chrome, Brave ou Edge&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et voilà, vous avez une icone &amp;ldquo;wikipedia&amp;rdquo; sur le bureau (ou dans votre menu &amp;ldquo;Activités&amp;rdquo;), cool&amp;hellip; Imaginez maintenant que vous fassiez cela avec Gmail, Youtube, etc&amp;hellip; plus besoin d&amp;rsquo;ouvir Chrome et d&amp;rsquo;aller chercher la page, vous avez maintenant une application de Bureau.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et c&amp;rsquo;est un peu à cause de cela que je suis parti de Firefox il y a quelques temps. Je n&amp;rsquo;avais pas ce confort avec Firefox, alors qu&amp;rsquo;avec Brave, Chromium ou encore Google Chrome, c&amp;rsquo;était si simple&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Sauf que depuis quelques temps, le vent commence à tourner et je retrouve enfin le panda roux.&lt;/p&gt;
&lt;p&gt;Alors, ce qu&amp;rsquo;on va faire, c&amp;rsquo;est que je vous montre comment ça fonctionne, et ensuite à vous de créer en quelques secondes des applications Netflix, Gmail, Facebook, ce que vous voulez, et ce &lt;strong&gt;sans être développeur !&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;bon-donc-avec-firefox&#34;&gt;Bon, donc avec Firefox&amp;hellip;&lt;/h1&gt;
&lt;p&gt;C&amp;rsquo;est un poil plus compliqué, mais au final c&amp;rsquo;est complètement jouable. &lt;strong&gt;Cette option est expérimentale, donc ça ne marchera pas parfaitement, on en parle après.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Cela faite même assez (trop) longtemps que cette option existe, et que ce &amp;ldquo;mode&amp;rdquo; n&amp;rsquo;est pas finalisé (près de 4 ans).&lt;/p&gt;
&lt;p&gt;Il faut passer par une activation de ce mode. Ouvrez Firefox et dans la barre d&amp;rsquo;adresse vous tapez: &lt;code&gt;about:config&lt;/code&gt; - et vous &amp;ldquo;acceptez les risques&amp;rdquo;. Tapez simplement &amp;ldquo;ssb&amp;rdquo; dans la barre de recherche, et vous allez voir ceci:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/firefox-pwa-ssb/ssb-config.webp&#34; alt=&#34;Config SSB dans Firefox&#34;&gt;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée est de faire en sorte que ce paramètre soit à &amp;ldquo;true&amp;rdquo;, donc cliquez sur l&amp;rsquo;icone à droite pour changer la valeur. Voilà, c&amp;rsquo;est fait.&lt;/p&gt;
&lt;p&gt;Maintenant, allez sur un site, n&amp;rsquo;importe lequel&amp;hellip; et dans le menu de votre navigateur vous devriez trouver une option &amp;ldquo;Use this site in App mode&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/firefox-pwa-ssb/ssb-2.gif&#34; alt=&#34;Activer le mode App&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et voilà, l&amp;rsquo;application existe&amp;hellip; sur Firfox. Oui parce que voilà, il crée pas d&amp;rsquo;icone sur le bureau (pas encore), et je sais que c&amp;rsquo;est casse pied. Cela-dit, vous allez trouver l&amp;rsquo;application dans le menu:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/firefox-pwa-ssb/ssb-3.gif&#34; alt=&#34;Retrouver votre App&#34;&gt;&lt;/p&gt;
&lt;p&gt;OK&amp;hellip; Donc là c&amp;rsquo;est fait, j&amp;rsquo;ai mon application.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais je veux l&amp;rsquo;avoir dans mon menu de bureau, je ne veux pas avoir à démarrer Firefox pour démarrer mon application en second lieu. Alors je fais comment ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;La solution, c&amp;rsquo;est de faire tout soit-même.&lt;/p&gt;
&lt;h2 id=&#34;via-une-ligne-de-commande-et-donc-pouvoir-créer-une-icone-de-bureau&#34;&gt;Via une ligne de commande, et donc pouvoir créer une icone de bureau&lt;/h2&gt;
&lt;p&gt;Je ne parle que de Linux ici, désolé pour les Windows et autre OSX, mais j&amp;rsquo;ai pas ça sous la main&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans un terminal, on va commencer par tester si ça marche. Prenons l&amp;rsquo;exemple d&amp;rsquo;une application web pour aller sur netflix par exemple.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;firefox --ssb https://netflix.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&amp;rsquo;exemple est un peu foireux parce que vous allez devoir vous authentifier sur une autre fenêtre, donc le faire, puis fermer votre application et relancer la commande. Après ce sera bon, il aura votre cookie d&amp;rsquo;authentification.&lt;/p&gt;
&lt;p&gt;Mais bon, voilà ce que j&amp;rsquo;ai de mon coté:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/firefox-pwa-ssb/netflix-app.webp&#34; alt=&#34;Une app netflix&#34;&gt;&lt;/p&gt;
&lt;p&gt;Donc&amp;hellip; On a une commande qui permet de démarrer un site en mode &amp;ldquo;SSB&amp;rdquo;, bha cool, on va juste créer un fichier desktop !&lt;/p&gt;
&lt;p&gt;Dans un editeur de texte, tapez ceci:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Name=Netflix
Comment=Films et séries sur Netflix
Exec=/usr/bin/firefox --ssb  https://netflix.com
Icon=netflix
Terminal=false
Type=Application
StartupNotify=true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous l&amp;rsquo;enregistrez  dans &lt;code&gt;$HOME/.local/share/applications/firefox-netflix.desktop&lt;/code&gt;. Allez chercher une icone pour Netflix, et placez la dans &lt;code&gt;$HOME/.local/share/icons/netflix.png&lt;/code&gt; (ou .svg, au choix).&lt;/p&gt;
&lt;p&gt;Et voilà&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;video src=&#34;https://www.metal3d.org/blog/2020/firefox-pwa-ssb/netflix-desktop.webm&#34; style=&#34;max-width: 90%; margin: auto&#34; controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Et bien entendu, on peut mettre cette icone sur le bureau (si vous avec encore des icones de bureau&amp;hellip;) ou dans la barre d&amp;rsquo;accès rapide.&lt;/p&gt;
&lt;div class=&#34;float-right&#34;&gt;
&lt;img src=&#34;https://www.metal3d.org//blog/2020/firefox-pwa-ssb/taskbar.webp&#34; /&gt;
&lt;/div&gt;
&lt;p&gt;Donc pas trop mal hein. C&amp;rsquo;est simplement ce dont j&amp;rsquo;avais besoin, malgré les actions à faire pour y arriver, je suis vraiment content d&amp;rsquo;avoir enfin un début de solution qui me permette de revenir à Firefox. Et donc, de me passer de Brave, Chrome, Chromium ou encore (oh mon dieu&amp;hellip;) de Edge (oui oui&amp;hellip; sur Linux&amp;hellip;) Allez voir la page de Korben qui utilise à merveille l&amp;rsquo;ironie de cette abération: &lt;a href=&#34;https://korben.info/microsoft-edge-linux.html&#34;&gt;https://korben.info/microsoft-edge-linux.html&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;oui-mais&#34;&gt;Oui mais&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Il y a des soucis&amp;hellip; pas énormes mais suffisants pour que vous restiez un peu pantois devant votre ordinateur.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;D&amp;rsquo;abord, certains sites comme Gmail me font des blagues. Impossible de visiter un lien qui apparait dans un mail. Je n&amp;rsquo;ai pas d&amp;rsquo;explications&amp;hellip; donc bha&amp;hellip;&amp;ldquo;copier/coller du lien&amp;rdquo; &lt;span class=&#34;emoji&#34;&gt;👍&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Les extensions ne marchent pas&amp;hellip; donc si vous avez un bloqueur de pub, ou une extension comme &amp;ldquo;youtube enhance&amp;rdquo; bha vous l&amp;rsquo;aurez pas (je cherche encore comment outrepasser ce souci)&lt;/li&gt;
&lt;li&gt;Attention, le comportement de l&amp;rsquo;application est de &amp;ldquo;s&amp;rsquo;ouvrir quoiqu&amp;rsquo;il arrive&amp;rdquo;, même si elle est déjà ouverte dans une autre fenêtre (je me suis retrouvé avec 5 fenêtres ouvertes pour la même application), pensez-y&lt;/li&gt;
&lt;li&gt;Le mode &amp;ldquo;vidéo incrustée&amp;rdquo; du lecteur intégré est bien bugué&amp;hellip;&lt;/li&gt;
&lt;li&gt;Si le site ne vous propose pas un bouton &amp;ldquo;plein écran&amp;rdquo;, la touche &amp;ldquo;F11&amp;rdquo; ne fait rien&amp;hellip; c&amp;rsquo;est pourtant un truc que je voulais&lt;/li&gt;
&lt;li&gt;Les PWA de Chrome et dérivés ont une jolie décoration de titre de fenêtre, malheureusement ce n&amp;rsquo;est pas le cas avec Firefox SSB, mais je suis certain que ça va évoluer&lt;/li&gt;
&lt;li&gt;L&amp;rsquo;icone de votre app, dans les tâches, c&amp;rsquo;est toujours celle de Firefox&amp;hellip; pareil j&amp;rsquo;attends avec impatience que ce soit corrigé&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bref, ça ne rend pas les SSB inutilisables, mais il faut accepter que certains détails ne sont pas encore là.&lt;/p&gt;
&lt;h1 id=&#34;et-vous-pouvez-aller-loin&#34;&gt;Et vous pouvez aller loin&lt;/h1&gt;
&lt;p&gt;J&amp;rsquo;ai une bonne dizaine d&amp;rsquo;applications de ce genre, Gmail, Youtube, Netflix, Mastodon, ou encore Twitter (que je vais quitter, mais j&amp;rsquo;en parlerai dans un autre post). Sous linux j&amp;rsquo;ai juste à utiliser un petite patron de fichier desktop que j&amp;rsquo;ai sous la main, je trouve une icone et voilà. En 2 minutes j&amp;rsquo;ai une PWA (ou bon ok SSB App) qui fonctionne avec Firefox.&lt;/p&gt;
&lt;p&gt;Je voulais vraiment revenir à Firefox, non pas que je hais Google (loin de là) mais Firefox est tellement plus respectueux de ses utilisateurs et il marche tellement bien, j&amp;rsquo;étais triste de m&amp;rsquo;en passer depuis tant de temps. Avec les SSB, je comble le manque.&lt;/p&gt;
&lt;p&gt;A terme, soyons clair, les SSB seront bien mieux supportés et bien entendu nous n&amp;rsquo;aurons plus à faire des manipulations de ce genre pour avoir une entrée sur le bureau. Pour l&amp;rsquo;heure, ça fonctionne pas trop mal.&lt;/p&gt;
&lt;p&gt;PS: bon du coup, je vous incite à utiliser Brave dans mon appel aux dons&amp;hellip; vais devoir reformuler.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Démystifier Python Async</title>
      <link>https://www.metal3d.org/blog/2020/d%C3%A9mystifier-python-async/</link>
      <pubDate>Tue, 04 Aug 2020 10:58:43 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2020/d%C3%A9mystifier-python-async/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2020/d%C3%A9mystifier-python-async/demystifier-python-async-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;C&amp;rsquo;est pas si évident qu&amp;rsquo;avec JS ou Go, mais c&amp;rsquo;est vraiment puissant et utile, et c&amp;rsquo;est pas si compliqué.&lt;/p&gt;
&lt;p&gt;Python propose l&amp;rsquo;asynchronisme natif depuis la version 3.4. C&amp;rsquo;est à dire depuis 2013&amp;hellip; et pourtant, malgré l&amp;rsquo;intérêt et la puissance de ce paradigme, on remarque que peu de développeurs Python s&amp;rsquo;en servent. Et je vais être honête, je en m&amp;rsquo;en suis pas beaucoup servi. Le fait est que Go propose un système de &amp;ldquo;goroutine&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&amp;rdquo; qui abstrait énormément de contraintes alors que Python, malgré toute l&amp;rsquo;affection que j&amp;rsquo;ai pour ce langage, demande un peu plus de réflexion pour la gestion des tâches asynchrones.&lt;/p&gt;
&lt;p&gt;La plupart des langages propose de quoi faire des tâches asynchrones, Javascript propose les &amp;ldquo;Promises&amp;rdquo;, Go propose ses &amp;ldquo;goroutines&amp;rdquo;, et j&amp;rsquo;en passe. Et tout le monde en est content. Si les développeurs Python utilise encore trop peu les coroutines c&amp;rsquo;est que quelque chose ne va pas, n&amp;rsquo;est-ce pas ?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je vous partage ma théorie: c&amp;rsquo;est tout simplement mal expliqué.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Clairement, il y a quelques années, j&amp;rsquo;ai trouvé la documentation de &lt;code&gt;asyncio&lt;/code&gt; austère, c&amp;rsquo;était obscure, ça donnait pas envie. Mais par la force des choses, j&amp;rsquo;ai insisté et j&amp;rsquo;ai très vite compris que c&amp;rsquo;était très puissant, utile, et au final absolument pas compliqué.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;aurai aimé, à cette époque, qu&amp;rsquo;on me montre &lt;code&gt;asyncio&lt;/code&gt; avec des exemples plus bruts - voilà donc ici comment j&amp;rsquo;aurais aimé qu&amp;rsquo;on me présente les choses.&lt;/p&gt;
&lt;h1 id=&#34;tldr&#34;&gt;TL;DR&lt;/h1&gt;
&lt;p&gt;Pour faire court:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import asyncio

async MachinTruc():
    # faire plein de chose
    # mais il faut avoir un appel
    # à await, au moins une fois
    await asyncio.sleep(0)


# conseil: avoir une fonction principale, asynchrone
async main():
    # démarre une coroutine
    await MachinTruc()

    # ou alors
    t = MachinTruc()
    await t

    # ou alors démarrer créer une tache
    # elle démarre &amp;quot;quasi immédiatement&amp;quot;
    task = asyncio.create_task(MachinTruc())

    # et l&#39;attendre plus tard
    await task

    # ou alors attendre une liste de taches
    await asyncio.wait(liste_de_taches)

    # ou, pour avoir les résultats
    resultats = await asyncio.gather(liste_de_taches)


if __name__ == &amp;quot;__main__&amp;quot;:
    # on crée une boucle d&#39;évènements
    asyncio.run(main())

    # ou avec python &amp;lt; 3.7
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est tout ce qu&amp;rsquo;il faut savoir: &lt;code&gt;await&lt;/code&gt; attend une coroutine ou une tache et permet à d&amp;rsquo;autres coroutines d&amp;rsquo;avoir une chance d&amp;rsquo;exécuter des trucs.&lt;/p&gt;
&lt;p&gt;Maintenant, voilà le détail.&lt;/p&gt;
&lt;h1 id=&#34;dabord-cest-quoi-lasynchronisme--cest-un-thread-&#34;&gt;D&amp;rsquo;abord c&amp;rsquo;est quoi l&amp;rsquo;asynchronisme ? C&amp;rsquo;est un thread ?&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Cette partie est utile seulement si vous n&amp;rsquo;avez jamais entendu parler de coroutine, d&amp;rsquo;asynchronisme, ni de &amp;ldquo;Promise&amp;rdquo;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ne tournons pas en rond: NON (enfin pas exactement, et zut on commence à s&amp;rsquo;embrouiller), une coroutine n&amp;rsquo;est pas un Thread.&lt;/p&gt;
&lt;p&gt;Les tâches asynchrones permettent de faire &lt;em&gt;comme si&lt;/em&gt; vous aviez des threads, sans pour autant avoir des tâche &lt;em&gt;en parallèle&lt;/em&gt;. L&amp;rsquo;idée c&amp;rsquo;est que vos tâches sachent attendre pour laisser la place à d&amp;rsquo;autres tâches. Il faut &amp;ldquo;ordonnancer&amp;rdquo; ces tâches.&lt;/p&gt;
&lt;p&gt;Cela impose quelques règles, dont une qui va vous poser un gros souci, mais qu&amp;rsquo;on saura résoudre par la suite: il ne &lt;strong&gt;faut pas qu&amp;rsquo;une tâche soit bloquante&lt;/strong&gt; (et si c&amp;rsquo;est le cas, pas de panique je vais vous donner des solutions)&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Bloquer&amp;rdquo; ça veut dire &lt;em&gt;prendre le CPU pour soi et ne pas permettre à quelqu&amp;rsquo;un d&amp;rsquo;autre de l&amp;rsquo;utiliser&lt;/em&gt;. C&amp;rsquo;est là où les Threads sont très différents dans le fonctionnement. Eux vont savoir gérer une tâche bloquant le CPU. Mais pour cela, les Thread utilisent une &amp;ldquo;commutation de contexte&amp;rdquo; (context switch) qui demande à l&amp;rsquo;OS de deplacer régulièrement un processus pour qu&amp;rsquo;un coeur de CPU puisse gérer toutes les tâches.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Les Threads ont donc moins de contraintes pour le développeur, mais si vous en avez des miliers à gérer, cela peut être très brutal en terme de resources.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Bref, les coroutines, asynchrones, sont éxécutées dans un seul &amp;ldquo;process&amp;rdquo;, et vont simplement se partager du temps.&lt;/p&gt;
&lt;p&gt;Dans énormément de cas, vous aurez l&amp;rsquo;impression de faire du parallélisme, gagner en temps, et en plus éviter de défoncer votre CPU et la RAM. Mais dans les faits, c&amp;rsquo;est surtout que vous allez faire en sorte qu&amp;rsquo;une tâche &amp;ldquo;pas prête&amp;rdquo; saura attendre en laissant la main à d&amp;rsquo;autres tâches de faire leur cuisine.&lt;/p&gt;
&lt;p&gt;Les coroutine, et l&amp;rsquo;asynchronisme, vous les avez certainement utilisé dans d&amp;rsquo;autres langages. Par exemple les &amp;ldquo;promesses&amp;rdquo; en Javascript, ou les &amp;ldquo;goroutines&amp;rdquo; dans Go. Notez toutefois que Go a un avantage certain, c&amp;rsquo;est qu&amp;rsquo;il sait démarrer un Thread si votre tâche est bloquante&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;await---on-en-parle-de-suite&#34;&gt;&lt;code&gt;await&lt;/code&gt; - on en parle de suite&lt;/h1&gt;
&lt;p&gt;Je préfère commencer par un truc qui est généralement expliqué après coup. &lt;strong&gt;Ne sautez pas cette section&lt;/strong&gt; - même si vous avez une idée de ce à quoi ça sert, parce que &lt;strong&gt;bien souvent c&amp;rsquo;est cette notion qui fait défaut&lt;/strong&gt; et qui empêche son utilisation.&lt;/p&gt;
&lt;p&gt;Python propose plusieurs types qui permettent l&amp;rsquo;asynchronisme, ces types sont dit &amp;ldquo;Awaitable&amp;rdquo; (qu&amp;rsquo;on peut attendre) c&amp;rsquo;est-à-dire qu&amp;rsquo;ils peuvent entrer dans un gestionnaire d&amp;rsquo;évènements (on en parle après), et qu&amp;rsquo;on doit les &lt;strong&gt;attendre&lt;/strong&gt;. Le mot clef &lt;code&gt;await&lt;/code&gt; est justement fait pour ça, &lt;strong&gt;mais pas seulement&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;une part, oui, l&amp;rsquo;idée est de pouvoir attendre qu&amp;rsquo;une fonction réponde, mais en quoi ce serait différent d&amp;rsquo;appeler une fonction non asynchrone? Et bien c&amp;rsquo;est là qu&amp;rsquo;on zappe un principe &lt;strong&gt;très important&lt;/strong&gt; de ce que fait ce fameux &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;await&lt;/code&gt; permet aussi, et &lt;strong&gt;surtout&lt;/strong&gt;, de dire à Python qu&amp;rsquo;on peut aller voir si une autre coroutine en attente peut continuer de travailler.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En d&amp;rsquo;autres termes, c&amp;rsquo;est quand Python rencontre le mot clef &amp;ldquo;&lt;code&gt;await&lt;/code&gt;&amp;rdquo; qu&amp;rsquo;il va donner une chance à une autre coroutine de continuer son travail. En soit, &lt;code&gt;await&lt;/code&gt; c&amp;rsquo;est le checkpoint, l&amp;rsquo;étape.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;mais-cest-quoi-une-coroutine-&#34;&gt;Mais c&amp;rsquo;est quoi une coroutine ?&lt;/h1&gt;
&lt;p&gt;Créons une function qui prend du temps à répondre. Dans cette fonction, nous simulons un traitement long en utilisant &lt;code&gt;asyncio.sleep()&lt;/code&gt;. Nous ne pouvons pas utiliser &lt;code&gt;time.sleep()&lt;/code&gt; parce que, comme je vous l&amp;rsquo;ai dit auparavent, il &lt;strong&gt;faut appeler &lt;code&gt;await&lt;/code&gt; pour que Python puisse laisser les autres coroutines s&amp;rsquo;exécuter periodiquement&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import asyncio
import random
import time  # on s&#39;en servira après

# async =&amp;gt; la fonction est une coroutine
async def bigWork(i):
    print(&#39;Big work %d starts&#39; % i)
    delay = random.uniform(0, 1.5)
    # surtout n&#39;utilisez pas time.sleep()
    # pour rappel, il faut avoir un appel à await pour que
    # la coroutine bigWork puisse laisser une chance aux autres
    # de tourner
    await asyncio.sleep(delay)
    print(&#39;Big work %d ends after %.2f seconds&#39; % (i, delay))

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette fonction est déclarée &amp;ldquo;asynchrone&amp;rdquo; via le mot clef &lt;code&gt;async&lt;/code&gt;, cela veut dire qu&amp;rsquo;elle pourra être ordonnancé, c&amp;rsquo;est une &amp;ldquo;coroutine&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Voyons ce qu&amp;rsquo;il se passe si on appelle la fonction &lt;code&gt;bigWork()&lt;/code&gt; (ici, dans iPython):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;&amp;gt;&amp;gt;&amp;gt; bigWork(1)
&amp;lt;coroutine object bigWork at 0x7f1814622440&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ha&amp;hellip; ça n&amp;rsquo;a rien lancé ! Mais à la place, j&amp;rsquo;ai reçu une &amp;ldquo;coroutine&amp;rdquo;, un objet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C&amp;rsquo;est la première chose à retenir: une fonction asynchrone, on ne l&amp;rsquo;appelle pas à l&amp;rsquo;arrache, on doit l&amp;rsquo;ordonnancer ou l&amp;rsquo;attendre&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Le souci, c&amp;rsquo;est que si je veux l&amp;rsquo;attendre, il faut que j&amp;rsquo;appelle la fonction avec &lt;code&gt;await&lt;/code&gt; et ça doit être fait dans une coroutine:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  File &amp;quot;/tmp/a.py&amp;quot;, line 11
    await bigWork(1)
    ^
SyntaxError: &#39;await&#39; outside function
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors je sais ce qui commence à vous prendre la tête (et c&amp;rsquo;est tout à fait normal): si je dois attendre une coroutine au sein d&amp;rsquo;une coroutine, qu&amp;rsquo;il faut attendre aussi, on tourne en rond là.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;La réponse est moins évidente qu&amp;rsquo;en JS ou en Go, Python demande une &amp;ldquo;boucle d&amp;rsquo;évènements&amp;rdquo; qu&amp;rsquo;on appelle aussi un &amp;ldquo;ordonnancement&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Un ordonnancement&lt;/strong&gt;, c&amp;rsquo;est un gestionnaire de tâches qui va faire tourner des &amp;ldquo;trucs&amp;rdquo; et les orchestrer. Quand une tâche peut attendre, on passe à une autre, et quand l&amp;rsquo;autre a fini, on regarde si une autre peut répondre et ainsi de suite. Et si vous avez suivi, c&amp;rsquo;est quand on rencontrera le mot &lt;code&gt;await&lt;/code&gt; que l&amp;rsquo;ordonnancement passera d&amp;rsquo;une routine à l&amp;rsquo;autre.&lt;/p&gt;
&lt;p&gt;Cet ordonnancement est très simple à démarrer, &lt;code&gt;asyncio.run&lt;/code&gt; prend en argument une coroutine, et il attend qu&amp;rsquo;elle soit finie. Si d&amp;rsquo;autres coroutines sont démarrées par cette coroutine, il attendra aussi. Il va gérer &lt;strong&gt;toutes&lt;/strong&gt; les coroutines au sein d&amp;rsquo;un processus.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# si vous travaillez avec Python &amp;gt;= 3.7
asyncio.run(bigWork(1))

# sinon...
loop = asyncio.get_event_loop()
loop.run_until_complete(bigWork(1))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tout ce que dit cette (ou ces) ligne(s) c&amp;rsquo;est: &amp;ldquo;Donne moi un gestionnaire de coroutine, et démarre &lt;code&gt;bigWork(1)&lt;/code&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Là, pour le moment, on a rien fait de très intéressant. On démarre une coroutine et on l&amp;rsquo;attend, ça ne change pas grand chose à démarrer une fonction non-asynchrone, pire c&amp;rsquo;est même plus compliqué à coder.&lt;/p&gt;
&lt;h1 id=&#34;il-est-temps-daller-plus-loin&#34;&gt;Il est temps d&amp;rsquo;aller plus loin&lt;/h1&gt;
&lt;p&gt;Allons de l&amp;rsquo;avant, et passons à ce qui nous intéresse: avoir plein de coroutines en &amp;ldquo;parallèle&amp;rdquo; (oui, en concurrence&amp;hellip;). Nous allons crére une fonction générale qui va lancer plein de tâches, et cette fonction sera donc celle qui sera attendu par la boucle d&amp;rsquo;évènements. Ce sera plus simple pour la suite.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;async def main():
    # on va lancer 5 coroutines
    for i in range(5):
        await bigWork(i)

# et ici je chronomètre l&#39;ensemble
start = time.time()
asyncio.run(main())  # on démarre ici
end = time.time() - start
print(&#39;Total time: %.2f&#39; % end)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Encore une fois (je vous tease un peu hein), pas trop d&amp;rsquo;intérêt mais vous voyez que je peux lancer plein de tâches les unes après les autres, et quand tout est terminé alors mon script s&amp;rsquo;arrête:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python /tmp/a.py
Big work 0 starts
Big work 0 ends after 0.57 seconds
Big work 1 starts
Big work 1 ends after 0.42 seconds
Big work 2 starts
Big work 2 ends after 0.22 seconds
Big work 3 starts
Big work 3 ends after 0.34 seconds
Big work 4 starts
Big work 4 ends after 0.46 seconds
Total time: 2.05
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais, on est d&amp;rsquo;accord, le temps total correspond plus ou moins à la somme des temps des coroutine. Elles ont démarré dans l&amp;rsquo;ordre et aucune coroutine ne démarre avant que l&amp;rsquo;autre ait terminé.&lt;/p&gt;
&lt;p&gt;Faisons une pause deux secondes avant de continuer. Dans l&amp;rsquo;ensemble, si vous aviez d&amp;rsquo;autres coroutines dans votre code, alors votre script est très bien écrit. Chaque itération de la boucle &lt;code&gt;for&lt;/code&gt; fait un &lt;code&gt;await&lt;/code&gt; et permet donc à une autre coroutine de tourner. Mais, dans notre exemple, nous voulons faire tourner nos coroutines &lt;code&gt;bigWork&lt;/code&gt; en concurrence. Donc, effectivement, à partir de maintenant, on va chercher à étudier des fonctions qui permettent d&amp;rsquo;aller plus loin, mais dans vos développements vous pouvez déjà estimer que ce bout de code est viable.&lt;/p&gt;
&lt;p&gt;Reprenons, nous voulons maintenant faire en sorte que toues les coroutines &lt;code&gt;bigWork&lt;/code&gt; tournent en concurrence.&lt;/p&gt;
&lt;p&gt;Rappelez vous maintenant ce qu&amp;rsquo;il s&amp;rsquo;est passé quand on a appelé &lt;code&gt;bigWork()&lt;/code&gt; sans utliser &lt;code&gt;await&lt;/code&gt;. Vous vous souvenez que la fonction n&amp;rsquo;est pas appelée mais que nous avions une &amp;ldquo;coroutine&amp;rdquo; en retour. Et bien cherchons à les stocker puis à les attendre &lt;strong&gt;en même temps&lt;/strong&gt;, et ce avec une des méthodes proposées par le module &lt;code&gt;asyncio&lt;/code&gt;, par exemple &lt;code&gt;wait()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;async def main():
    tasks = []
    for i in range(5):
        tasks.append(bigWork(i))

    # on attend que la liste
    # de coroutines soit terminée
    await asyncio.wait(tasks)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et maintenant, tout se passe comme on le veut:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python /tmp/a.py
Big work 1 starts
Big work 2 starts
Big work 3 starts
Big work 0 starts
Big work 4 starts
Big work 1 ends after 0.20 seconds
Big work 3 ends after 0.30 seconds
Big work 0 ends after 1.08 seconds
Big work 2 ends after 1.32 seconds
Big work 4 ends after 1.46 seconds
Total time: 1.47
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le temps total a pris à peine plus de temps que la tâche la plus longue. Et là c&amp;rsquo;est ce qu&amp;rsquo;on attendait !&lt;/p&gt;
&lt;p&gt;Remarquez aussi que les tâches n&amp;rsquo;ont pas été démarrées dans l&amp;rsquo;ordre, ça peut paraitre étonnant mais c&amp;rsquo;est aussi un effet de l&amp;rsquo;asynchronisme, les tâches sont ordonnancées de manière non déterministe (on ne peut pas prévoir).&lt;/p&gt;
&lt;h1 id=&#34;task-et-coroutine&#34;&gt;Task et Coroutine&lt;/h1&gt;
&lt;p&gt;Parlons maintenant d&amp;rsquo;un piège, celui de vouloir créer des coroutines et de les attendre via &lt;code&gt;await&lt;/code&gt; sans passer pas &lt;code&gt;asyncio.wait&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;async def main():
    tasks = []
    for i in range(5):
        tasks.append(bigWork(i))

    for t in tasks:
        await t
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est une erreur logique, mais si vous lancez votre script, vous allez vous retrouver dans le même état que lorsque vous faisiez &lt;code&gt;await biWork(i)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mais pourquoi ? Comment fait &lt;code&gt;asyncio.wait()&lt;/code&gt; pour s&amp;rsquo;en sortir ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est à ce moment qu&amp;rsquo;il faut parler des &lt;code&gt;Task&lt;/code&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Un &lt;em&gt;coroutine&lt;/em&gt; est un objet qui représente une fonction asynchrone, mais pour qu&amp;rsquo;elle soit exécutée il faut l&amp;rsquo;encapsuler dans une tâche (Task). Sauf que Python le fait pour vous, de manière transparente, quand vous utilisez &lt;code&gt;await&lt;/code&gt; (si la coroutine n&amp;rsquo;est pas encapsulée). Quant à &lt;code&gt;asyncio.wait()&lt;/code&gt;, ainsi que d&amp;rsquo;autres fonctions de ce module, il va aussi le faire pur vous.&lt;/p&gt;
&lt;p&gt;Alors comment faire ça manuellement ? Comment démarrer une coroutine &lt;em&gt;tout de suite&lt;/em&gt;, et les attendre après coup ?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;async def main():
    tasks = []
    for i in range(5):
        # je découpe tout pour vous montrer les étapes
        coroutine = bigWork(i)
        task = asyncio.create_task(coroutine)
        tasks.append(task)

    for t in tasks:
        await t
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le principe est donc d&amp;rsquo;avoir une coroutine, et de l&amp;rsquo;encapsuler avec &lt;code&gt;create_task&lt;/code&gt;, cela à pour effet de démarrer tout de suite la tâche. Le mot clef &lt;code&gt;await&lt;/code&gt; voit bien que vous avez non plus une coroutine mais une tâche (qui est &lt;code&gt;Awaitable&lt;/code&gt;) et va juste attendre qu&amp;rsquo;elle soit terminée.&lt;/p&gt;
&lt;p&gt;Effectivement, on aura plutôt tendance à faire &lt;code&gt;asyncio.create_task(bigWork(i))&lt;/code&gt; mais je vous ai découpé les opérations pour que ce soit bien clair.&lt;/p&gt;
&lt;p&gt;Bon, si vous lancez votre script, on revient à ce que fait &lt;code&gt;asyncio.wait()&lt;/code&gt;. Les tâches sont ordonnancées, tout va bien.&lt;/p&gt;
&lt;h1 id=&#34;et-quand-ça-bloque-&#34;&gt;Et quand ça bloque ?&lt;/h1&gt;
&lt;p&gt;Nous y voilà&amp;hellip; Le souci est que vous ne maitrisez pas forcément tout le code, vous allez utiliser des packages externes, ou des anciens développements, et bien souvent cet historique de travail ne contient pas de code asynchrone. Alors, on s&amp;rsquo;en sort comment&amp;hellip; ?&lt;/p&gt;
&lt;p&gt;Prenons notre code &lt;code&gt;bigWork&lt;/code&gt; et rendons le &lt;code&gt;synchrone&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;en supprimant &lt;code&gt;async&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;en remplacant &lt;code&gt;asyncio.sleep&lt;/code&gt; par &lt;code&gt;time.sleep&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# synchrone...
def bigWork(i):
    print(&#39;Big work %d starts&#39; % i)
    delay = random.uniform(0, 1.5)
    time.sleep(delay)  # là ça va bloquer
    print(&#39;Big work %d ends after %.2f seconds&#39; % (i, delay))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Impossible d&amp;rsquo;utiliser &amp;ldquo;&lt;code&gt;await&lt;/code&gt;&amp;rdquo; car la fonction n&amp;rsquo;est pas déclarée aysnchrone, et en plus &lt;code&gt;time.sleep&lt;/code&gt; n&amp;rsquo;est pas &lt;code&gt;awaitable&lt;/code&gt; donc elle va bloquer le processus.&lt;/p&gt;
&lt;p&gt;Heureusement, &lt;code&gt;asyncio&lt;/code&gt; va nous permettre de nous en sortir malgré tout. L&amp;rsquo;idée est d&amp;rsquo;utiliser a boucle d&amp;rsquo;évènements et de lui demander de gérer des Thread (oui&amp;hellip; des threads) qu&amp;rsquo;on saura attendre.&lt;/p&gt;
&lt;p&gt;Il y a plusieurs manière de faire, la première est de demander à la boucle d&amp;rsquo;évèenements de prendre en charge un Thread:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# on récupère la boucle
loop = asyncio.get_event_loop()

# None =&amp;gt; on voit ça après, mais disons
# que là, on ne lui donne pas, donc il se débrouille...
loop.run_in_exector(None, function)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;function&lt;/code&gt; correspond à la fonction à ajouter à l&amp;rsquo;exécution, par exemple &lt;code&gt;bigWork&lt;/code&gt;, donc sans argument&amp;hellip; Sauf que voilà, nous avons un argument à donner. Pour se sortir de l&amp;rsquo;impasse, on va utiliser un &lt;code&gt;partial&lt;/code&gt; qui vient du module &lt;code&gt;functools&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# un partial est une fonction
# partielle, on lui fourni les arguments
# en liste...
coroutine = functools.partial(bigWork, i)
# et au final:
loop.run_in_exector(None, coroutine)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;run_in_exector&lt;/code&gt; retourne une task, et on va pouvoir l&amp;rsquo;atendre de la même manière qu&amp;rsquo;avant:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;async def main():
    tasks = []
    loop = asyncio.get_event_loop()
    for i in range(5):
        coroutine = functools.partial(bigWork, i)
        task = loop.run_in_executor(None, coroutine)
        tasks.append(task)

    await asyncio.wait(tasks)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui va nous donner:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python a.py
Big work 0 starts
Big work 1 starts
Big work 2 starts
Big work 3 starts
Big work 4 starts
Big work 1 ends after 0.35 seconds
Big work 4 ends after 0.56 seconds
Big work 2 ends after 0.70 seconds
Big work 0 ends after 1.16 seconds
Big work 3 ends after 1.23 seconds
Total time: 1.23
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Magnifique, ça fonctionne (c&amp;rsquo;était prévu&amp;hellip;)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cela veut dire que si vous avez des fonctions non asynchrones, il est possible d&amp;rsquo;utiliser la boucle d&amp;rsquo;évènements pour exécuter des Threads.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Une autre manière de faire est d&amp;rsquo;utiliser un pool, encore une fois via &lt;code&gt;asyncio&lt;/code&gt; pour utiliser la boucle d&amp;rsquo;évènements. Le package natif &lt;code&gt;concurrent.futures&lt;/code&gt; propose un système de Pool de Thread ou de Process. Voici comment faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;async def main():
    tasks = []
    loop = asyncio.get_event_loop()
    with futures.ThreadPoolExecutor() as pool:
        for i in range(5):
            coroutine = functools.partial(bigWork, i)
            # et cette fois, on lui donne l&#39;exécutor,
            # donc pas None, mais le pool
            t = loop.run_in_executor(pool, coroutine)
            tasks.append(t)

    await asyncio.wait(tasks)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gros intérêt, on peut limiter le nombre de Threads (ou de Process en utilisant &lt;code&gt;ProcessPoolExecutor&lt;/code&gt;) via l&amp;rsquo;argument &lt;code&gt;max_workers&lt;/code&gt;. C&amp;rsquo;est une excellente manière de ne pas surcharger vos CPU avec des miliers de Threads à switcher.&lt;/p&gt;
&lt;h1 id=&#34;on-résume&#34;&gt;On résume&lt;/h1&gt;
&lt;p&gt;Donc, pour faire simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Une fonction asynchrone doit être déclarée avec le mot cle &lt;code&gt;async&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Elle doit attendre un truc, même un &lt;code&gt;await asyncio.sleep(0)&lt;/code&gt; suffit, mais il &lt;strong&gt;faut&lt;/strong&gt; attendre avec &lt;code&gt;await&lt;/code&gt;, sinon ça bloque les autres coroutines&lt;/li&gt;
&lt;li&gt;Vous devez faire passer une fonction à &lt;code&gt;asyncio.run()&lt;/code&gt; pour gérer une boucles d&amp;rsquo;évènements&lt;/li&gt;
&lt;li&gt;Vous pouvez ordonnancer des fonctions non asynchrones, via des Thread ou Process, en utilisant un &lt;code&gt;executor&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notez aussi que:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;asyncio.wait&lt;/code&gt; est annulable, alors que &lt;code&gt;asyncio.gather&lt;/code&gt; vous retourne une liste de résultats des fonctions asynchrones&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asyncio.create_task(coroutine)&lt;/code&gt; envoloppe une coroutine pour avoir une tâche, une tâche démarre au plus tôt, alors qu&amp;rsquo;une coroutine doit être attendu pour démarrer.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Certes, Python demande un peu plus de boulot qu&amp;rsquo;avec Go ou JS pour démarrer une coroutine, surtout avec des fonctions bloquantes (que Go sait tellement bien gérer), mais après quelques heures vous allez avoir beaucoup moins de mal à gérer des coroutines.&lt;/p&gt;
&lt;p&gt;Notez que le module &lt;code&gt;asyncio&lt;/code&gt; propose des équivalents à pas mal de fonctions que vous utlisiez, par exemple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;asyncio.sleep&lt;/code&gt; au lieu de &lt;code&gt;time.sleep&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asyncio.create_subprocess_exec&lt;/code&gt; au lieu de &lt;code&gt;suprocesss.Process&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asyncio.Queue&lt;/code&gt; au lieu de &lt;code&gt;queue.Queue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;et j&amp;rsquo;en passe.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Allez voir la page &lt;a href=&#34;https://docs.python.org/3/library/asyncio.html&#34;&gt;de documentation asyncio&lt;/a&gt; qui est une ressource de connaissances exemplaire, et prenez le temps de bien lire &lt;a href=&#34;https://docs.python.org/3/library/asyncio-api-index.html&#34;&gt;la page d&amp;rsquo;api&lt;/a&gt; qui est assez claire.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;avantage de &lt;code&gt;asyncio&lt;/code&gt; est de limiter l&amp;rsquo;utilisation de &lt;code&gt;Queue&lt;/code&gt;, des context switch CPU, et de gérer des tâches en concurrence alors que le parallélisme est souvent inutile. Vous allez limiter la charge OS en ayant un seul processus tout en ayant une gestion de tâches concurrente qui va accélerer les traitements. À terme, les Threads seront votre solution de recours, et non pas la solution initiale.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée n&amp;rsquo;étant pas de dénigrer les Threads et les sous-processus, mais de les utiliser que dans le cas où ils sont vraiment utiles.&lt;/p&gt;
&lt;p&gt;En espérant que mon article vous ait servit.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;non je n&amp;rsquo;ai pas fait une faute de frappe, c&amp;rsquo;est bien avec un &amp;ldquo;g&amp;rdquo;, les goroutines sont des coroutines spécifiques à Go.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>Une UI cross-plateform, en Go, embarquée, ça existe</title>
      <link>https://www.metal3d.org/blog/2020/une-ui-cross-platform-en-go-embarqu%C3%A9e-%C3%A7a-existe/</link>
      <pubDate>Sun, 28 Jun 2020 21:18:58 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2020/une-ui-cross-platform-en-go-embarqu%C3%A9e-%C3%A7a-existe/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2020/une-ui-cross-platform-en-go-embarqu%C3%A9e-%C3%A7a-existe/fyne-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Ce qui manquait à Golang c&amp;rsquo;était une librairie graphique pour faire des interfaces, mais sans dépendance, et qui marche partout, même en app Mobile - et bien &lt;a href=&#34;https://fyne.io&#34;&gt;Fyne&lt;/a&gt; c&amp;rsquo;est ça !&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai attaqué Go depuis quasiment le début de sa sortie (Merci Nicolas d&amp;rsquo;avoir insisté pour que je jette un oeil), ce langage m&amp;rsquo;a convaincu en quelques heures. J&amp;rsquo;adore ce langage pour sa philosophie, son fonctionnement, son principe clair et qui ne force pas la POO à foison. En Go, tout est logique, on vous pousse à coder et non à chercher comment coder.&lt;/p&gt;
&lt;p&gt;Mais depuis le début, j&amp;rsquo;ai senti un manque, un vrai manque. Vous l&amp;rsquo;avez capté, à cause du titre, c&amp;rsquo;est la possibilité de coder des interfaces graphiques.&lt;/p&gt;
&lt;p&gt;Alors oui, bien entendu, on peut utiiser GTK ou Qt, oui.&lt;/p&gt;
&lt;p&gt;Mais un des principes de Go (qui peut déplaire, je le conçois) c&amp;rsquo;est que &lt;em&gt;vous vous abstrayez des dépandances&lt;/em&gt;. J&amp;rsquo;entends par là que la compilation est (par défaut) statique, et que vous évitez dans 99% des cas d&amp;rsquo;avoir à installer des librairies &lt;em&gt;externes&lt;/em&gt; à Go. Or, par exemple avec GTK, vous devez avoir les librairies Gtk-Dev pour compiler votre application et le client &lt;strong&gt;doit&lt;/strong&gt; avoir la bonne version de GTK pour faire tourner le binaire. On est donc loin du compilé statique natif.&lt;/p&gt;
&lt;p&gt;Autre chose, en GTK, il est difficile d&amp;rsquo;exploiter la puissance des &lt;em&gt;goroutines&lt;/em&gt; - on est obligé de travailler avec des états de la Glib qui permettent de mettre en parallèle des traitements. Bref, ça fonctionne très bien mais ça n&amp;rsquo;est pas &amp;ldquo;Golang ready&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Et puis, pour ajouter une cerise sur le gâteau déjà bien empilé, &lt;strong&gt;j&amp;rsquo;aimerai faire une application qui fonctionne partout, y compris sur du mobile.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Bref, j&amp;rsquo;avais entendu parler de &amp;ldquo;&lt;a href=&#34;https://github.com/golang-ui/nuklear&#34;&gt;nuklear&lt;/a&gt;&amp;rdquo; mais la complexité du code me laissait de marbre.&lt;/p&gt;
&lt;p&gt;Et je suis tombé par hasard sur la pépite, le graal, enfin bref le truc qui répond à mon besoin et potentiellement le votre. Ce petit bijou s&amp;rsquo;appelle &lt;strong&gt;Fyne&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id=&#34;miiiiiissssss-fyne-&#34;&gt;Miiiiiissssss Fyne&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; !&lt;/h1&gt;
&lt;div style=&#34;float: right; width: 33%; margin: 0 0 .5em .5em&#34;&gt;
&lt;img src=&#34;https://media.giphy.com/media/RA6YG8FkxeopO/giphy.gif&#34; alt=&#34;The nany&#34; /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Fyne&lt;/em&gt; est une librairie qui fait partie d&amp;rsquo;un projet plus grand: &lt;em&gt;conquérir le monde&lt;/em&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Enfin pas exactement, hein. En gros il y a deux projets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un projet de gestionnaire de bureau qui se nomme &lt;a href=&#34;https://fyne.io/fynedesk/&#34;&gt;fynedesk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;La librairie graphique qui permet de créer des applications (avec tout plein de widgets) qu&amp;rsquo;on appelle &amp;ldquo;Fyne&amp;rdquo;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.fyne.io/index.html&#34;&gt;La page de présentation est ici&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/fyne-io/fyne&#34;&gt;le projet Github est ici&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le premier point, on ne va pas en parler ici - par contre la librairie graphique, elle, c&amp;rsquo;est du lourd.&lt;/p&gt;
&lt;p&gt;Ils sont partis de zéro, pour que la définition des objets graphiques puissent répondre aux différents &lt;em&gt;drivers&lt;/em&gt; que l&amp;rsquo;on peut utiliser. Ces &lt;em&gt;drivers&lt;/em&gt; permettent d&amp;rsquo;afficher les objets &lt;strong&gt;de la même manière que vous soyez sur Linux, Windows, Mac, iOS ou Android.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vous avez bien lu, on parle de Desktop et de Mobile. Vous allez voir, il suffit de compiler l&amp;rsquo;application pour la bonne cible et sans trop s&amp;rsquo;arracher la perruque.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Votre application contiendra, si vous le souhaitez, un gestionnaire de préférences, encore une fois sans vous soucier de l&amp;rsquo;emplacement des données puisque cela est toujours différent selon l&amp;rsquo;OS que vous ciblez.&lt;/p&gt;
&lt;p&gt;Et le petit truc qui donne encore plus de saveur à ce projet, c&amp;rsquo;est que les développeurs ont inclu ce qu&amp;rsquo;il faut pour créer votre application en TDD&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; et ce sans avoir à utiliser un serveur graphique. Vous trouverez une bon didacticiel dans cette vidéo:&lt;/p&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/L-ePkdSoP-0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;

&lt;p&gt;Je ne vais pas en parler dans cet article, par contre je vous conseille de l&amp;rsquo;utiliser dans vos développements, parce que lorsque votre application commence à prendre un peu de poids, tout devient plus simple à corriger. Et le TDD est à mon avis l&amp;rsquo;une des méthodologies les plus intéressantes dans notre métier - on code un test qui décrit ce qu&amp;rsquo;on veut faire, et on fait en sorte que le test passe en codant la fonctionnalité.&lt;/p&gt;
&lt;p&gt;Bon bref, voyons comment ça fonctionne.&lt;/p&gt;
&lt;h1 id=&#34;premier-test-à-larrache&#34;&gt;Premier test, à l&amp;rsquo;arrache&lt;/h1&gt;
&lt;p&gt;On va se créer un petit projet, dans &lt;code&gt;/tmp/test-fyne&lt;/code&gt; et utiliser aussi le gestionnaire de dépendances intégré à Go (il était temps qu&amp;rsquo;il y en ait):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# on crée le répertoire de demo
mkdir -p /tmp/test-fyne
cd /tmp/test-fyne
# on initialise notre gestion de dépendances
go mod init demo
# on installe Fyne
go get fyne.io/fyne
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et on commence par développer notre première application graphique, simplement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;package main

import (
    &amp;quot;fyne.io/fyne&amp;quot;
	&amp;quot;fyne.io/fyne/app&amp;quot;
)

func main(){
    // l&#39;objet d&#39;application
    application := app.New()

    // on se crée une fenêtre
    win := application.NewWindow(&amp;quot;Hello&amp;quot;)
    // comme on a pas de contenu... je vous conseille de mettre ça
    win.Resize(fyne.NewSize(400, 300)) 

    // on lance l&#39;app
    // cette fonction est un alias à win.Show() et application.Run()
    win.ShowAndRun()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors, ne me dites pas que c&amp;rsquo;est compliqué. Testez l&amp;rsquo;application en lançant &lt;code&gt;go run main.go&lt;/code&gt; et vous allez voir:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://www.metal3d.org//blog/2020/une-ui-cross-platform-en-go-embarqu%C3%A9e-%C3%A7a-existe/basic-win.webp&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;Notre première application&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;blockquote&gt;
&lt;p&gt;Bon, mettons nous tout de suite d&amp;rsquo;accord: le design est léger, pas forcément joli, voir &amp;ldquo;old-school&amp;rdquo;. Mais l&amp;rsquo;idée n&amp;rsquo;est pas de remplacer GTK, Qt, ou tout autre librairie graphique adaptée à votre environnement. Là, on parle de faire une application rapidement, cross-platform, et facile à déployer &amp;ldquo;partout&amp;rdquo;. Donc, on va s&amp;rsquo;y faire (et rien ne dit que les thèmes ne soient pas, plus tard, encore plus jolis).&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Maintenant, on va ajouter des trucs dans cette fenêtre, un label (un texte&amp;hellip;) et un bouton qui a pour action de fermer l&amp;rsquo;application - notez que j&amp;rsquo;ai supprimé le &lt;code&gt;Resize&lt;/code&gt; et ajouté l&amp;rsquo;utilisation d&amp;rsquo;une boite verticale (&lt;em&gt;VBox&lt;/em&gt;). Aussi, il faut ajouter &lt;code&gt;fyne.io/fyne/widget&lt;/code&gt; dans les imports pour avoir accès à ces gadgets graphiques (c&amp;rsquo;est à peu près la traducition du mot &lt;em&gt;widget&lt;/em&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;package main

import (
	&amp;quot;fyne.io/fyne/app&amp;quot;
	&amp;quot;fyne.io/fyne/widget&amp;quot;
)

func main() {
	application := app.New()

	win := application.NewWindow(&amp;quot;Hello&amp;quot;)

	label := widget.NewLabel(&amp;quot;Et un bouton, un&amp;quot;)
	button := widget.NewButton(&amp;quot;Click me&amp;quot;, func() {
		application.Quit()
	})

	// une boite verticale pour les gouverner
	// et la fenêtre les lier
	box := widget.NewVBox(label, button)

	win.SetContent(box)
	win.ShowAndRun()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là, simplement:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://www.metal3d.org//blog/2020/une-ui-cross-platform-en-go-embarqu%C3%A9e-%C3%A7a-existe/button-label.webp&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;Un bouton, un label, dans une boîte&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Il y a à peu près tout ce dont on a besoin. Le package &amp;ldquo;widget&amp;rdquo; contient des boutons, labels, entrées de texte, etc. Dans &amp;ldquo;canvas&amp;rdquo; vous trouverez des conteneurs d&amp;rsquo;image, de dessins - mais vous avez aussi un package pour des &lt;strong&gt;boites de dialogue, de notifications, de thèmes&lt;/strong&gt;&amp;hellip; Donc à peu près tout ce dont on a besoin pour faire une application graphique complète&lt;/p&gt;
&lt;p&gt;Bref, il faut fouiller un peu dans la documentation&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt; mais c&amp;rsquo;est vraiment pas très complexe.&lt;/p&gt;
&lt;p&gt;Bon, vous avez aussi certainement remarqué l&amp;rsquo;abscence d&amp;rsquo;icône dans votre barre des tâches. Alors l&amp;rsquo;idée c&amp;rsquo;est d&amp;rsquo;éviter à tout prix d&amp;rsquo;avoir un fichier externe. On va donc installer la commande &amp;ldquo;fyne&amp;rdquo; pour transformer une image en resource (en code):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# installons la commande fyne
go get fyne.io/fyne/cmd/fyne

# allez chercher une icone, et créons la ressource en Go
fyne bundle face-monkey.png &amp;gt; icon.go
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez regarder dans le fichier &lt;code&gt;icon.go&lt;/code&gt; qui contient une variable nommée &lt;code&gt;resourceFaceMonkeyPng&lt;/code&gt;, elle correspond à votre icône.&lt;/p&gt;
&lt;p&gt;Il faut ajouter une petite ligne de code pour gérer ça:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;// après la création de l&#39;application:
application.SetIcon(resourceFaceMonkeyPng)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans ma barre des tâches, c&amp;rsquo;est bon j&amp;rsquo;ai l&amp;rsquo;icône:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://www.metal3d.org//blog/2020/une-ui-cross-platform-en-go-embarqu%C3%A9e-%C3%A7a-existe/with-icon.webp&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;L&amp;#39;icône dans la barre de taches, c&amp;#39;est OK&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Et donc, le binaire &lt;strong&gt;contient&lt;/strong&gt; l&amp;rsquo;icône, inutile de gérer des chemins etc&amp;hellip; Effectivement c&amp;rsquo;est un peu réberbatif de devoir convertir vos icônes, mais en soit c&amp;rsquo;est une source d&amp;rsquo;énervement en moins pour livrer votre programme à quelqu&amp;rsquo;un.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors oui, coupons court à toutes discussions, le binaire de sortie est très lourd !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Le binaire va contenir les ressources &lt;strong&gt;et&lt;/strong&gt; la librairie graphique, on table donc à minima sur du 17Mo pour une fenêtre. Mais après, clairement, quand l&amp;rsquo;interface sera plus fournie avec des évènements à gérer etc., le binaire ne va pas tant grossir. Et puis il faut faire un choix entre se battre avec des dépendances (et y arriver, je ne dis pas le contraire hein) ou faire un soft qui n&amp;rsquo;impose finalement qu&amp;rsquo;une contrainte de poids. À vous de voir.&lt;/p&gt;
&lt;h1 id=&#34;une-petite-subtilité-le-redimensionnement&#34;&gt;Une petite subtilité: le redimensionnement&lt;/h1&gt;
&lt;p&gt;Rien n&amp;rsquo;est jamais parfait, et je me suis très vitre confronté à un petit souci qui m&amp;rsquo;a pris un peu de temps à résoudre. Heureusement, les auteurs sont à l&amp;rsquo;écoute et &lt;a href=&#34;https://github.com/fyne-io/fyne/issues/1146&#34;&gt;m&amp;rsquo;ont répondu sur GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Étant donné le travail fourni pour que les applications passent aussi bien sur Mac que Linux ou Windows, et sur les mobiles, la gestion du placement et des espaces d&amp;rsquo;éléments graphiques est parfois un peu complexe à comprendre. Prenons un exemple simple. Je veux afficher:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un label&lt;/li&gt;
&lt;li&gt;un texte plus gros &lt;em&gt;scrollable&lt;/em&gt; (par exemple une information de licence)&lt;/li&gt;
&lt;li&gt;un bouton pour passer à la suite&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le tout dans une boite verticale. Naturellement je vais faire ceci&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;label := widget.NewLabel(&amp;quot;Information de license&amp;quot;)

// on fait un gros texte qu&#39;on place dans un conteneur
// qui permet le défilement
license := widget.NewLabel(licenseText)
scrollable := widget.NewVScrollContainer(license)

button := widget.NewButton(&amp;quot;Click me&amp;quot;, func() {
	application.Quit()
})

// on place tout à la verticale
box := widget.NewVBox(label, scrollable, button)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sauf que voilà&amp;hellip; tout l&amp;rsquo;espace de la fenêtre n&amp;rsquo;est pas utilisé.&lt;/p&gt;
&lt;p&gt;Tentez le coup, vous allez voir. La fenêtre se cale à la taille &amp;ldquo;définie&amp;rdquo; et si vous redimensionnez la fenêtre, tous les éléments restent en haut de la fenêtre. Or, on veut que notre texte s&amp;rsquo;étire sur la hauteur.&lt;/p&gt;
&lt;p&gt;Quand on vient du monde GTK ou Qt, on se dit qu&amp;rsquo;il suffit de donner une indication à tel ou tel widget pour qu&amp;rsquo;il s&amp;rsquo;étire. Mais ce n&amp;rsquo;est pas le cas avec Fyne. Il va falloir connaitre un peu ce que sont les &lt;em&gt;Layouts&lt;/em&gt; et jouer avec.&lt;/p&gt;
&lt;p&gt;Avec un peu de lecture de doc, on comprend très vite comment faire. La solution ici est d&amp;rsquo;utiliser un &lt;em&gt;Layout&lt;/em&gt; nommé &lt;code&gt;BorderLayout&lt;/code&gt; qui se trouve dans le package à inclure &lt;code&gt;fyne.io/fyne/layout&lt;/code&gt;. Et ce &lt;em&gt;Layout&lt;/em&gt; doit servir à dimensionner un &amp;ldquo;conteneur&amp;rdquo; avec &lt;code&gt;fyne.NewContainerWithLayout()&lt;/code&gt; (package &lt;code&gt;fyne.io/fyne&lt;/code&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En soit, les VBox et HBox, par exemple, sont des &lt;strong&gt;Layout prédéfinis&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On peut d&amp;rsquo;ailleurs coder notre propres Layout.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ce que fait ce &lt;em&gt;BorderLayout&lt;/em&gt;, c&amp;rsquo;est de placer les éléments en &amp;ldquo;haut&amp;rdquo;, &amp;ldquo;bas&amp;rdquo;, &amp;ldquo;gauche&amp;rdquo; et &amp;ldquo;droite&amp;rdquo; de la fenêtre. Mais on peut omettre des éléments.&lt;/p&gt;
&lt;p&gt;Ce qu&amp;rsquo;on va faire est:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;poser notre label en &amp;ldquo;haut&amp;rdquo; (top)&lt;/li&gt;
&lt;li&gt;poser le bouton en &amp;ldquo;bas&amp;rdquo; (bottom)&lt;/li&gt;
&lt;li&gt;rien à doite et à gauche&lt;/li&gt;
&lt;li&gt;et placer notre &amp;ldquo;scrolable&amp;rdquo; entre le haut et le bas&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Si vous avez plusieurs éléments à mettre en haut et bas, alors vous devez les faire entrer dans des &lt;em&gt;box&lt;/em&gt; (Vbox, Hbox ou autres conteneurs) et ce seront ces &amp;ldquo;box&amp;rdquo; qui devront être placées en &amp;ldquo;top&amp;rdquo; et &amp;ldquo;bottom&amp;rdquo;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;label := widget.NewLabel(&amp;quot;Information de license&amp;quot;)
license := widget.NewLabel(licenseText)
scrollable := widget.NewVScrollContainer(license)
button := widget.NewButton(&amp;quot;Click me&amp;quot;, func() {
	application.Quit()
})

// on definit un &amp;quot;conteneur&amp;quot; et on lui donne les éléments qui servent à calculer
// l&#39;espaement et le placement - ici, on veut le label en haut et le bouton en bas
// mais rien à droite et à gauche
container := layout.NewBorderLayout(label, button, nil, nil)
// et notre boite utilisera ce conteneur, on a plus qu&#39;à lui donner la
// liste des éléments (l&#39;ordre n&#39;importe pas ici)
box := fyne.NewContainerWithLayout(container, label, scrollable, button)

win.SetContent(box)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fyne va alors utiliser le layout pour placer les éléments que vous lui avez donné, et au bon endroit, il place le scrollable là où il peut (au milieu) et le redimensionnement de la fenêtre va alors étirer l&amp;rsquo;élément.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://www.metal3d.org//blog/2020/une-ui-cross-platform-en-go-embarqu%C3%A9e-%C3%A7a-existe/border-layout.webp&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;La fenêtre peut être redimensionnée&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Sans cela, vous devriez donner une taille fixe ou vous contenter d&amp;rsquo;une application qui ne redimensionne pas le contenu. C&amp;rsquo;est donc un point à savoir, et il faut vraiment prendre en compte ce comportement.&lt;/p&gt;
&lt;h1 id=&#34;compiler--packager-votre-application-desktop-mobile&#34;&gt;Compiler / packager votre application (desktop, mobile)&lt;/h1&gt;
&lt;p&gt;Vous pouver proposer le binaire tel quel, mais votre &amp;ldquo;client&amp;rdquo; va devoir lancer la commande dans un terminal. Et un terminal, &lt;em&gt;ça fait peur&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;En général, sous Linux, on doit proposer un fichier &amp;ldquo;.desktop&amp;rdquo; qui décrit l&amp;rsquo;application, son icône de lancement, son emplaement&amp;hellip; Sous Mac il faut packager un &amp;ldquo;.dmg&amp;rdquo; et sous windows il faut embarquer l&amp;rsquo;image dans le binaire en &amp;ldquo;.exe&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et quand il faut fournir une application mobile, par exemple pour Android, là c&amp;rsquo;est un package &lt;code&gt;.apk&lt;/code&gt; qu&amp;rsquo;il faut construire.&lt;/p&gt;
&lt;p&gt;Du boulot donc et pas des moindres.&lt;/p&gt;
&lt;p&gt;Du coup, une nouvelle fois, les auteurs de Fyne ont fait un excellent boulot - la commande &lt;code&gt;fyne&lt;/code&gt; propose une sous-commande nommée &amp;ldquo;package&amp;rdquo;. On sent que les auteurs de la librairie ont eut les mêmes problèmes que la plupart d&amp;rsquo;entre nous pour faire des applications.&lt;/p&gt;
&lt;p&gt;Les commandes sont les suivantes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;fyne package -os linux -icon myapp.png
fyne package -os darwin -icon myapp.png
fyne package -os windows -icon myapp.png
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Pour Linux, un &lt;em&gt;tarball&lt;/em&gt; est généré, contenant le fichier &amp;ldquo;.desktop&amp;rdquo; et une structure de répertoire à installer sur la machine cible.&lt;/li&gt;
&lt;li&gt;Pour Mac (darwin), c&amp;rsquo;est un dmg tout ce qu&amp;rsquo;il y a de plus commun, qui est généré.&lt;/li&gt;
&lt;li&gt;Et pour Windows, un executable avec l&amp;rsquo;icône embarquée.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Encore une fois, Fyne soulage vraiment les procédures en automatisant les tâches qui fâchent.&lt;/p&gt;
&lt;p&gt;Et pour finir, vous pouvez générer les applications mobiles:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;fyne package -os android -appID com.example.ma-demo -icon mobileIcon.png
fyne package -os ios - appID com.example.ma-demo -icon mobileIcon.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il vous faudra bien entendu choisir un &lt;em&gt;appID&lt;/em&gt; unique, et avoir le nécessaire pour compiler vers la plateforme cible (&lt;code&gt;adb&lt;/code&gt; pour Android, &lt;code&gt;Xcode&lt;/code&gt; pour iOS) - mais encore une fois, c&amp;rsquo;est clair, net et ça soulage les nerfs.&lt;/p&gt;
&lt;p&gt;Allez &lt;a href=&#34;https://developer.fyne.io/started/distribution&#34;&gt;voir la page de documentation qui explique toute la procédure&lt;/a&gt;. Vous aurez toutes les informations nécessaires.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai testé sur Android, ça fonctionne vraiment bien.&lt;/p&gt;
&lt;h1 id=&#34;le-mot-de-la-fin&#34;&gt;Le mot de la fin&lt;/h1&gt;
&lt;p&gt;Je suis vraiment impressionné par le boulot qui a été fourni, que ce soit en tant que compatibilité cross-platform, mais aussi en terme de simplicité et d&amp;rsquo;outillage. Fyne me permet de créer des applications clientes rapidement et sans que j&amp;rsquo;ai à me soucier de savoir si les machines cibles ont ou non les librairies graphiques. Cette notion &amp;ldquo;d&amp;rsquo;embarquer&amp;rdquo; les ressources (graphique et librairies dans un binaire) est exactement ce dont j&amp;rsquo;avais besoin.&lt;/p&gt;
&lt;p&gt;Fyne est en cours d&amp;rsquo;écriture pour une nouvelle mouture, encore plus poussée, qui va proposer du &lt;strong&gt;data-binding et des animations&lt;/strong&gt;. J&amp;rsquo;ai hâte de voir ça &lt;span class=&#34;emoji&#34;&gt;🤯&lt;/span&gt; - pour l&amp;rsquo;heure, je vous laisse jeter un oeil sur cette petite perle.&lt;/p&gt;
&lt;p&gt;N&amp;rsquo;hésitez pas à partager vos remarques, vos tests et un grand merci à vous pour la lecture &lt;span class=&#34;emoji&#34;&gt;😘&lt;/span&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Avouez que vous connaissez la référence &lt;span class=&#34;emoji&#34;&gt;😁&lt;/span&gt; &lt;a href=&#34;https://fr.wikipedia.org/wiki/Une_nounou_d%27enfer&#34;&gt;Une nounou d&amp;rsquo;enfer&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Test Driven Development, ou développement dirigé par des tests&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;C&amp;rsquo;est le lot de notre métier, il faut lire la documentation&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>Passer De Pip À Pipenv</title>
      <link>https://www.metal3d.org/blog/2020/passer-de-pip-%C3%A0-pipenv/</link>
      <pubDate>Sat, 20 Jun 2020 10:04:24 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2020/passer-de-pip-%C3%A0-pipenv/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2020/passer-de-pip-%C3%A0-pipenv/pipenv-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;La commande &amp;ldquo;pip&amp;rdquo; couplée à des virtualenv, c&amp;rsquo;est un peu le b.a.-ba en Python. Mais &amp;ldquo;pipenv&amp;rdquo; va vous rendre la vie encore plus facile.&lt;/p&gt;
&lt;p&gt;Cela fait des années que je code en Python, depuis environ 2003 pour être exact. Je suis passé par pas mal de techniques pour éviter d&amp;rsquo;écrire mes dépendances dans le système, et il faut bien avouer que les &lt;em&gt;Virtualenv&lt;/em&gt; ont beaucoup aidé l&amp;rsquo;adoption de Python. Ça, et l&amp;rsquo;avènement du &lt;em&gt;Machine Learning&lt;/em&gt; dans lequel Python fait de plus en plus d&amp;rsquo;adpetes. Avant toute chose, je vais insister sur un truc qui m&amp;rsquo;énerve très souvent&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Arrêtez de faire des &amp;ldquo;&lt;code&gt;sudo pip install truc&lt;/code&gt;&amp;rdquo; !!!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Cette commande installe les paquets (packages et modules) &lt;strong&gt;sur votre système&lt;/strong&gt;, donc :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;si un paquet de votre distribution (deb, rpm, etc&amp;hellip;), contient cette dépendance, il va vous l&amp;rsquo;exploser&lt;/li&gt;
&lt;li&gt;si vous avez deux projets / applications Python qui ont la même dépendance, mais pas avec la même version, vous allez en avoir une des deux qui va se vautrer&lt;/li&gt;
&lt;li&gt;et puis mince à la fin, c&amp;rsquo;est quoi cette idée de faire des installations dans le système ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Alors oui, on peut ajouter l&amp;rsquo;option &lt;code&gt;--user&lt;/code&gt; qui va installer les packages et binaires dans votre HOME, c&amp;rsquo;est bien&amp;hellip; Mais cela doit rester anecdotique, pour des outils dont le paquet n&amp;rsquo;existe pas sur votre distribution, ou si vous savez ce que vous faites. Dans tous les cas, pour développer en Python, on passe par un environnement virtuel.&lt;/p&gt;
&lt;p&gt;Je vais donc vous faire un rappel, puis on parlera de Pipenv&lt;/p&gt;
&lt;h1 id=&#34;avant-on-faisait-des-virtualenv-et-on-utilisait-pip&#34;&gt;Avant, on faisait des virtualenv et on utilisait &amp;ldquo;pip&amp;rdquo;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Avant de parler de Pipenv, je pense qu&amp;rsquo;il est important de voir comment la plupart des projets sont démarrés en Python avec &lt;code&gt;pip&lt;/code&gt; et les &lt;code&gt;virtualenv&lt;/code&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;La méthode super standard aujourd&amp;rsquo;hui, qui marche très bien, c&amp;rsquo;est de créer un environnement virtuel.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;mkdir -p ~/Projects/monsite1
cd ~/Projects/monsite1

python -m venv virtualenv

# et on doit activer cet environnement avant de bosser
# ... à chaque fois...
source vitrualenv/bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;À partir de là, toutes les commandes &lt;code&gt;pip install&lt;/code&gt; vont installer les packages et dépendances dans le répertoire &lt;code&gt;virtualenv&lt;/code&gt; (vous pouvez le nommer autrement si ça vous chante) - c&amp;rsquo;est pratique, et quand on a l&amp;rsquo;habitude c&amp;rsquo;est quasiment un reflex.&lt;/p&gt;
&lt;p&gt;Intéressant aussi, on peut taper cette commande pour générer un fichier de dépendances pour que vos collaborateurs puissent avoir la même version que vous :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;pip freeze &amp;gt; requirements.txt

# vos collaborateurs vont ensuite faire:
pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est cool, mais ça a quelques inconvénients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;d&amp;rsquo;abord, il faut penser à créer le virtualenv sur les postes de vos collègues de projet&lt;/li&gt;
&lt;li&gt;ensuite, le &amp;ldquo;freeze&amp;rdquo; colle les paquets + dépendances&lt;/li&gt;
&lt;li&gt;et en plus de cela, si vous avez des packages / outils seulement pour le développement, et bien ils sont dans ce fichier&amp;hellip;&lt;/li&gt;
&lt;li&gt;et puis si on travaille avec git, il faut penser à &amp;ldquo;ignorer&amp;rdquo; le virtualenv (ou le placer en dehors du projet)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, oui, ça marche bien, mais ça demande un peu de manipulation pour séparer ces fameuses dépendances de &amp;ldquo;dev&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;À côté de cela, on voit Node utiliser un &amp;ldquo;package.json&amp;rdquo; via &lt;code&gt;npm&lt;/code&gt;, qui sait d&amp;rsquo;ailleurs très bien séparer les dépendances de dev et de run - ou encore &amp;ldquo;composer&amp;rdquo; pour PHP qui fait à peu près bien ce travail&amp;hellip;&lt;/p&gt;
&lt;p&gt;Sous Python, on se sentait un peu démunis, en dessous du lot, un peu peiné d&amp;rsquo;avoir un langage tellement cool mais avec une gestion de package limitée&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et puis arrive le prince Pipenv !&lt;/p&gt;
&lt;h1 id=&#34;pipenv-mon-ami-mon-frère&#34;&gt;Pipenv, mon ami mon frère&lt;/h1&gt;
&lt;p&gt;Depuis quelques mois, je teste &amp;ldquo;pipenv&amp;rdquo; - et je peux vous dire que &lt;strong&gt;j&amp;rsquo;ai du mal à comprendre pourquoi si peu de Pythonistes ne l&amp;rsquo;utilisent&lt;/strong&gt;. Et surtout, pourquoi il n&amp;rsquo;est pas plus connu !? Car là, on a un outil propre, qui fait exactement ce dont on a toujours besoin en dev Python, qui reprend les concepts utiles de &amp;ldquo;npm&amp;rdquo;, &amp;ldquo;glide&amp;rdquo;, ou &amp;ldquo;composer&amp;rdquo;&amp;hellip; Bref, un truc qui sert professionnellement !&lt;/p&gt;
&lt;p&gt;Cette commande est dans la plupart des dépôts de distribution, donc &lt;code&gt;dnf install pipenv&lt;/code&gt;, ou &lt;code&gt;apt install pipenv&lt;/code&gt; fonctionnera. Dans &amp;ldquo;alpine&amp;rdquo;, pas le choix, on passera par &amp;ldquo;pip&amp;rdquo; pour l&amp;rsquo;installer avec &lt;code&gt;pip install pipenv&lt;/code&gt; (c&amp;rsquo;est logique d&amp;rsquo;utiliser pip ici). OSX n&amp;rsquo;est pas en reste&amp;hellip; Avec &lt;code&gt;brew&lt;/code&gt; ça passe aussi.&lt;/p&gt;
&lt;p&gt;Pipenv ne va pas vous éloigner de &amp;ldquo;pip&amp;rdquo;, en fait il est même simplement un genre de &amp;ldquo;superset&amp;rdquo; de cet outil. Vous allez voir, c&amp;rsquo;est vraiment le panard.&lt;/p&gt;
&lt;p&gt;Créons un projet de site:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;mkdir -p ~/Projects/monsite1
cd ~/Projects/monsite1

pipenv install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et c&amp;rsquo;est tout.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Voilà&amp;hellip; Croyez-le ou non, c&amp;rsquo;est déjà prêt. Il vient de faire le nécessaire;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;création d&amp;rsquo;un virtualenv&lt;/li&gt;
&lt;li&gt;préparation d&amp;rsquo;install de dépendances&lt;/li&gt;
&lt;/ul&gt;&lt;/blockquote&gt;
&lt;p&gt;Vous allez trouver deux fichiers, &lt;code&gt;Pipfile&lt;/code&gt; et &lt;code&gt;Pipfile.lock&lt;/code&gt;, l&amp;rsquo;un contient les dépendances, et le second va contenir les informations de version de tout ce que vous allez installer.&lt;/p&gt;
&lt;p&gt;Déjà, il faut bien comprendre que le fichier &amp;ldquo;Pipfile&amp;rdquo; remplace (en quelque sorte) le fichier &amp;ldquo;requirements.txt&amp;rdquo;. Donc, quand un collaborateur va récupérer votre dépôt, il fera aussi un appel à la commande &amp;ldquo;&lt;code&gt;pipenv install&lt;/code&gt;&amp;rdquo;, et cela installera les dépendances de projet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On est clairment dans la même situation qu&amp;rsquo;avec &lt;code&gt;package.json&lt;/code&gt; pour NPM, ou &lt;code&gt;composer.json&lt;/code&gt; pour composer&amp;hellip; Mais en Python !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Allons y, installons &amp;ldquo;Flask&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;pipenv install flask
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Quoi ? Comme ça ? Sans activer de virtualenv ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et oui mes amis, le virtualenv a été créé par l&amp;rsquo;outil et il l&amp;rsquo;utilise tout seul. Mais il est où ce virtualenv ?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ pipenv --venv
/home/metal3d/.local/share/virtualenvs/monsite2-EXci-bmn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il le trouve comme un grand.&lt;/p&gt;
&lt;p&gt;Pour que cet environment soit actif vous avez plusieurs solutions. La plus simple est d&amp;rsquo;utiliser la commande &lt;code&gt;pipenv run [command]&lt;/code&gt; avec, bien entendu, la commande que vous voulez. Par exemple, pour lancer flask:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;pipenv run flask run -h 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sinon, vous pouvez lancer un shell:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;pipenv shell
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette commande active le virtualenv. Alors &lt;strong&gt;important !!!&lt;/strong&gt; évitez d&amp;rsquo;utiliser la commande &lt;code&gt;deactivate&lt;/code&gt; et pressez plutôt CTRL+D pour désactiver le shell. Si vous vous plantez, c&amp;rsquo;est pas très grave : redémarrez votre terminal si vous avez des soucis.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mais c&amp;rsquo;est pas fini !&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Installons un outils de test unitaire, par exemple&amp;hellip;&amp;ldquo;pytest&amp;rdquo;. On est d&amp;rsquo;accord que cet outil ne doit être installé que pour le développement ? En production, on en veut pas. Et bien, tout comme avec &amp;ldquo;npm&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;pipenv install pytest --dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà, une dépendance installée pour les développeurs. Ce qui veut dire que :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# n&#39;installe que les packages de run
pipenv install 

# install SANS METTRE À JOUR le fichier .lock
# (pour installer en prod)
pipenv sync

# pour avoir les outils de dev
pipenv install --dev

# et si on veut tout, en une fois :
pipenv install --all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et attendez&amp;hellip; C&amp;rsquo;est pas fini&amp;hellip;&lt;/p&gt;
&lt;p&gt;Vous voulez générer les fichiers de requirements, parce que votre environment de déploiement gère &amp;ldquo;pip&amp;rdquo;. Et bien, pas de souci, &amp;ldquo;pipenv&amp;rdquo; sait le créer en évitant les paquets de développements :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;pipenv lock -r &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et si vous voulez générer des requirements séparés :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;pipenv lock -r --dev &amp;gt; requirements-all.txt
pipenv lock -r --dev-only &amp;gt; requirements-dev.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le site propose une jolie animation:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://gist.githubusercontent.com/jlusk/855d611bbcfa2b159839db73d07f6ce9/raw/7f5743401809f7e630ee8ff458faa980e19924a0/pipenv.gif&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;Exemple de séquence Pipenv - source https://pipenv.pypa.io/en/latest/&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h1 id=&#34;les-petits-trucs-en-plus&#34;&gt;Les petits trucs en plus&lt;/h1&gt;
&lt;p&gt;Un truc que j&amp;rsquo;apprécie énormément, c&amp;rsquo;est le fait de pouvoir utiliser un fichier &lt;code&gt;.env&lt;/code&gt;. Très pratique avec Flask pour mettre &amp;ldquo;FLASK_DEBUG=1&amp;rdquo; par exemple. Et bien entendu, ces variables sont chargées en tant que variable d&amp;rsquo;environnement (le nom aurait dû vous orienter) :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ echo &amp;quot;FOO=bar&amp;quot; &amp;gt; .env
$ pipenv run python -c &amp;quot;import os; print(os.environ[&#39;FOO&#39;])&amp;quot;
Loading .env environment variables…
bar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si vous voulez avoir l&amp;rsquo;arbre de dépendance, c&amp;rsquo;est aussi très pratique à lire :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ pipenv graph
MarkupSafe==1.1.1
pytest==5.4.3
  - attrs [required: &amp;gt;=17.4.0, installed: ?]
  - more-itertools [required: &amp;gt;=4.0.0, installed: 8.4.0]
  - packaging [required: Any, installed: 20.1]
  - pluggy [required: &amp;gt;=0.12,&amp;lt;1.0, installed: 0.13.1]
  - py [required: &amp;gt;=1.5.0, installed: 1.8.2]
  - wcwidth [required: Any, installed: 0.2.3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour parfaire le tout, pipenv propose de la completion, dans votre &lt;code&gt;~/.bashrc&lt;/code&gt;, ajoutez :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;source &amp;lt;(pipenv --completion)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et le tour est joué.&lt;/p&gt;
&lt;p&gt;Et il y a bien d&amp;rsquo;autres options, comme &amp;ldquo;check&amp;rdquo; qui vérifie les requirements &amp;ldquo;PEP 508&amp;rdquo;. La &lt;a href=&#34;https://pipenv.pypa.io/en/latest/&#34;&gt;documentation&lt;/a&gt; est simple mais efficace. La page &lt;a href=&#34;https://pipenv.pypa.io/en/latest/advanced&#34;&gt;PIpenv Advance Usage&lt;/a&gt; est une mine d&amp;rsquo;informations.&lt;/p&gt;
&lt;p&gt;Et vous pouvez même &lt;a href=&#34;https://rootnroll.com/d/pipenv/&#34;&gt;tester en ligne&lt;/a&gt; l&amp;rsquo;outil sans rien installer chez vous &lt;span class=&#34;emoji&#34;&gt;🎉&lt;/span&gt;&lt;/p&gt;
&lt;h1 id=&#34;en-bref&#34;&gt;En bref&lt;/h1&gt;
&lt;p&gt;En gros, je ne me sers plus vraiment de &amp;ldquo;pip&amp;rdquo;. Pipenv a pris le relais. Mon process est clairement plus souple. J&amp;rsquo;ouvre vim avec la commande &lt;code&gt;pipenv run vim&lt;/code&gt; pour avoir le contexte de virtualenv dans l&amp;rsquo;éditeur.&lt;/p&gt;
&lt;p&gt;Notez que VSCode, &lt;a href=&#34;https://vscodium.com/&#34;&gt;vscodium&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, Pycharms, etc&amp;hellip; savent utiliser pipenv.&lt;/p&gt;
&lt;p&gt;Je génère tout de même les fichiers de requirements pour installations plus anciennes, et/ou pour le déploiement (car &amp;ldquo;pip&amp;rdquo; est plus répandu sur les plateformes de déploiement), mais je les génère plus proprement avec &amp;ldquo;pipenv&amp;rdquo; qui évite les packages de dev.&lt;/p&gt;
&lt;p&gt;Pipenv ne change pas radicalement votre workflow, il vous évite simplement certains init, des commandes superflues, et une gestion de dépendances manuelle. En soit, les deux seules commandes utiles pour démarrer sont :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pipenv install ... [--dev]&lt;/code&gt; pour créer, installer le projet, en séparant ou non les paquets de dev&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pipenv run &amp;lt;command&amp;gt;&lt;/code&gt; pour lancer une commande avec l&amp;rsquo;environnement virtuel activé&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eventuellement, ponctuellement, &lt;code&gt;pipenv uninstall&lt;/code&gt;, &lt;code&gt;pipenv lock -r&lt;/code&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;En gros, pipenv est un bel équivalent de &amp;ldquo;npm&amp;rdquo; mais pour Python. Je vous conseille vraiment de tester. N&amp;rsquo;hésitez pas à commenter, partager l&amp;rsquo;article et discuter avec moi de ces sujets &lt;span class=&#34;emoji&#34;&gt;👍&lt;/span&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;vscodium est un build de VSCode qui n&amp;rsquo;envoit pas de données à Microsoft&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>Vue et React avec Typescript pour s&#39;approcher de Angular</title>
      <link>https://www.metal3d.org/blog/2020/vue-react-angular-typescript/</link>
      <pubDate>Sun, 14 Jun 2020 09:04:07 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2020/vue-react-angular-typescript/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2020/vue-react-angular-typescript/ts-angular-reat-vue-cover.svg&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Voici comment j&amp;rsquo;ai retrouvé un de l&amp;rsquo;intérêt à React, un vrai plaisirs de coder avec Vue - mais aussi pourquoi je continue à militer pour Angular.&lt;/p&gt;
&lt;p&gt;Depuis que ES6 (javascript moderne), développer en JS est devenu encore plus adapté aux projets d&amp;rsquo;envergure. Aujourd&amp;rsquo;hui, nos navigateurs sont de véritable socles à applications, un site devient une application à part entière qui est interactive et permet de tourner coté client. Depuis des années, les frameworks se sont succédé pour finalement voir au moins 3 frameworks se dégager:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Angular&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; qui &lt;strong&gt;n&amp;rsquo;est plus un framework JS comme l&amp;rsquo;était AngularJS&lt;/strong&gt;, proposé par Google&lt;/li&gt;
&lt;li&gt;React&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; (ou ReactJS) par Facebook&lt;/li&gt;
&lt;li&gt;VueJS&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt; qui est développé par un ancien développeur de Angular&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Angular se détâche des deux autres car, il est pensé pour développer une application en Typescript, et ne permet pas (du moins pas facilement) de s&amp;rsquo;intégrer dans une page existante. La philosophie de Angular est de pousser le développement dans le sens de la qualité et d&amp;rsquo;exploiter les concepts de développement en Typescript, avec des concepts d&amp;rsquo;injection de dépendance, décorateurs, et une structure très précise. Il est purement MVC&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt; ce qui est donc bien plus &amp;ldquo;logique&amp;rdquo; à utiliser pour un développeur qui vient d&amp;rsquo;un autre univers (Java, PHP, Python&amp;hellip;)&lt;/p&gt;
&lt;p&gt;VueJS propose deux mode de développement. Soit en pure JS pour s&amp;rsquo;intégrer dans une page, ou alors une écriture en &amp;ldquo;vue file&amp;rdquo;, qui est en soit une manière de séparer la vue et le controle mais de rester dans le même fichier. Mais ce que je lui reproche c&amp;rsquo;est le fait de demander de proposer des propriétés spécifiques pour définir les méthodes. Et d&amp;rsquo;ailleurs, un composant est un objet à retouner et non une classe.&lt;/p&gt;
&lt;p&gt;ReactJS est, tout comme VueJS, une librairie qui permet de créer une application ou de s&amp;rsquo;intgrérer à une page existante. Cela dit, il a une approche un peu spéciale en utilisant JSX et, par conséquent, &lt;em&gt;mélange&lt;/em&gt; le HTML au JS. Ce choix divise beaucoup, et je ne suis pas un fan de cette écriture. Cela-dit, j&amp;rsquo;ai du mal à l&amp;rsquo;apprécier tel quel. Il manque de standards, d&amp;rsquo;homogénéité, et ce mélange de code HTML dans le JS ne m&amp;rsquo;apporte rien, voir me fait perdre en qualité. &lt;strong&gt;Mais il faut l&amp;rsquo;admettre, ReactJS marche très bien.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bref, React et VueJS sont d&amp;rsquo;excellents outils, mais je préfère largement Angular pour sa rigueur et son assurance.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Je me suis surtout rendu compte d&amp;rsquo;une chose importante, c&amp;rsquo;est que &lt;em&gt;je pense&lt;/em&gt; que ce qui me plait énormément avec Angular, au delà de la structure du projet, c&amp;rsquo;est Typescript. Beaucoup rejettent ce langage qui est pourtant une amélioration compatible de Javascript (vraiment, en fait vous codez en JS mais avec des extensions de langage) - et que c&amp;rsquo;est un point majeur pour assurer le fonctionnement d&amp;rsquo;un projet. Le typechecking, les &amp;ldquo;generics&amp;rdquo; (je suis pourtant pas un adorateur de cette écriture), etc&amp;hellip; je trouve cela rassurant. Angular utilise ce langage à la perfection, et pour cause, il est nativement prévu pour ce langage. La différence se trouve dans le choix par défaut.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et donc, ReactJS et VueJS ont choisi JSX et JS comme approche native.&lt;/p&gt;
&lt;p&gt;oui&amp;hellip; mais&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Mais vous le savez certainement, il est possible d&amp;rsquo;utiliser TypeScript avec React et VueJS. Autaut vous dire que cela change &lt;strong&gt;fortement&lt;/strong&gt; mon point de vue - l&amp;rsquo;écriture de composant est tout à coup bien plus adaptée, surtout si vous travaillez en équipe. J&amp;rsquo;ai donc tenté l&amp;rsquo;expérience sur quelques projets. Et je me suis fait violence pour démarrer un projet en React + TSX (TypeScript à la JSX) pour vraiment creuser sur le fonctionnement et faire preuve d&amp;rsquo;honnêteté quant à mon analyse.&lt;/p&gt;
&lt;p&gt;Et bha&amp;hellip; c&amp;rsquo;est pas mal du tout ! VueJS et React s&amp;rsquo;en sortent même très bien, et cela m&amp;rsquo;a apporté bien plus de plaisirs à coder et surtout bien moins de stress pour gérer les erreurs.&lt;/p&gt;
&lt;p&gt;Alors comment ça se passe ?&lt;/p&gt;
&lt;h1 id=&#34;vue--ts&#34;&gt;Vue + TS&lt;/h1&gt;
&lt;p&gt;Depuis que &lt;code&gt;npx&lt;/code&gt;&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt; est sorti, c&amp;rsquo;est bien plus simple de démarrer un projet sans trop se prendre la tête. Le client &amp;ldquo;vue&amp;rdquo; propose des options qu&amp;rsquo;il faut regarder. L&amp;rsquo;idée est de passer par le choix &amp;ldquo;manuel&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;npx -p @vue/cli vue create my-ts-application
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il faut passer par le menu &amp;ldquo;Manually select features&amp;rdquo; et sélectionner (avec la touche espace) les options suivante:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Babel&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;Router (je le recommande)&lt;/li&gt;
&lt;li&gt;CSS preprocessor (vous choisirez celui qui vous plait)&lt;/li&gt;
&lt;li&gt;Linter / Formatter (encore une fois c&amp;rsquo;est conseillé)&lt;/li&gt;
&lt;li&gt;Unit Testing (optionnel mais on aime faire de la qualité n&amp;rsquo;est-ce pas ?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ensuite, je vous coneseille &lt;strong&gt;fortement&lt;/strong&gt; de laisser les choix par défaut (&amp;ldquo;Use class-style component syntax&amp;rdquo;, &amp;ldquo;Babel along Typescript&amp;rdquo;&amp;hellip;), &lt;strong&gt;sauf&lt;/strong&gt; l&amp;rsquo;option ESLint que je trouve plus adapté avec &amp;ldquo;Prettier&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Donc, le résultat:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Router, CSS Pre-processors, Linter
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass)
? Pick a linter / formatter config: Prettier
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors, ça change quoi ?&lt;/p&gt;
&lt;p&gt;Et bien voilà à quoi ressemble une Vue:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;import { Component, Prop, Vue } from &amp;quot;vue-property-decorator&amp;quot;;

@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;

  onButtonClick() {
    this.msg = &amp;quot;Clicked !&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On a enfin une vraie classe exportée, les méthodes, data, etc sont enfin des éléments de la classe et non des propriétés à écrire dans une propriété spécifique. Les décorateurs &lt;code&gt;@Component&lt;/code&gt; et &lt;code&gt;@Prop&lt;/code&gt; proposent de configurer notre classe de manière claire. Tout change ! Et bon sang que c&amp;rsquo;est plus pratique pour coder.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;@Prop&lt;/code&gt; est clairment l&amp;rsquo;équivalent de &lt;code&gt;@Input&lt;/code&gt; de Angular - on retrouve vite ses petits.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;span class=&#34;emoji&#34;&gt;⚠&lt;/span&gt; Juste un point &lt;strong&gt;important&lt;/strong&gt; - n&amp;rsquo;oubliez pas que la balise &amp;ldquo;script&amp;rdquo; doit avoir l&amp;rsquo;attribut &lt;code&gt;lang=&amp;quot;ts&amp;quot;&lt;/code&gt;, c&amp;rsquo;est à dire: &lt;code&gt;&amp;lt;script lang=&amp;quot;ts&amp;quot;&amp;gt;...&lt;/code&gt; &lt;span class=&#34;emoji&#34;&gt;⚠&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;En ce qui concerne la reconnaissance de syntaxe dans VSCode (ou VSCodium si vous préférez une version plus respectueuse de votre vie privée), passez par le plugin &amp;ldquo;Vutter&amp;rdquo; va vous donner satisfaction. Il reconnait très bien le code TypeScript.&lt;/p&gt;
&lt;h1 id=&#34;react--tsx&#34;&gt;React + TSX&lt;/h1&gt;
&lt;p&gt;Je ne suis pas un fan de React (loin de là) - je le trouve peu rigoureux (on peut écrire un composant avec une fonction ou une classe, le routing se fait dans la vue, et la vue se trouve dans le code&amp;hellip; bof&amp;hellip;). Mais je dois être honnête, il est pratique et simple. Je comprend l&amp;rsquo;engouement qu&amp;rsquo;ont les développeurs Web pour cette techno.&lt;/p&gt;
&lt;p&gt;Mais il faut que je le reconnaisse aussi, avoir utilisé React avec TypeScript m&amp;rsquo;a donné bien plus de plaisirs.&lt;/p&gt;
&lt;p&gt;Comment on fait ?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;npx create-react-app my-ts-react --template typescript
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bon&amp;hellip; Déception, l&amp;rsquo;application générée ne propose pas un composant exemple. Mais en soit c&amp;rsquo;est assez simple de trouver la manière de faire. Voici un petit exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;import React, { Component } from &#39;react&#39;;

export default class Foo extends Component {
    render() {
        return &amp;lt;div&amp;gt;Mon Composant !&amp;lt;/div&amp;gt;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous allez me dire &amp;ldquo;ok&amp;hellip; super&amp;hellip; c&amp;rsquo;est juste plus lourd que d&amp;rsquo;écrire une classe JS ou une fonction&amp;hellip; super ton idée&amp;rdquo;, mais &lt;strong&gt;non !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Tout d&amp;rsquo;abord, la gestion d&amp;rsquo;état (statefull) est plus simple et surtout plus claire en utilisant l&amp;rsquo;écriture génériques. Idem pour les propriétés qui peuvent être des interfaces à part entière:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;
// Des propriétés du composant
// comprennez par là: attribut de la balise
interface Profile {
    name: string,
    age: number
}

// Une interface pour les états
interface Activated {
    isActive: boolean
}

// on utilise cet état et les propriétés dans notre classe
export default class Foo extends Component&amp;lt;Profile, Activated&amp;gt; {

    state: Activated {
        isActive: false
    }

    activate = () =&amp;gt; {
        this.setState({
            isActive: !this.state.isActive
        })
    }

    render() {
        return &amp;lt;div&amp;gt;
            &amp;lt;p&amp;gt;{this.props.name} - {this.props.age} ans - {this.state.isActivate}}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;button onClick={this.activate}&amp;gt;(De)Activate&amp;lt;/button&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On a donc enfin du &lt;em&gt;type checking&lt;/em&gt;, des classes bien plus claires et faciles à adapter, une logique d&amp;rsquo;écriture de projet plus adaptée à un environnement plus large. On réduit drastiquement le nombre d&amp;rsquo;erreurs.&lt;/p&gt;
&lt;p&gt;Bon, par contre, la perte de contexte dans la vue est toujours actuelle&amp;hellip; donc les &amp;ldquo;handlers&amp;rdquo; doivent être déclarés en &amp;ldquo;arrow fuction&amp;rdquo; (source d&amp;rsquo;erreur&amp;hellip;)&lt;/p&gt;
&lt;p&gt;Bref, j&amp;rsquo;ai clairement redécouvert React en passant par TypeScript.&lt;/p&gt;
&lt;h1 id=&#34;du-coup-je-quitte-angular-&#34;&gt;Du coup, je quitte Angular ?&lt;/h1&gt;
&lt;p&gt;Et bien&amp;hellip; non. &lt;span class=&#34;emoji&#34;&gt;😁&lt;/span&gt; - du moins, sur les gros projets, je reste sur le fait que Angular est plus adapté. Mais pour des applications plus légère, ou dans le cas où Angular peut faire peur, je pense pouvoir proposer React avec TSX. Quant à Vue, je le propose généralement si Angular est refusé, et j&amp;rsquo;essai de faire passer l&amp;rsquo;idée d&amp;rsquo;utiliser TS. Vous l&amp;rsquo;avez compris, React est mon 3ieme choix. Mais dites-vous que quelques semaines auparavent je refusais simplement de le proposer. J&amp;rsquo;ai simplement pris conscience que c&amp;rsquo;est la structure projet d&amp;rsquo;une applicaiton React qui me gène (je parle de structure de code, pas de la structure logicielle car, avec de la rigueur, on peut structurer une applicaiton proprement avec n&amp;rsquo;importe quel techno)&lt;/p&gt;
&lt;p&gt;Angular est, de base, prévu pour développer en Typsescript. Il propose l&amp;rsquo;injection de dépendance et le routage par défaut. Ses templates sont bien séparés du controlleur, et je trouve que la gestion des requêtes HTTP (incluses nativement) est beaucoup plus adaptée que celle de React et VueJS (non native, pas standard).&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;apprécie aussi énormément le langage de template de Angular - celle de Vue me va, alors que je trouve React bien en dessous en terme d&amp;rsquo;écriture. Par exemple, faire une boucle d&amp;rsquo;affichage dans React n&amp;rsquo;est pas générique, il y a plein de façons de faire, et (je sais que cet argument ne vous ira pas) ce n&amp;rsquo;est pas du tout commun par rapport à Jinja / Twig ou à ce qu&amp;rsquo;on peut trouver ailleurs. J&amp;rsquo;attends que React propose une vraie amélioration de ce coté.&lt;/p&gt;
&lt;p&gt;De plus, Angular a un ecosystème que j&amp;rsquo;adore, par exemple &lt;a href=&#34;https://ng-bootstrap.github.io/&#34;&gt;ng-bootstrap&lt;/a&gt;&amp;hellip; Je sais qu&amp;rsquo;il existe des équivalents chez React et VueJS, mais l&amp;rsquo;intégration est selon moi bien mieux pensée dans l&amp;rsquo;univers Angular.&lt;/p&gt;
&lt;p&gt;Vue me parait aussi plus simple que React, y compris avec TypeScript. Pas besoin de mettre des &lt;em&gt;arrow functions&lt;/em&gt; pour éviter de perdre le contexte avec les handlers, et il ne demande pas l&amp;rsquo;utilisation de &amp;ldquo;generics&amp;rdquo; pour ajouter une propriété de composant. Idem pour la gestion d&amp;rsquo;état qui est plus lourde chez React. Clairement, Vue utilise Typescript de manière plus optimale que React, mais encore une fois Angular est un cran au dessus à ce niveau.&lt;/p&gt;
&lt;p&gt;Je ne rejette pas React. Je n&amp;rsquo;ai juste pas la même philosophie d&amp;rsquo;écriture de projet que celle proposée par ce framework. Cela étant dit, si je dois démarrer un projet React à l&amp;rsquo;avenir, je vais militer pour que TypeScript soit utilisé dans le projet. Même si il n&amp;rsquo;expoite pas toute la puissance du TypeScript, la qualité d&amp;rsquo;un projet augemente fortement par rapport à ce que je vois en JSX.&lt;/p&gt;
&lt;p&gt;Donc, bilan, je suis très attaché au code, et pour ma part le langage importe énormément pour la productivité. React gagne à utiliser ce langage. C&amp;rsquo;est bien entendu &lt;strong&gt;ma vision, mon avis, ma façon de voir les choses&lt;/strong&gt;. Vous avez tout à fait le droit, et certainement raison, de ressentir les choses autrement, d&amp;rsquo;avoir d&amp;rsquo;autres objectifs et une philosophie différente de la mienne. C&amp;rsquo;est aussi ça la force de notre métier, chers amis.&lt;/p&gt;
&lt;p&gt;Vous l&amp;rsquo;aurez donc compris, mon choix reste sur Angular, puis VueJS, et enfin React. Et surtout que je préfère largement utiliser Typescript. Et je finis sur le positif, React me plait avec Typescript, il &lt;em&gt;peut, potentiellement,&lt;/em&gt; être un de mes choix dans des futurs projets à condition d&amp;rsquo;utiliser TSX.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://angular.io/&#34;&gt;https://angular.io/&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://reactjs.org/&#34;&gt;https://reactjs.org/&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://vuejs.org/&#34;&gt;https://vuejs.org/&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;Model Vue Controller&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:5&#34;&gt;
&lt;p&gt;Voir &lt;a href=&#34;https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b&#34;&gt;Introducing npx and npm package runner&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </description>
    </item>
    
    <item>
      <title>Que vaut Kapsule, le Kubernetes autogéré de Scaleway ?</title>
      <link>https://www.metal3d.org/blog/2020/kapsule-scaleway/</link>
      <pubDate>Tue, 07 Apr 2020 05:15:15 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2020/kapsule-scaleway/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2020/kapsule-scaleway/illu-kapsule-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Après 3 ans de bons et loyaux services, mon cluster Kubernetes a lâché.
Il a fallu que je trouve une solution pour remettre mon blog ,gitea, et
autres services sur pattes sans y passer des heures. J&amp;rsquo;ai essayé
Kapsule, le Kubernetes autogéré de Scaleway - voilà ce que j&amp;rsquo;en pense.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT 22/04/2020:&lt;/strong&gt; Si vous avez déjà lu cet article, j&amp;rsquo;ai corrigé une
partie de ce que je disais sur le LoadBalancer, voir plus bas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT 14/05/2020:&lt;/strong&gt; Scaleway a pris en compte le manque de
documentation à propos du LoadBalancer et je les en remercie. Voir plus
bas, je vous donne le lien vers la documentation.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.scaleway.com&#34;&gt;Scaleway&lt;/a&gt; est un service &amp;ldquo;cloud&amp;rdquo;, fournisseur
européen d&amp;rsquo;infrastructure aussi connu sous le nom de &lt;em&gt;Online SAS&lt;/em&gt;.
Fondée par Xavier Niel, c&amp;rsquo;est une filiale du groupe Iliad. Vous savez,
Free&amp;hellip; tout ça&amp;hellip;&lt;/p&gt;
&lt;p&gt;Scaleway est un &lt;em&gt;IaaS&lt;/em&gt; (Infrastructure as a Service), un peu comme
Amazon AWS mais avec des prix nettement inférieurs. Soyons honnêtes, il
y a encore 4 ou 5 ans, l&amp;rsquo;offre était plutôt limitée. Peu de machines
proposées, et rien de bien fou coté stockage à part des disques à monter
sur celles-ci.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Par contre, l&amp;rsquo;idée était intelligente. J&amp;rsquo;ai beau avoir un vrai souci
avec Free pour ses offres aussi cassées que son réseau, là on parle
d&amp;rsquo;un service de location de serveurs pour pas cher, avec des options
de préinstallation bien fichues et qui fonctionnaient (à peu près)
bien.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Pour chaque machine que vous créez, vous avez le choix de la
distribution à installer, ou carrément de demander une installation d&amp;rsquo;un
service prêt à l&amp;rsquo;emploi. En clair, vous pouvez tout aussi bien demander
une Fedora, une Debian ou une CentOS que d&amp;rsquo;avoir une instance avec
Gitlab, OpenVPN ou Redmine. En quelques clics, tout est réglé.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et soyons clairs, c&amp;rsquo;est vraiment pas cher du tout. Les machines
virtuelles 2CPU 2Go de RAM à 2.99€ je trouve que c&amp;rsquo;est vraiment à la
porté de tous.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scaleway a beaucoup évolué&lt;/strong&gt; pour proposer, depuis quelques temps,
d&amp;rsquo;autres services tels qu&amp;rsquo;un service d&amp;rsquo;Object Storage de type S3, un
Load Balancer, un Postgres à la demande, des machines GPU (pour le
machine learning), et dernièrement Kapsule qui propose la gestion d&amp;rsquo;un
cluster Kubernetes autogéré.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et je vais être honnête, une fois de plus je ne suis pas fana de
X.Niel, ni de Free, mais Scaleway a vraiment fait un effort de qualité
pour un prix toujours aussi bas - sans pour autant être le fameux &amp;ldquo;AWS
du pauvre&amp;rdquo; comme je l&amp;rsquo;ai entendu. Non, là vraiment, c&amp;rsquo;est pas mal du
tout.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Alors pour mon cluster pété, les solutions proposées étaient donc:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;soit de passer à des nouvelles machines (et donc de me retaper une
installation de Kubernetes)&lt;/li&gt;
&lt;li&gt;soit de passer à
&lt;a href=&#34;https://www.scaleway.com/fr/kubernetes-kapsule/&#34;&gt;Kapsule&lt;/a&gt; - ce
fameux nouveau service Kubernetes autogéré de Scaleway.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;kapsule&#34;&gt;Kapsule&lt;/h1&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/kapsule-scaleway/kapsule.webp&#34; alt=&#34;Kapsule&#34;&gt;&lt;/p&gt;
&lt;p&gt;Le principe est super simple, au lieu de vous taper l&amp;rsquo;installation de
Kubernetes manuellement (avec Rancher, Kubespray, ou ce que vous
voulez), vous n&amp;rsquo;avez ici qu&amp;rsquo;à demander la création du cluster. Après
quelques minutes (en fait moins d&amp;rsquo;une minute dans mon cas) le cluster
est prêt à l&amp;rsquo;emploi.&lt;/p&gt;
&lt;p&gt;Le prix de base est vraiment pas cher, pour un &amp;ldquo;node&amp;rdquo; DEV 1M (3cpu, 4Go
RAM) vous en aurez pour 7,99€/mois.&lt;/p&gt;
&lt;p&gt;Une option très intéressante est que &lt;strong&gt;vous pouvez activer l&amp;rsquo;auto
scaling avec une limite maximum de Nodes&lt;/strong&gt;. Le tout dans des Pools de
machines dimensionnées selon vos besoins. On en parle après.&lt;/p&gt;
&lt;p&gt;Par exemple, dans mon cas, j&amp;rsquo;ai demandé à avoir maximum 3 machines en
cas de besoin. Bien entendu, ces machines sont facturées à l&amp;rsquo;heure
(0.016€/h) jusqu&amp;rsquo;à une limite de 2€99, et &lt;strong&gt;seulement si elles sont
démarrées&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Notez que, à cela, vous allez certainement ajouter quelques euros si
vous avez besoin de stockage, car Kapsule propose de base un
&amp;ldquo;StorageClass&amp;rdquo; qui permet de créer des disques SSD réseaux. Donc en
gros, si vous démarrez une base de donnée sur votre cluster, il faudra
stocker les données sur un &amp;ldquo;Persitent volume&amp;rdquo;, et donc passer un par un
&amp;ldquo;Claim&amp;rdquo; - le prix des stockages n&amp;rsquo;est pas douloureux (environ
€0.08/GB/mois).&lt;/p&gt;
&lt;p&gt;La présentation de l&amp;rsquo;offre est ici :
&lt;a href=&#34;https://www.scaleway.com/fr/kubernetes-kapsule/&#34;&gt;Kapsule&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pour résumé, Kapsule associé quelques services Scaleway, ça peut se
représenter ainsi:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/kapsule-scaleway/./kapsule-diagrame.webp&#34; alt=&#34;Structure Kapsule&#34;&gt;&lt;/p&gt;
&lt;p&gt;Maintenant, faisons le point sur ce que je trouve vraiment bien, et ce
que je trouve un peu moins cool&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;création-dun-cluster&#34;&gt;Création d&amp;rsquo;un cluster&lt;/h1&gt;
&lt;p&gt;L&amp;rsquo;interface de création est vraiment bien fichue.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/kapsule-scaleway/cluster-creation.webp&#34; alt=&#34;Création du cluster&#34;&gt;&lt;/p&gt;
&lt;p&gt;Une des choses que j&amp;rsquo;apprécie particulièrement chez Scaleway, c&amp;rsquo;est le
fait de &lt;strong&gt;voir le prix que vous allez payer en bas de la page de
création&lt;/strong&gt; - c&amp;rsquo;est d&amp;rsquo;ailleurs ce qui m&amp;rsquo;agace chez AWS qui ne permet pas
de calculer clairement le coût d&amp;rsquo;une création de machine ou de cluster.
Scaleway ne se fout pas de nous, en plus de proposer un service pas
cher, il indique clairement les informations de prix (remarquez, c&amp;rsquo;est
peut-être lié&amp;hellip;)&lt;/p&gt;
&lt;p&gt;Autres options, vous pouvez demander à avoir accès au &lt;em&gt;Dashboard&lt;/em&gt; et
d&amp;rsquo;installer un &lt;em&gt;Ingress Controller&lt;/em&gt; de type NGinx ou HAProxy. Pas besoin
du Dashboard pour ma part, mais j&amp;rsquo;ai opté pour Nginx afin de ne pas
avoir à le faire moi-même.&lt;/p&gt;
&lt;p&gt;Finalement, vous avez :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les masters gérés par Scaleway&lt;/li&gt;
&lt;li&gt;des nodes dans au moins un &amp;ldquo;Pool&amp;rdquo; (on en parle après)&lt;/li&gt;
&lt;li&gt;un fichier pour &lt;code&gt;kubectl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;au choix, un dashboard et/ou un ingress-controller&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kapsule est donc un soulagement pour la création du cluster. C&amp;rsquo;est
rapide, pas cher, et je vais enfin m&amp;rsquo;abstraire de pas mal de tâches de
gestions.&lt;/p&gt;
&lt;h1 id=&#34;le-principe-de-pool&#34;&gt;Le principe de Pool&lt;/h1&gt;
&lt;p&gt;Kapsule propose un système de Pool de machines qui est assez clair et
pas mal pensé.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/kapsule-scaleway/pool.webp&#34; alt=&#34;Pool&#34;&gt;&lt;/p&gt;
&lt;p&gt;Un Pool est en fait un groupe de machines (node) à attacher à votre
cluster Kubernetes. Chaque Pool défini le type de machine que vous
voulez (DEV 1 M, GPU, Bare Metal&amp;hellip;) auquel vous pouvez définir l&amp;rsquo;auto
scaling ou non. Ces nodes seront labellisés correctement ce qui va vous
permettre d&amp;rsquo;utiliser des &amp;ldquo;nodeAffinity&amp;rdquo; lors des déploiements de
service.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et ça c&amp;rsquo;est une sacrée bonne idée.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Pour ma part j&amp;rsquo;ai estimé qu&amp;rsquo;une seule machine dans un seul Pool me
suffit, mais que je peux potentiellement monter en charge. J&amp;rsquo;ai donc
réglé ce Pool pour avoir maximum 3 machines qui seront démarrées
automatiquement si le besoin apparait. Pas de surprise au niveau du
prix, je sais que mon cluster me coûtera 7.99€/mois minimum, ou
23.97€/mois si le Pool grossi. Pas plus !&lt;/p&gt;
&lt;p&gt;Si vous avez quelques sites à démarrer avec plusieurs services
gourmands, il vous suffit de créer un pool avec des machines légères
(DEV 1 M par exemple), et un autre plus costaud avec des machines
robustes pour faire tourner des BDD, un Elastic Search ou je ne sais
quel glouton de CPU/RAM que vous voulez démarrer. Les Pools peuvent être
créés ou supprimés à tout moment. Ce qui est en soit une excellente
manière d&amp;rsquo;éviter de vous ruiner alors que votre besoin en CPU s&amp;rsquo;est tout
à coup réduit pour des mois.&lt;/p&gt;
&lt;p&gt;Petite chose que je viens de remarqué, on me propose des mises à jour à
faire facilement:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/kapsule-scaleway/./kapsule-update.webp&#34; alt=&#34;Kapsule update&#34;&gt;&lt;/p&gt;
&lt;p&gt;Il suffit de cliquer sur le lien &amp;ldquo;upgrade&amp;rdquo; et de choisir de faire la
mise à jour vers la version proposée.&lt;/p&gt;
&lt;p&gt;Bref, la gestion de Pool est vraiment agréable, facile à gérer, et
économique.&lt;/p&gt;
&lt;h1 id=&#34;le-storageclass&#34;&gt;Le StorageClass&lt;/h1&gt;
&lt;p&gt;Il est difficile d&amp;rsquo;utiliser un cluster Kubernetes sans avoir du stockage
persistant.&lt;/p&gt;
&lt;p&gt;Pour ceux qui ne connaissent pas le principe, une petite piqûre de
rappel:&lt;/p&gt;
&lt;p&gt;Un conteneur est dit &amp;ldquo;stateless&amp;rdquo;, cela veut dire qu&amp;rsquo;il est sensé pouvoir
être détruit sans garder un état. Du coup, toutes les données stockées
au sein du conteneur disparaissent avec le conteneur quand il est
éteint. Cela est vrai pour Docker, Podman, mais aussi Kubernetes. Vous
pensez que c&amp;rsquo;est une mauvaise chose ? &lt;strong&gt;NON&lt;/strong&gt;, c&amp;rsquo;est voulu !&lt;/p&gt;
&lt;p&gt;Le but est que vous puissiez perdre une machine du cluster et voir votre
conteneur démarrer sur une autre machine viable. Il ne &lt;strong&gt;faut pas&lt;/strong&gt; que
les données soient stockées sur l&amp;rsquo;hôte.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors comment on fait ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;La réponse est d&amp;rsquo;avoir des volumes de stockage qui sont &lt;em&gt;montés&lt;/em&gt; sur le
conteneur. On appelle cela un &lt;em&gt;volume persistant&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Or, si vous voulez servir une base de données (e.g. MariaDB, Mongo,
SQLite&amp;hellip;), la vieille méthode est de créer ce volume, puis de le
définir dans le déploiment pour indiquer où le monter. C&amp;rsquo;est pas
marrant&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bonne nouvelle ! Kubernetes propose un système de &amp;ldquo;réclamation&amp;rdquo;,
appelé &amp;ldquo;Persistent Volume Claim&amp;rdquo;.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En gros, au lieu de créer un volume, vous créez une &amp;ldquo;demande de
volume&amp;rdquo;. À charge d&amp;rsquo;un système de génération de volume de vous créer
l&amp;rsquo;espace demandé et de vous fournir le volume que Kubernetes va monter
pour vous. Pas de prise de tête. C&amp;rsquo;est super pratique&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Mais ?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip; mais y&amp;rsquo;a un hic.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Tout n&amp;rsquo;est jamais aussi simple 😤&lt;/p&gt;
&lt;p&gt;Comme le volume doit être monté sur n&amp;rsquo;importe quel conteneur de
n&amp;rsquo;importe quelle machine, il faut que ce système de stockage soit
&amp;ldquo;réseau&amp;rdquo;. On en connait quelques&amp;rsquo;uns:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ceph,&lt;/li&gt;
&lt;li&gt;Gluster,&lt;/li&gt;
&lt;li&gt;NFS,&lt;/li&gt;
&lt;li&gt;et bien d&amp;rsquo;autres&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Et par conséquent, il faut une API qui sache prendre en charge la
demande de création. Bref&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Pour ajouter une couche de stress supplémentaire, certains types de
volumes ne peuvent &lt;strong&gt;pas se monter sur plusieurs conteneurs en même
temps&lt;/strong&gt;. Cela veut dire que quand vous allez scaler un service, le
volume ne pourra se monter que sur un seul hôte. Par exemple, GlusterFS
n&amp;rsquo;a aucun souci à se monter sur plusieurs conteneurs positionnés sur
plusieurs machines, aucun souci (à part la lenteur d&amp;rsquo;écriture), NFS
pareil, mais Ceph RBD, lui, ne veut pas.&lt;/p&gt;
&lt;p&gt;Il y a donc plusieurs modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ReadWriteOnce ⇒ veut dire que le mode de montage est sur un seul
hôte à la fois, et pas plus&lt;/li&gt;
&lt;li&gt;ReadWriteMany ⇒ peut être monté sur autant d&amp;rsquo;hôte en parallèle&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;OK, reveneons à Scaleway.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Scaleway propose des disques BSSD (donc en réseau), et a préconfiguré ce
fameux StorageClass, ouf ! 🎉🕺🎊&lt;/p&gt;
&lt;p&gt;Les BSSD, ce sont ces fameux volumes proposés depuis des lustres qui
sont ici simplement créés et montés dans les Pods que vous allez
démarrer. Ils sont plutôt rapides en écriture, et ne coûtent vraiment
pas cher. Et par défaut, votre cluster aura ce &amp;ldquo;StorageClass&amp;rdquo;
préparamétré - vous n&amp;rsquo;avez rien à faire de plus, c&amp;rsquo;est là, clef-en-main
je vous dis. C&amp;rsquo;est la fête hein ! hein&amp;hellip; non ?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Heu en fait y&amp;rsquo;a encore un hic&amp;hellip; 😒&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Seulement voilà, ces disques sont ReadWriteOnce&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors, &lt;strong&gt;on se calme hein&lt;/strong&gt;, c&amp;rsquo;est fréquent. Parce que les services de
stockage distribués sont rarement capables de proposer du
ReadWriteMany en garantissant une bonne vitesse d&amp;rsquo;écriture.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Généralement les services de stockage rapides ne gèrent que le mode
ReadWriteOnce. Cela n&amp;rsquo;est pas une grosse contrainte en soi. C&amp;rsquo;est un
choix basé sur ce qui existe déjà et aussi pour éviter que vous pétiez
un câble lors de l&amp;rsquo;écriture.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pour la petite histoire, j&amp;rsquo;adore Gluster qui sait faire du
ReadWriteMany, mais sitôt que j&amp;rsquo;ai eu besoin d&amp;rsquo;écrire des donnnées
rapidement, j&amp;rsquo;ai déchanté. Et dans les faits, je n&amp;rsquo;avais pas tant
besoin de monter du ReadWriteMany.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Si vraiment vous voulez avoir un service de stockage ReadWriteMany, il
faudra l&amp;rsquo;installer par vous-même sur votre cluster, ou sur des instances
séparées. Scaleway propose ce qu&amp;rsquo;il vous faut pour 99% des besoins,
c&amp;rsquo;est déjà très bien ! Coté AWS vous pouvez utiliser un EFS (Elastic
File Système) mais souvenez vous que c&amp;rsquo;est du NFS, donc lent&amp;hellip; Sinon
c&amp;rsquo;est du EBS qui est un équivalent de ce que propose Scaleway. Donc
voilà&amp;hellip; (Voir &lt;a href=&#34;https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes&#34;&gt;la liste
ici&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Notez que c&amp;rsquo;est un sujet récurrent dans le monde de Kubernetes, et je
préfère désormais &lt;em&gt;ruser&lt;/em&gt; lorsque j&amp;rsquo;ai besoin d&amp;rsquo;avoir plusieurs Pods en
parallèle qui doivent utiliser un espace de stockage commun. Par
exemple, si je veux scaler un site qui dessert des fichiers statiques,
ils se trouve sur un S3 - et si le framework ne le gère pas, je me
débrouille pour faire tourner un truc léger, genre NFS, pour monter un
volume dans le Pod. Il exite d&amp;rsquo;autres techniques, comme par exemple
ajouter un &lt;em&gt;sidecar&lt;/em&gt; qui fait la synchro des volumes, ou Mongo qui sait
stocker des fichier en GridFS, et j&amp;rsquo;en passe&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas vraiment une spécificité de Scaleway. Le principe est
donc de s&amp;rsquo;adapter. Utiliser Kubernetes demande tout de même un peu de
boulot.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Bref, la côté positif, c&amp;rsquo;est que Scaleway propose un StorageClass qui
fonctionne très bien. Les volumes sont créés à la demande et monté en
BSSD. Et quand vous supprimez l&amp;rsquo;application, le volume est recyclé. Si
votre cluster à plus d&amp;rsquo;un node en marche, et que le Pod décide de passer
d&amp;rsquo;un node à l&amp;rsquo;autre, le volume est très vite remonté dans sur l&amp;rsquo;hôte
cible. Et pour ma part, je me passe plutôt facilement du ReadWriteMany.&lt;/p&gt;
&lt;h1 id=&#34;la-gestion-du-cluster&#34;&gt;La gestion du cluster&lt;/h1&gt;
&lt;p&gt;Là on entre dans la partie un peu moins agréable.&lt;/p&gt;
&lt;p&gt;Si vous connaissez bien Kubernetes, vous n&amp;rsquo;aurez potentiellement pas
trop de soucis de ce côté. Pour ma part, &lt;code&gt;kubectl&lt;/code&gt; et &lt;code&gt;helm&lt;/code&gt; me
suffisent à faire ce que je veux. Je n&amp;rsquo;ai pas besoin de plus.&lt;/p&gt;
&lt;p&gt;Mais si vous êtes à la recherche d&amp;rsquo;une solution interfacée pour
permettre à des équipes de Dev de déployer des projets, il va falloir
installer quelque chose. Par exemple, un
&lt;a href=&#34;https://www.rancher.com&#34;&gt;Rancher&lt;/a&gt; sur une instance dédiée.&lt;/p&gt;
&lt;p&gt;Et pour le coup, vous risquez une petite déception, car puisque vous
n&amp;rsquo;avez pas accès aux nodes en SSH, il en pourra pas tourner sur une de
ces machines. Et puisque vous n&amp;rsquo;avez pas non plus accès aux &amp;ldquo;masters&amp;rdquo;,
Rancher va vous claquer des gros blocs rouges bien terrifiants sur la
page de gestion puisqu&amp;rsquo;il n&amp;rsquo;arrivera pas à accéder à toutes les
ressources.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alors ce n&amp;rsquo;est pas si grave en soi&amp;hellip; C&amp;rsquo;est même normal et pas
bloquant du tout.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas grave car votre but est &lt;strong&gt;justement de ne pas avoir à gérer
ces parties&lt;/strong&gt;. Le monitoring est proposé par Scaleway, vous pourrez donc
utiliser Rancher normalement pour tout le reste (installation de
services, gestion des droits utilisateurs, monitoring des conteneurs,
etc.)&lt;/p&gt;
&lt;p&gt;Finalement, le souci n&amp;rsquo;est pas vraiment Scaleway, mais Rancher qui ne
propose pas (à ce que je sache) de désactiver ces erreurs de monitoring.
Toujours est-il qu&amp;rsquo;il vous faudra une machine de plus pour ça. Donc
ajoutez à la balance de 3 à 8€.&lt;/p&gt;
&lt;h1 id=&#34;sécurité-et-load-balancer&#34;&gt;Sécurité et Load Balancer&lt;/h1&gt;
&lt;p&gt;Cette partie est un peu plus problématique. Rien de très grave, mais
cela peut entrer en conflit avec vos besoins.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abords, les nodes ont forcément une IP &lt;strong&gt;publique&lt;/strong&gt;. Impossible de
mettre ces machines derrière un bastion. Ce qui est gênant c&amp;rsquo;est que
cela implique que vos machines peuvent avoir un &amp;ldquo;NodePort&amp;rdquo; ouvert par un
service qui sera accessible.&lt;/p&gt;
&lt;p&gt;Alors, en soit c&amp;rsquo;est pratique et ce n&amp;rsquo;est pas si grave, cela permet
d&amp;rsquo;utiliser facilement &lt;code&gt;nginx-controller&lt;/code&gt; et faire pointer vos domaines
sur un de vos Nodes. Mais aussi d&amp;rsquo;utiliser des NodePort pour des
services non HTTP. Par exemple avec Gitea pour accéder au protocole git
en SSH et non en HTTPS, je peux &lt;em&gt;binder&lt;/em&gt; sur un NodePort.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est plus compliqué avec un bastion devant avec une configuration de
Load Balancer à régler au poil (ce qui est possible avec le LB de
Scaleway, on en parle tout de suite). Effectivement, ici, pas besoin de
se battre puisque les nodes sont accessibles.&lt;/p&gt;
&lt;p&gt;Ça vous gène niveau sécurité ? Je peux le comprendre mais dans les faits
ce n&amp;rsquo;est pas si &amp;ldquo;open bar&amp;rdquo; que ça en à l&amp;rsquo;air. Les nodes n&amp;rsquo;acceptent pas
de shell SSH, ils sont gérés par Scaleway.&lt;/p&gt;
&lt;p&gt;Là où ça pêche, c&amp;rsquo;est si vous ne maitrisez pas Kubernetes et que, pour
des raisons pratiques, vous décidiez d&amp;rsquo;ouvrir des NodePort sur des
services sensibles. &lt;strong&gt;C&amp;rsquo;est de votre responsabilité !&lt;/strong&gt;. Il est évident
que vous devez faire un peu de travail pour que vos services tournent
proprement. Donc, si vous avez une base de données qui tourne sur votre
cluster, il serait complètement fou d&amp;rsquo;ouvrir son port d&amp;rsquo;accès en
NodePort, &lt;strong&gt;il faut bien entendu utiliser un type ClusterIP&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Reste la question des accès HTTP(s) puisque vous avez certainement
demandé d&amp;rsquo;avoir un &lt;strong&gt;Ingress Controller&lt;/strong&gt; (NGinx ou Traefik). Je me suis
posé la question un moment, et je deais être un peu fatigué ce jour là
parce que, quand on connait Kubernetes, la réponse tombe sour le sens&amp;hellip;&lt;/p&gt;
&lt;p&gt;Scaleway propose un service de LoadBalancer haute dispo, et au sein de
votre cluster il y a déjà un controller qui repère les &amp;ldquo;Services&amp;rdquo; qui
ont un type &amp;ldquo;LoadBalancer&amp;rdquo;. Si vous en créez un, alors un LoadBalancer
va être créé coté Scaleway avec une IP fixe. Du coup, si vous faite
tomber un node et qui redémarre, ou si vous ajoutez des nodes, le
loadbalancer sera reconfiguré pour faire son travail.&lt;/p&gt;
&lt;p&gt;Si vous voulez que votre IngressController puisse être utilisé par un
LoadBalancer, il suffit de lui ajouter un &amp;ldquo;Service&amp;rdquo; de type
&amp;ldquo;LoadBalancer&amp;rdquo; (non vraiment je ne comprends pas pourquoi je n&amp;rsquo;y ai pas
pensé&amp;hellip;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Changement au 14/05/2020&lt;/strong&gt; Scaleway à donné une très bonne page pour
expliquer comment activer un load-balancer wildcard.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai signalé un manque d&amp;rsquo;informations sur ce sujet, et j&amp;rsquo;en avais parlé
avec ScaleWay sur leur Slack et Twitter. Par chance, chez Scaleway, les
DevOps et admins sont très sympas et prennent le temps de répondre de
manière détaillée. Vraiment, merci à eux. J&amp;rsquo;ai donc eu la réponse, mais
j&amp;rsquo;ai surtout eut la surprise d&amp;rsquo;avoir cette réponse sur mon Twitter:&lt;/p&gt;
&lt;p&gt;{{&amp;lt; twitter 1260510424178601984 &amp;gt;}}&lt;/p&gt;
&lt;p&gt;Là pour le coup, je tire mon chapeau. Il ne manquait vraiment que cette
page. Donc, la voici, elle contient ce dont vous avez besoin:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.scaleway.com/en/docs/using-a-load-balancer-to-expose-your-kubernetes-kapsule-ingress-controller-service/&#34;&gt;Using a load balancer to expose your Kubernetes Kapsule ingress
controller
service&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;En résumé, pour Nginx-ingress, il suffit de pousser cette configuration
sur votre cluster une fois pour toute:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1
kind: Service
metadata:
  name: nginx-ingress
  namespace: kube-system
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  type: LoadBalancer
  ports:
    - port: 80
      name: http
      targetPort: 80
    - port: 443
      name: https
      targetPort: 443
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En appliquant ce fichier, on a bien une IP pour notre
ingress-controller:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nginx-ingress   LoadBalancer   10.45.150.14    51.XX.XX.XX   80:31468/TCP,443:30454/TCP   10s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le tour est joué, cette IP peut maintenant être assigné dans les DNS, on
la perdra pas, et le LB va faire le boulot comme demandé.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org/blog/2020/kapsule-scaleway/backend-status.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Ce serait bien cool que Scaleway documente cela, car mon article est
resté plusieurs jours avec une mauvaise info&amp;hellip; Mais je ne vais pas
faire le lourdingue, les équipe de Scalexay répondent rapidement et avec
des vraies bonne infos.&lt;/p&gt;
&lt;p&gt;Donc, on résume:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Le LoadBalancer est utilisable, fiable, fait le boulot, c&amp;rsquo;est
automatique et ça coute pas si cher.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;je-vais-conclure--jc-duss&#34;&gt;Je vais conclure (© J.C. Duss)&lt;/h1&gt;
&lt;p&gt;Kapsule est un très bon service de création et de gestion de cluster
Kubernetes. Il est économique, &lt;strong&gt;prêt à l&amp;rsquo;emploi&lt;/strong&gt;, et facile à
manipuler. Il est parfait pour les freelances qui veulent déployer des
démos de site, ou pour des petites entreprises qui veulent héberger
leurs services dans un PaaS.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tout est là, y compris la création de volume.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Les plus grosses entreprises pourront l&amp;rsquo;utiliser notamment pour
l&amp;rsquo;hébergement de démo et/ou pour l&amp;rsquo;intégration continue, mais cela
demande un petit effort supplémentaire pour intégrer des outils de
gestion PaaS tels que Rancher (afin d&amp;rsquo;avoir une gestion de droits par
utilisateurs).&lt;/p&gt;
&lt;p&gt;Les pools élastiques sont clairement efficaces et économiques. C&amp;rsquo;est
beaucoup moins compliqué et moins cher que chez AWS.&lt;/p&gt;
&lt;p&gt;En ce qui concerne l&amp;rsquo;hébergement de services pour les grosses
entreprises avec de gros besoins, je ne vais pas trop m&amp;rsquo;avancer.
Cela-dit, je gère déjà des clusters pour des grosses structures et je
dois bien avouer que Kapsule peut répondre à certains clients
d&amp;rsquo;envergure. Si mon avis vous intéresse, je place Kapsule au dessus de
la solution EKS de Amazon, que ce soit en terme d&amp;rsquo;interface, de prix,
d&amp;rsquo;aide et de tenu de route. C&amp;rsquo;est bien entendu mon avis personnel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le fait de pouvoir gérer des pools de machines adaptées à la puissance
nécessaire, et de pouvoir les couper à tout moment permet de faire de
sacrées économies.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Kapsule peut donc clairement être utilisé par une entreprise de
&lt;strong&gt;grande envergure&lt;/strong&gt;, tout comme un particuier, un &lt;strong&gt;freelance ou une
petite entreprise&lt;/strong&gt;. C&amp;rsquo;est selon moi la meilleure offre du marché de
par son prix, sa souplesse et sa simplicité.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Scaleway propose un &lt;strong&gt;S3&lt;/strong&gt; et un &lt;strong&gt;registre docker privé&lt;/strong&gt; qui, couplés
à Kapsule, permettent en tout cas de faire 99% du travail attendu en ce
qui concerne un hébergement PaaS Kubernetes. Le fait que les machines
soient monitorées et que Kubernetes soit géré par une équipe dédiée en
prestation est un vrai plus. Le prix du stockage d&amp;rsquo;image est de 0.025€
mais je trouve que le prix de la bande passante inter-région un peu
élevé. 0.03€ par push/pull ça peut devenir vite angoissant. &lt;strong&gt;Par
contre, en intra-région, c&amp;rsquo;est grauit&lt;/strong&gt;. J&amp;rsquo;ai mis en place un
&lt;a href=&#34;https://drone.io&#34;&gt;Drone&lt;/a&gt; qui construir les images et les pousse sur le
registre. Donc de ce coté, je ne paye rien car les pulls effectués par
mes noeud Kubernetes se trouve sur la même région (Paris) que le
registre.&lt;/p&gt;
&lt;p&gt;Je ne vois plus qu&amp;rsquo;un seul bémol: l&amp;rsquo;absence de possibilité d&amp;rsquo;utiliser un
bastion. Si cela est votre besoin, vous devriez plutôt passer par
&lt;a href=&#34;https://www.scaleway.com/fr/datacenter/&#34;&gt;Scaleway Datacenter&lt;/a&gt; qui
propose aussi une gestion de Kubernetes mais bien plus adaptée à la
production niveau &amp;ldquo;entreprise&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Bref, si vous avez envie de tester un hébergement Kuberenetes pour pas
cher, et qui fonctionne bien, Kapsule est une bonne solution alternative
à AWS EKS. Et en plus, &lt;strong&gt;c&amp;rsquo;est Européen ! (et largement géré par une
boite Française).&lt;/strong&gt;&lt;/p&gt;
&lt;script&gt;
document.querySelectorAll(&#39;.article-post img&#39;).forEach(el =&gt; {
    if (el.naturalWidth &gt; 400) {
        el.classList.add(&#39;shadow&#39;)
    }
})

document.querySelectorAll(&#39;.article-post img[alt=Kapsule]&#39;).forEach(el =&gt; {
    el.classList.add(&#39;float-left&#39;, &#39;mr-2&#39;, &#39;mb-2&#39;)
})
&lt;/script&gt;

      </description>
    </item>
    
    <item>
      <title>Le don de calcul pour rémunérer un blog</title>
      <link>https://www.metal3d.org/blog/2019/monero-donation/</link>
      <pubDate>Sun, 13 Oct 2019 03:58:36 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2019/monero-donation/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2019/monero-donation/account-accounting-balance-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Au lieu de vous imposer de la pub, je vous propose de faire un &amp;ldquo;don de calcul&amp;rdquo;. Pourquoi ? Comment ? je vous explique !&lt;/p&gt;
&lt;h1 id=&#34;de-hein--quoi--&#34;&gt;De hein ? Quoi ? &lt;span class=&#34;emoji&#34;&gt;👀&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;Ce blog a failli être abandonné un certain nombre de fois. J&amp;rsquo;en ai perdu la foi, j&amp;rsquo;ai même quasiment arrêté d&amp;rsquo;écrire. Mais j&amp;rsquo;y reviens.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;étais très productif à une certaine époque, mais à force de vouloir proposer des tests, des techniques, et notamment avec la monté de la technologie de conteneurs, j&amp;rsquo;ai changé de système d&amp;rsquo;hébergement un certain nombre de fois. Même si je le voulais, en passant ce blog sur un hébergeur gratuit, j&amp;rsquo;aurais besoin de serveurs pour tester mes travaux. En l&amp;rsquo;occurence, vous le comprennez, &lt;strong&gt;ce blog me coute de l&amp;rsquo;argent.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Je propose pas mal à la communauté:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;des images Docker&lt;/li&gt;
&lt;li&gt;Je développe et je fournis aussi quelques outils&lt;/li&gt;
&lt;li&gt;je partage mon expérience ici quand j&amp;rsquo;en ai le temps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tout ça en version opensource ou creative commons, en fonction de ce que je fournis. Je ne suis pas un grand héro de l&amp;rsquo;ère informatique, mais j&amp;rsquo;apporte ma petite brique comme des milions le font. Je n&amp;rsquo;en tire pas de mérite (c&amp;rsquo;est important que vous ne pensiez pas que je pense &amp;ldquo;mériter&amp;rdquo; quoique ce soit)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Seulement, ça me coûte. Je persévère à utiliser des serveurs parce que je suis passionné par mon métier et que j&amp;rsquo;aime partager, mais ça me coûte vraiment en terme de finance.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Pour palier à cela, j&amp;rsquo;avais positionné un bloc de pub AdSense - non pas que je sois un grand fana du tracking d&amp;rsquo;utilisateur, mais soyons honnêtes leur intégration est facile, et çame rapportait de quoi rembourser une petite partie de mes frais.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et un jour&amp;hellip; AdBlock est arrivé&amp;hellip; &lt;span class=&#34;emoji&#34;&gt;😡&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;AdBlock n&amp;rsquo;est pas arrivé là par hasard. Puisque la pub peut rapporter un peu de sous, certains n&amp;rsquo;ont pas hésité à en abuser fortement. En plaçant énormément de pubs, et en faisant un gros de travail de recherche de traffic, il était possible pour certains site de se faire beaucoup d&amp;rsquo;argent. Mais au détriment des lecteurs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ralentissment de chargement (avec énormément de données à faire transiter, la planète ne nous dit pas merci)&lt;/li&gt;
&lt;li&gt;la gestion de la vie privée, et bien&amp;hellip; on s&amp;rsquo;en fiche&lt;/li&gt;
&lt;li&gt;un confort de lecture du contenu &amp;ldquo;réel&amp;rdquo; dégradé (sur mobile, certains sites sont tout bonnement inutilisables)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bref, AdBlock a permi de virer toutes ses pubs. Mais&amp;hellip; Le souci est qu&amp;rsquo;il bloque &amp;ldquo;par défaut&amp;rdquo;. Ceux qui par contre tentaient, comme je le faisais, de ne pas trop gêner le lecteur tout en essayant de ganger quelques sous pour le travail fourni, se sont retrouvé avec une perte de gain énorme. Pour ma part, la chute a été de plus de 90%.&lt;/p&gt;
&lt;h1 id=&#34;des-solutions--oui-oui&#34;&gt;Des solutions ? oui oui&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Alors il a fallu fournir encore plus de travail pour arriver à retrouver un peu de gain.&lt;/p&gt;
&lt;p&gt;Première tentative, détecter l&amp;rsquo;utilisation de bloqueur de pub et :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;soit demander gentiment au lecteur de couper son bloqueur&lt;/li&gt;
&lt;li&gt;soit interdire la lecture si AdBlock est activé&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Pour l&amp;rsquo;option 1, 50% des lecteurs ne le coupent pas. Sympa les mecs&amp;hellip; En plus cela demande un travail assez lourd, car il existe des &amp;ldquo;bloqueurs de détecteurs de bloqueurs&amp;rdquo;, du coup ça devient une guerre sans fin pour tenter de détecter un détecteur de détecteur de bloqueur de pub&amp;hellip; Bref, pas marrant.&lt;/p&gt;
&lt;p&gt;Option 2, je me suis pris des mails d&amp;rsquo;insultes&amp;hellip;&lt;/p&gt;
&lt;p&gt;OK, donc, autre tentative, demander un don.&lt;/p&gt;
&lt;p&gt;Personne ne donne. Vraiment personne. Et je vais être honnête, moi non plus je ne donne jamais d&amp;rsquo;argent à des auteurs de blogs, même si la qualité est vraiment au rendez-vous. Parce qu&amp;rsquo;en général, je vais sur un blog après une recherche ponctuelle, je trouve l&amp;rsquo;info, et je me casse.&lt;/p&gt;
&lt;p&gt;Flattr, PayPal, etc&amp;hellip; rien, que dalle, pas un don.&lt;/p&gt;
&lt;p&gt;Du coup, j&amp;rsquo;ai envie de tester un truc.&lt;/p&gt;
&lt;h1 id=&#34;le-don-de-calcul---pourquoi-pas-&#34;&gt;Le don de calcul - pourquoi pas ?&lt;/h1&gt;
&lt;p&gt;Le don de calcul existe depuis des années. Bien avant la cryptomonnaie, on avait déjà les projets Seti@Home ou notre PS3 qui calculait, pendant la veille, des constructions de molécules pour la recherche.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est un truc pas idiot en soit. Et je me suis dit que, comme la vaste majorité des lecteurs de mon blog sont des techos, on peut peut-être trouver un terrain d&amp;rsquo;entente.&lt;/p&gt;
&lt;p&gt;Aujourd&amp;rsquo;hui, la cryptomonnaie s&amp;rsquo;est bien installée dans notre univers, et elle a un petit avantage, elle permet de rémunerer le minage. Je ne vais pas trop entrer dans le détail, mais le minage de cryptomonnaie demande à un ordinateur de &amp;ldquo;prouver&amp;rdquo; qu&amp;rsquo;il a travaillé pour valider des transactions. Et cela, on peut le faire sur n&amp;rsquo;importe quelle machine (ou presque)&lt;/p&gt;
&lt;p&gt;Pour le Bitcoin, c&amp;rsquo;est mort. Ça rapporte rien à moins d&amp;rsquo;avoir un ASIC et beaucoup de chance, ou une ferme de calcul.&lt;/p&gt;
&lt;p&gt;Par contre&amp;hellip; Le Minero, ça passe. CPU ou GPU, resistant aux ASIC, ouais ça peut le faire.&lt;/p&gt;
&lt;p&gt;Alors voilà, je me suis dit:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et pourquoi pas demander à mes lecteurs de miner un peu &amp;ldquo;à mon compte&amp;rdquo;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;En gros, ça ne vous coûte rien (enfin si, un peu d&amp;rsquo;électricité), vous lancez un mineur local ou web, en disant que votre preuve de travail me revient.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OK, mais comment ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Deux solutions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Vous pouvez utiliser mon conteneur Xmrig qui, par défaut, est paramétrer pour miner pour moi (mais vous pouvez le paramétrer pour votre compte hein)&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, vous allez utiliser un &amp;ldquo;proxy&amp;rdquo; qui tourne sur mes serveurs, qui table sur &lt;a href=&#34;https://mineroocean.stream&#34;&gt;MoneroOcean&lt;/a&gt; - ça mine des Alcoins.
Ci-après, voilà à quoi ça ressemble quand on lance le conteneur.&lt;/p&gt;
&lt;script id=&#34;asciicast-cAvcE4DzPGQhYDJj2cuA09mVJ&#34; src=&#34;https://asciinema.org/a/cAvcE4DzPGQhYDJj2cuA09mVJ.js&#34; async&gt;&lt;/script&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Vous utilisez le minage &amp;ldquo;web&amp;rdquo;, qui est sur la colonne à gauche de mon site (oui, AdBlock le cache&amp;hellip; toujours sympa hein&amp;hellip; &lt;span class=&#34;emoji&#34;&gt;😤&lt;/span&gt;)
Ici, c&amp;rsquo;est un &lt;a href=&#34;https://minero.cc&#34;&gt;Minero.cc&lt;/a&gt; qui est utilisé, ça rapporte peu, mais c&amp;rsquo;est plus facile pour vous si vous n&amp;rsquo;avez pas Docker.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Faites tourner ça &lt;strong&gt;au moins quelques minutes&lt;/strong&gt;, heures, jours&amp;hellip; Le minage, ça rapporte peu&amp;hellip;&lt;/p&gt;
&lt;p&gt;Le conteneur Docker est paramétré pour utiliser les CPU en mode &amp;ldquo;idle&amp;rdquo;, donc seulement quand ils ne sont pas utilisés pour autre chose. De plus, il n&amp;rsquo;utilise que la moitié des CPU disponibles. Vous pouvez régler le nombre de CPU utilisés en ajoutant une variable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# par exmple, avec seulement 2 CPUs
docker run --rm -it -e THREADS=2 metal3d/xmrig
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;ty-crois-vraiment-&#34;&gt;T&amp;rsquo;y crois vraiment ?&lt;/h1&gt;
&lt;p&gt;Je n&amp;rsquo;en sais rien. C&amp;rsquo;est un test. Je veux vérifier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;si ça vous intéresse&lt;/li&gt;
&lt;li&gt;si ça rapporte un peu&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour le moment je tente le coup&amp;hellip;&lt;/p&gt;
&lt;p&gt;À partir d&amp;rsquo;aujourd&amp;rsquo;hui, il y aura donc un message sur chaque billet, après introduction, qui vous demandera de faire ce minage le temps de la lecture. Si vous avez Docker, vous n&amp;rsquo;aurez donc pas à couper AdBlock. Si vous le désirez, vous pouvez utiliser le minage Web (sur votre navigateur), mais il faut couper AdBlock &lt;span class=&#34;emoji&#34;&gt;😒&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Je ne deviendrai pas riche, l&amp;rsquo;argent gagné devrait à peine rembourser 15% du coup de mes serveurs. Et encore&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans tous les cas, à tous ceux qui acceptent de miner un peu pour moi, un grand merci &lt;span class=&#34;emoji&#34;&gt;🧡&lt;/span&gt; !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Requête d&#39;API REST en bash avec http et jq</title>
      <link>https://www.metal3d.org/blog/2019/httpie/</link>
      <pubDate>Sun, 05 May 2019 14:49:15 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2019/httpie/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2019/httpie/httpie-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Nous sommes dans l&amp;rsquo;ère des API REST, le Web se tourne vers ce mode depuis des années, et ce n&amp;rsquo;est pas pour nous déplaire - car effectivement, détacher la data du &amp;ldquo;front&amp;rdquo; est une bonne idée. Et clairement cela permet aussi de devenir &amp;ldquo;client&amp;rdquo; de l&amp;rsquo;API depuis n&amp;rsquo;importe quel technologie capable de faire des appels HTTP.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;adore travailler avec un terminal, je pense que ceux qui me cotoient le savent. Et j&amp;rsquo;adore automatiser des tâches, telles que récupérer des outils non packagés sans avoir à aller sur une page web, télécharger, déplacer le binaire, etc.&lt;/p&gt;
&lt;p&gt;Ici, je vais vous montrer comment je me débrouille pour rendre automatique une récupération de nouvelle version d&amp;rsquo;un outil que j&amp;rsquo;utilise avec &lt;a href=&#34;https://scaleway.com&#34;&gt;Scaleway&lt;/a&gt;, à savoir l&amp;rsquo;outil &amp;ldquo;scw&amp;rdquo; qui me permet de manipuler les serveurs à distance (un peu comme l&amp;rsquo;outil aws-cli pour Amazon).&lt;/p&gt;
&lt;p&gt;Je vais utiliser httpie, jq, et wget en utilisant l&amp;rsquo;API github. Vous allez vite comprendre l&amp;rsquo;intérêt.&lt;/p&gt;
&lt;h1 id=&#34;avant-automatisation&#34;&gt;Avant automatisation&lt;/h1&gt;
&lt;p&gt;D&amp;rsquo;abord, il faut comprendre comment ça se passe. Pour récupérer l&amp;rsquo;outil &amp;ldquo;scw&amp;rdquo;, je dois aller sur la page &lt;a href=&#34;https://github.com/scaleway/scaleway-cli/releases&#34;&gt;https://github.com/scaleway/scaleway-cli/releases&lt;/a&gt;, chercher la dernière (taguée &amp;ldquo;latest&amp;rdquo;), puis trouver la version &amp;ldquo;linux-amd64&amp;rdquo; qui correspond à mon environnement.&lt;/p&gt;
&lt;p&gt;Clique droit sur l&amp;rsquo;url&amp;hellip; et je finis par coller l&amp;rsquo;url dans un terminal pour faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;wget &amp;lt;url collée&amp;gt; -O ~/.local/bin/scw
chmod +x ~/.local/bin/scw
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ça parait assez simple, mais ça me demande pas mal de manipulations. Or, github propose une API ouverte. Alors ne nous en privons pas.&lt;/p&gt;
&lt;h1 id=&#34;travailer-avec-lapi&#34;&gt;Travailer avec l&amp;rsquo;API&lt;/h1&gt;
&lt;p&gt;Un petit tour sur la &lt;a href=&#34;https://developer.github.com/v3/projects/&#34;&gt;documentation de github&lt;/a&gt; pour me rendre compte que je peux avoir la liste des &amp;ldquo;releases&amp;rdquo; de scw rapidement, et sans effort avoir la &amp;ldquo;dernière release&amp;rdquo; - comprennez bien &amp;ldquo;latest&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;URL est donc, dans mon cas:  &lt;a href=&#34;https://api.github.com/repos/scaleway/scaleway-cli/releases/latest&#34;&gt;https://api.github.com/repos/scaleway/scaleway-cli/releases/latest&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Le retourn est un JSON qui me présente des &amp;ldquo;assets&amp;rdquo;, quelques infos utiles, et des URL de téléchargement. Bref, un &amp;ldquo;asset&amp;rdquo; est un objet que je peux télécharger, ici ce sont les binaires pour toutes les plateformes. Or, moi, je veux la version Linux amd64.&lt;/p&gt;
&lt;p&gt;Il est temps de parler de httpie et jq.&lt;/p&gt;
&lt;h2 id=&#34;httpie-&#34;&gt;HTTPIe ?&lt;/h2&gt;
&lt;p&gt;J&amp;rsquo;utilise souvent &amp;ldquo;curl&amp;rdquo;, il est pratique et complet, mais depuis quelques années j&amp;rsquo;aime beaucoup &amp;ldquo;httpie&amp;rdquo; qui est selon moi plus adapté pour jouer avec des APIs. En soit, ça ne change pas grand chose, mais dans les faits il propose une &amp;ldquo;sortie&amp;rdquo; propre, colorée, et qui met en forme le JSON, le XML etc.&lt;/p&gt;
&lt;p&gt;Vous pouvez bien entendu ne pas utiliser httpie, mais sa facilité pourrait vous convaincre.&lt;/p&gt;
&lt;p&gt;Notez bien que &amp;ldquo;httpie&amp;rdquo; est le nom du logiciel, mais la commande à utiliser est &amp;ldquo;&lt;code&gt;http&lt;/code&gt;&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;jq-&#34;&gt;jq ?&lt;/h2&gt;
&lt;p&gt;JQ, pour Json Query, est un outil en ligne de commande qui permet de traiter (depuis STDIN, donc via des &amp;ldquo;pipes&amp;rdquo;) du contenu JSON, de faire des sélections, de la manipulation&amp;hellip;&lt;/p&gt;
&lt;p&gt;JQ peut paraitre un peu complexe dans sa syntaxe, mais il évite de passer par des commandes grep/awk qui engendrent souvent des erreurs de sélection. JQ est bien plus fiable.&lt;/p&gt;
&lt;p&gt;Allons-y&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;traitement-du-json&#34;&gt;Traitement du JSON&lt;/h1&gt;
&lt;p&gt;Pour aller plus vite, je vais juste enregistrer l&amp;rsquo;url dans une variable pour éviter de la répéter dans les exemples ci-dessous.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ export URL=&amp;quot;https://api.github.com/repos/scaleway/scaleway-cli/releases/latest&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En utilisant l&amp;rsquo;adresse  &lt;a href=&#34;https://api.github.com/repos/scaleway/scaleway-cli/releases/latest&#34;&gt;https://api.github.com/repos/scaleway/scaleway-cli/releases/latest&lt;/a&gt;, on remarque une entrée &amp;ldquo;racine&amp;rdquo; nommé &amp;ldquo;assets&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ http $URL
 ...
 ...
 {
    &amp;quot;assets&amp;quot;: [
    ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc, clairement, nous avons là un taleau d&amp;rsquo;assets. Nous devons donc descendre dans cette entrée pour avoir les assests.&lt;/p&gt;
&lt;p&gt;JQ vous propose la syntaxe &amp;ldquo;&lt;code&gt;.assets[]&lt;/code&gt;&amp;rdquo; qui va sortir tous les assets, un par un, mais &amp;ldquo;hors tableau&amp;rdquo;, donc en supprimant les crochets. Le résultat n&amp;rsquo;est plus un JSON valide, mais une série de JSON. En soit ça change presque rien, mais pour le traitement final, ça aura son effet. Mais allons-y pas à pas.&lt;/p&gt;
&lt;p&gt;Testons:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;http $URL | jq &#39;.assets[]&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Très bien&amp;hellip; Nous avons les assets. On peut récupérer les noms, par exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ http $URL | jq &#39;.assets[].name&#39;
&amp;quot;scw-darwin-amd64&amp;quot;
&amp;quot;scw-darwin-i386&amp;quot;
&amp;quot;scw-freebsd-amd64&amp;quot;
&amp;quot;scw-freebsd-arm&amp;quot;
&amp;quot;scw-freebsd-i386&amp;quot;
&amp;quot;scw-linux-amd64&amp;quot;
&amp;quot;scw-linux-arm&amp;quot;
&amp;quot;scw-linux-arm64&amp;quot;
&amp;quot;scw-linux-i386&amp;quot;
&amp;quot;scw-netbsd-amd64&amp;quot;
&amp;quot;scw-netbsd-arm&amp;quot;
&amp;quot;scw-netbsd-i386&amp;quot;
&amp;quot;scw-windows-amd64.exe&amp;quot;
&amp;quot;scw-windows-i386.exe&amp;quot;
&amp;quot;scw_1.19_amd64.deb&amp;quot;
&amp;quot;scw_1.19_arm.deb&amp;quot;
&amp;quot;scw_1.19_arm64.deb&amp;quot;
&amp;quot;scw_1.19_i386.deb&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bien, et pour les url:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ http $URL | jq &#39;.assets[].browser_download_url&#39;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-darwin-amd64&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-darwin-i386&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-freebsd-amd64&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-freebsd-arm&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-freebsd-i386&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-linux-amd64&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-linux-arm&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-linux-arm64&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-linux-i386&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-netbsd-amd64&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-netbsd-arm&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-netbsd-i386&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-windows-amd64.exe&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-windows-i386.exe&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw_1.19_amd64.deb&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw_1.19_arm.deb&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw_1.19_arm64.deb&amp;quot;
&amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw_1.19_i386.deb&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il faut maintenant ne récupérer que l&amp;rsquo;adresse de téléchargment de la version &amp;ldquo;scw-linux-amd64&amp;rdquo;, et c&amp;rsquo;est là que ça se complique un peu. Rien d&amp;rsquo;insurmontable, mais il faut connaitre ce que propose JQ en terme de filtrage.&lt;/p&gt;
&lt;p&gt;Ici, nous voulons utiliser l&amp;rsquo;asset dont le nom (name) est &amp;ldquo;scw-linux-amd64&amp;rdquo;. Pour cela, on va utiliser un &amp;ldquo;pipe jq&amp;rdquo; avec la fonction &amp;ldquo;select&amp;rdquo; (Voir &lt;a href=&#34;https://stedolan.github.io/jq/manual/#select(boolean_expression)&#34;&gt;la documentation&lt;/a&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ http $URL | jq &#39;.assets[] | select(.name==&amp;quot;scw-linux-amd64&amp;quot;)&#39;
{
  &amp;quot;url&amp;quot;: &amp;quot;https://api.github.com/repos/scaleway/scaleway-cli/releases/assets/11793235&amp;quot;,
  &amp;quot;id&amp;quot;: 11793235,
  &amp;quot;node_id&amp;quot;: &amp;quot;MDEyOlJlbGVhc2VBc3NldDExNzkzMjM1&amp;quot;,
  &amp;quot;name&amp;quot;: &amp;quot;scw-linux-amd64&amp;quot;,
  &amp;quot;label&amp;quot;: null,
  &amp;quot;uploader&amp;quot;: {
    &amp;quot;login&amp;quot;: &amp;quot;jerome-quere&amp;quot;,
    &amp;quot;id&amp;quot;: 526046,
    &amp;quot;node_id&amp;quot;: &amp;quot;MDQ6VXNlcjUyNjA0Ng==&amp;quot;,
    &amp;quot;avatar_url&amp;quot;: &amp;quot;https://avatars0.githubusercontent.com/u/526046?v=4&amp;quot;,
    &amp;quot;gravatar_id&amp;quot;: &amp;quot;&amp;quot;,
    &amp;quot;url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere&amp;quot;,
    &amp;quot;html_url&amp;quot;: &amp;quot;https://github.com/jerome-quere&amp;quot;,
    &amp;quot;followers_url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere/followers&amp;quot;,
    &amp;quot;following_url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere/following{/other_user}&amp;quot;,
    &amp;quot;gists_url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere/gists{/gist_id}&amp;quot;,
    &amp;quot;starred_url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere/starred{/owner}{/repo}&amp;quot;,
    &amp;quot;subscriptions_url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere/subscriptions&amp;quot;,
    &amp;quot;organizations_url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere/orgs&amp;quot;,
    &amp;quot;repos_url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere/repos&amp;quot;,
    &amp;quot;events_url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere/events{/privacy}&amp;quot;,
    &amp;quot;received_events_url&amp;quot;: &amp;quot;https://api.github.com/users/jerome-quere/received_events&amp;quot;,
    &amp;quot;type&amp;quot;: &amp;quot;User&amp;quot;,
    &amp;quot;site_admin&amp;quot;: false
  },
  &amp;quot;content_type&amp;quot;: &amp;quot;application/octet-stream&amp;quot;,
  &amp;quot;state&amp;quot;: &amp;quot;uploaded&amp;quot;,
  &amp;quot;size&amp;quot;: 11409167,
  &amp;quot;download_count&amp;quot;: 80,
  &amp;quot;created_at&amp;quot;: &amp;quot;2019-03-29T13:24:02Z&amp;quot;,
  &amp;quot;updated_at&amp;quot;: &amp;quot;2019-03-29T13:24:06Z&amp;quot;,
  &amp;quot;browser_download_url&amp;quot;: &amp;quot;https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-linux-amd64&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il ne reste plus qu&amp;rsquo;à récupérer la propriété &amp;ldquo;&lt;code&gt;browser_download_url&lt;/code&gt;&amp;rdquo; qui est l&amp;rsquo;url de téléchargement. Un dernier point, nous utilisons l&amp;rsquo;option &amp;ldquo;&lt;code&gt;-r&lt;/code&gt;&amp;rdquo; pour avoir une sortie brute (raw) afin d&amp;rsquo;éviter que JQ ne mette en forme l&amp;rsquo;url avec des guillemets.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ http $URL | jq -r &#39;.assets[] | select(.name==&amp;quot;scw-linux-amd64&amp;quot;).browser_download_url&#39;
https://github.com/scaleway/scaleway-cli/releases/download/v1.19/scw-linux-amd64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, nous avons l&amp;rsquo;url de téléchargement de la dernière version de &lt;code&gt;scw&lt;/code&gt;, reste à faire un petit script de mise à jour (version simplifiée):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# simple function to get latest release from a repository
get_release() {
	REPO=$1
	NAME=$2
	URL=&amp;quot;https://api.github.com/repos/${REPO}/releases/latest&amp;quot;
	http $URL | jq -r &#39;.assets[] | select(.name==&amp;quot;&#39;$NAME&#39;&amp;quot;).browser_download_url&#39;
}

# update scaleway cli
update_scw() {
	REPO=&amp;quot;/scaleway/scaleway-cli&amp;quot;
	NAME=scw-linux-amd64

	URL=$(get_release &amp;quot;$REPO&amp;quot; &amp;quot;$NAME&amp;quot;)
	wget &amp;quot;$URL&amp;quot; -O ~/.local/bin/scw
}


case $1 in
    scw) update_scw ;
    *) echo &amp;quot;Unknown $1 command to update&amp;quot;;;
esac
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et je peux ensuite créer des fonctions pour chaque clients que je récupère sur github, en ne spécifiant le dépôt, et le nom du binaire, en paramètre de &amp;ldquo;&lt;code&gt;get_release&lt;/code&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Et voilà, de temps en temps je tape la commande: &lt;code&gt;update-cli scw&lt;/code&gt; (ou autre, j&amp;rsquo;en ai quelques-uns dans ma liste), et plus besoin d&amp;rsquo;aller sur la page github, de faire des manipulations, etc.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;La ligne de commande n&amp;rsquo;est pas privée de l&amp;rsquo;intérêt des API REST. Avec httpie ou curl, jq, et l&amp;rsquo;ensemble des outils ancêstraux comme awk, grep, sed&amp;hellip; quelques lignes de commande suffisent à avoir un client bash capable d&amp;rsquo;utiliser les API.&lt;/p&gt;
&lt;p&gt;Il est évident que pour des API plus complexes, un langage de programmation plus adapté (Python, Go, NodeJS&amp;hellip;) va proposer des options plus avancées, mais &lt;code&gt;jq&lt;/code&gt; est assez complet pour faire des manipulations json remarquables.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Fedora, de Docker à Moby</title>
      <link>https://www.metal3d.org/blog/2019/docker-to-moby/</link>
      <pubDate>Sun, 07 Apr 2019 00:51:17 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2019/docker-to-moby/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2019/docker-to-moby/docker-to-moby-cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Si vous êtes utilisateur de Docker sur Fedora 29, vous avez certainement installé le paquet &amp;ldquo;docker&amp;rdquo; des dépôts officiels, ou &amp;ldquo;docker-ce&amp;rdquo; des dépôts Docker. Sauf que les paquets diffèrent drastiquement en terme de version. Alors on va parler de Moby.&lt;/p&gt;
&lt;h1 id=&#34;tldr&#34;&gt;TL;DR&lt;/h1&gt;
&lt;p&gt;Pour résumer, à ceux qui n&amp;rsquo;ont pas envie de lire tout le billet, la version sans marque de Docker-CE existe dans les dépôts officiels de Fedora sous le nom &amp;ldquo;moby-engine&amp;rdquo;. Pour l&amp;rsquo;installer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;sudo systemctl stop docker
sudo dnf remove docker docker-ce
# si vous avez installé les dépôts Docker, vous pouvez les supprimer
# dans le répertoire /etc/yum.repos.d/
# puis:
sudo dnf install moby-engine
sudo systemctl start docker
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et vous voilà avec une version de docker à jour, gérée par la communauté Fedora.&lt;/p&gt;
&lt;h1 id=&#34;moby-&#34;&gt;Moby ?&lt;/h1&gt;
&lt;p&gt;Quand on parle de Docker, on ne sait pas vraiment de quoi on parle&amp;hellip; Parlons-nous du client, des conteneurs, du services, de ses commandes pour démarrer et gérer des conteneurs? On en oublie que Docker est un produit assez large, géré par une entreprise du même nom, qui propose une version &amp;ldquo;CE&amp;rdquo; (Community Edition) et une version &amp;ldquo;Entreprise&amp;rdquo; (EE).&lt;/p&gt;
&lt;p&gt;Sur Fedora, on retrouve une paquet un peu ancien, nommé &amp;ldquo;docker&amp;rdquo;, mais à quoi fait-il référence ?&lt;/p&gt;
&lt;p&gt;Le fait est que certaines distributions tiennent à garder une certaine cohérence en tereme de licence et de respect de la philosophie &amp;ldquo;libre&amp;rdquo;. Donc la préférence va généralement se porter sur la version la plus &amp;ldquo;libre&amp;rdquo; et la moins soumise à des droits pouvant porter préjudice aux projets &amp;ldquo;vraiment libres&amp;rdquo;. Mais alors que peut-il se passer avec Docker ?&lt;/p&gt;
&lt;p&gt;Et bien sur Fedora, ça pose un léger problème. Docker-CE est &amp;ldquo;brandé&amp;rdquo; (terme angliciste qui veut dire, en gros, que nous parlons d&amp;rsquo;une marque, et non d&amp;rsquo;une technologie). En soit ce n&amp;rsquo;est pas si grave, mais dans les fats il existe depuis 2017 une séparation de projet. En effet, il y a &amp;ldquo;Moby&amp;rdquo;, et &amp;ldquo;Docker&amp;rdquo; &amp;ndash; bien que liés, il y a une différence en terme de &amp;ldquo;marque&amp;rdquo;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bon, OK, et du coup c&amp;rsquo;est quoi Moby ?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Moby a été lancé à l&amp;rsquo;initiative de Docker en 2017, c&amp;rsquo;est un projet de &amp;ldquo;kit&amp;rdquo; permettant de construire une solution de conteneurisation standardisée sur laquelle Docker lui-même repose.&lt;/p&gt;
&lt;p&gt;Solomon Hykes, le créateur et gérant du projet Docker en parle dans un de ses tweets:&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;The &lt;a href=&#34;https://twitter.com/moby?ref_src=twsrc%5Etfw&#34;&gt;@moby&lt;/a&gt; Project in a nutshell: inside and outside. &lt;a href=&#34;https://t.co/K8Rn9YYtVs&#34;&gt;pic.twitter.com/K8Rn9YYtVs&lt;/a&gt;&lt;/p&gt;&amp;mdash; Solomon Hykes (@solomonstre) &lt;a href=&#34;https://twitter.com/solomonstre/status/855918630915133440?ref_src=twsrc%5Etfw&#34;&gt;April 22, 2017&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;
&lt;p&gt;On ne va pas en faire des caisses, disons simplement que Moby est la base du projet, et que Docker est un produit constuit à base de Moby. Si je devais vous expliquer tout ce qu&amp;rsquo;impliquent Docker, Moby, et les &amp;ldquo;sous-projets&amp;rdquo; que sont &amp;ldquo;runC&amp;rdquo;, CRI etc&amp;hellip; on y passerait des pages entières, et vous ne serez pas plus avancés.&lt;/p&gt;
&lt;h1 id=&#34;revenons-à-fedora-et-les-paquets-docker&#34;&gt;Revenons à Fedora et les paquets &amp;ldquo;docker&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;Depuis des mois, voir des années, j&amp;rsquo;utilise le paquet &amp;ldquo;docker&amp;rdquo; fourni dans les dépôts Fedora. Et comme mon collègue, Daniel, me l&amp;rsquo;a fait remarqué, ce paquet semble bloqué à la version 1.13 &amp;ndash; alors que la version &amp;ldquo;docker-ce&amp;rdquo; proposé par Docker est en version 1.18 depuis&amp;hellip; des mois.&lt;/p&gt;
&lt;p&gt;Donc, j&amp;rsquo;ai deux choix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;utiliser un paquet officiel Fedora&lt;/li&gt;
&lt;li&gt;utiliser un paquet officiel Docker&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans le premier cas, j&amp;rsquo;évite l&amp;rsquo;installation d&amp;rsquo;un dépôt tiers, mais (vous allez comprendre juste après) je me retrouve avec une version très en retard de Docker. Et dans le second cas, je suis à jour, mais j&amp;rsquo;ai moins confiance. C&amp;rsquo;est un dépôt tiers qui n&amp;rsquo;est pas géré par la communauté &amp;ndash; mais j&amp;rsquo;ai une version à jour. Bref, le choix est rude. Sauf que&amp;hellip;&lt;/p&gt;
&lt;p&gt;En parlant sur IRC avec Kwizart (décidément il est une source d&amp;rsquo;infos importante pour moi), j&amp;rsquo;apprend qu&amp;rsquo;un paquet nommé &amp;ldquo;moby-engine&amp;rdquo; existe sur les dépôts, alors que le paquet &amp;ldquo;docker&amp;rdquo; est en cours d&amp;rsquo;abandon. Et ce paquet, vous l&amp;rsquo;avez compris, part du projet Moby (capt&amp;rsquo;ain Obious merci), et propose une version à jour.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée est qu&amp;rsquo;en partant de Moby, on évite de nommer le paquet avec le nom de la marque, mais on peut tout de même utiliser le mot &amp;ldquo;docker&amp;rdquo; pour les commandes et le service. Ça semble bon.&lt;/p&gt;
&lt;p&gt;Et donc, j&amp;rsquo;ai testé.&lt;/p&gt;
&lt;h1 id=&#34;on-essaye&#34;&gt;On essaye&lt;/h1&gt;
&lt;p&gt;Allez, ça coûte rien d&amp;rsquo;essayer, de toutes manières mes conteneurs sont &amp;ldquo;jetables&amp;rdquo; (c&amp;rsquo;est le but des conteneurs je vous le rappelle) et je peux parfaitement revenir à un état antérieur grâce à &amp;ldquo;dnf history&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;On vire docker, point barre:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;sudo systemctl stop docker
sudo dnf remove docker docker-ce
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, c&amp;rsquo;est fait. Reste à installer &amp;ldquo;Moby-engine&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;sudo dnf install moby-engine
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là, pas de panique, &lt;strong&gt;ce paquet propose les mêmes outils et commandes docker&lt;/strong&gt; &amp;ndash; donc vous allez retrouver le service &amp;ldquo;docker&amp;rdquo; (et &amp;ldquo;dockerd&amp;rdquo;) et la commande du même nom.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;sudo systemctl start docker
docker version
Client:
 Version:           18.06.0-dev
 API version:       1.38
 Go version:        go1.11.5
 Git commit:        0ffa825
 Built:             Mon Feb 11 14:47:59 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          dev
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.11.5
  Git commit:       0ffa825
  Built:            Mon Feb 11 14:47:26 2019
  OS/Arch:          linux/amd64
  Experimental:     false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et oui, la version colle bien à &amp;ldquo;docker-ce&amp;rdquo;, mais je n&amp;rsquo;ai pas eu besoin des dépôts Docker, tout est bien dans les dépôts officiels de Fedora.&lt;/p&gt;
&lt;p&gt;Et voilà une raison de plus qui me fait préférer Fedora aux autres distributions: j&amp;rsquo;évite de prendre des dépôts externes et je reste en confiance.&lt;/p&gt;
&lt;p&gt;Pour information, mes anciens conteneurs fonctionnent toujours, mes images sont encore là, rien n&amp;rsquo;a vraiment bougé à part le fait que je suis mainenant en train d&amp;rsquo;utiliser une version récente de Docker.&lt;/p&gt;
&lt;h1 id=&#34;un-souci-&#34;&gt;Un souci ?&lt;/h1&gt;
&lt;p&gt;Il se peut que vous ayez un souci, honnêtement rien n&amp;rsquo;est jamais parfait, et sur un de mes postes j&amp;rsquo;ai eu un tout petit problème à régler.&lt;/p&gt;
&lt;p&gt;Dans mon cas, je mets à jour Fedora sur un poste depuis la version 25 je crois, et sans réinstallation. Depuis cette date, j&amp;rsquo;ai beaucoup manipulé les installations de Docker et j&amp;rsquo;avais utilisé &amp;ldquo;devicemapper&amp;rdquo; des années auparavent. Puis je suis passé à Overlay2, sans jamais avoir supprimé le répertoire de volumes et conteneurs. Pour la version 1.13 de Docker, il n&amp;rsquo;y avait pas de souci, mais là&amp;hellip; En passant à Moby-Engine, j&amp;rsquo;ai eu une erreur.&lt;/p&gt;
&lt;p&gt;En regardant les logs (via &lt;code&gt;journalctl&lt;/code&gt;), j&amp;rsquo;ai vu qu&amp;rsquo;il n&amp;rsquo;aimait pas le fait d&amp;rsquo;avoir plusieurs possibilités de pilotes de volumes d&amp;rsquo;écritures. Il m&amp;rsquo;a suffit de supprimer le répertoire &lt;code&gt;/var/lib/docker/devicemapper&lt;/code&gt; pour que ça passe.&lt;/p&gt;
&lt;p&gt;Si vous avez des soucis, je serai heureux que vous commentiez et j&amp;rsquo;ajouterai les solutions dans ce billet.&lt;/p&gt;
&lt;h1 id=&#34;pour-conlure&#34;&gt;Pour conlure&lt;/h1&gt;
&lt;p&gt;Je n&amp;rsquo;ai rien contre l&amp;rsquo;entreprise Docker, bien au contraire je respecte le travail de fait et leur ouverture à un produit qui est, aujourd&amp;rsquo;hui, majeur. Je ne cherchais pas à m&amp;rsquo;écarter de l&amp;rsquo;entreprise mais je voulais vraiment avoir un paquet récent de Docker depuis les dépôts Fedora. Je comprends que le nom du paquet puisse limiter sa reconnaissance (et c&amp;rsquo;est pour cela que j&amp;rsquo;en parle dans ce billet) &amp;ndash; mais si vous êtes utilisateur de Fedora, je vous conseille de passer à &amp;ldquo;moby-engine&amp;rdquo;. Cela ne vous changera rien en soit, ce sera bien Docker-CE, vous aurez les mêmes outils, etc. Mais &lt;strong&gt;sans utiliser un dépôt tiers, et surtout en restant à jour.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le projet Moby est une initiative qui me plait énormément. J&amp;rsquo;applaudie Docker d&amp;rsquo;en être l&amp;rsquo;initiateur et de respecter l&amp;rsquo;ouverture d&amp;rsquo;esprit propre à la philosophie du libre.&lt;/p&gt;
&lt;p&gt;Pour en savoir plus sur Moby:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/moby/moby&#34;&gt;https://github.com/moby/moby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mobyproject.org/&#34;&gt;https://mobyproject.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://twitter.com/moby&#34;&gt;https://twitter.com/moby&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </description>
    </item>
    
    <item>
      <title>Cuda et Tensorflow sur Fedora 29</title>
      <link>https://www.metal3d.org/blog/2019/cuda-tf/</link>
      <pubDate>Sat, 06 Apr 2019 15:33:25 +0200</pubDate>
      
      <guid>https://www.metal3d.org/blog/2019/cuda-tf/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;L&amp;rsquo;un des points sensibles quand on veut travailler en machine learning avec TensorFlow, c&amp;rsquo;est l&amp;rsquo;installation et la gestion de version de CUDA - voici la marche à suivre que j&amp;rsquo;utilise depuis quelques temps et qui me permet de faire les choses bien.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;utilise Fedora 29, et il fait bien l&amp;rsquo;avouer, l&amp;rsquo;installation de CUDA et des libs CuDNN sont franchement pas géniales si on se réfère à la documentation Nvidia. La récupération de la bonne version CUDA et le fait de devoir s&amp;rsquo;inscrire pour récuperer un tarball pour la librarie CuDNN est pas l&amp;rsquo;idée que je préfère&amp;hellip; Bref !&lt;/p&gt;
&lt;p&gt;Au détour d&amp;rsquo;une conversation sur le canal fedora-fr (IRC, sur freenode), Nicola Chanvet alias Kwizart me parle d&amp;rsquo;une section de RPM Fusion qui fait mon bonheur. Alors allons-y, lançons nous dans une installation propre de ce qu&amp;rsquo;il faut pour avoir les pilotes Nvidia, CUDAO 10, et les libs CuDNN nécessaires pour Tensorflow, mais aussi pour faire des rendus Blender rapides, ou encore jouer dans de bonnes conditions.&lt;/p&gt;
&lt;h1 id=&#34;étape-1-on-dégage-les-vieilles-libs-pourries&#34;&gt;Étape 1, on dégage les vieilles libs pourries&lt;/h1&gt;
&lt;p&gt;C&amp;rsquo;est la phase la plus ennuyeuse, à faire si vous avez déjà installé des paqutes. Voilà comment j&amp;rsquo;ai fait:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;# on vire cuda
sudo dnf remove cuda
sudo dnf remove cuda-*
sudo dnf remove $(sudo dnf list installed cuda*)
sudo dnf remove $(sudo package-cleanup --leaves | grep cuda)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, ça c&amp;rsquo;est fait.&lt;/p&gt;
&lt;p&gt;Reste à nettoyer les déchets:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;sudo rm -rf /usr/local/cuda /usr/local/cuda*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ça fait du bien hein :)&lt;/p&gt;
&lt;h1 id=&#34;étape-2-ou-1-pour-certains-on-installe-les-bon-dépôts-les-pilotes-etc&#34;&gt;Étape 2 (ou 1 pour certains) on installe les bon dépôts, les pilotes, etc&amp;hellip;&lt;/h1&gt;
&lt;h2 id=&#34;si-vous-navez-pas-encore-rpm-fusion&#34;&gt;Si vous n&amp;rsquo;avez pas encore RPM Fusion&lt;/h2&gt;
&lt;p&gt;Maintenant que vous avez nettoyé un peu, on peut passer à une installation cool.&lt;/p&gt;
&lt;p&gt;Si ce n&amp;rsquo;est pas déjà fait, vous allez installé les dépôt RPM Fusion:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;sudo dnf install \
   https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \
   https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;installer-le-pilote-nvidia&#34;&gt;Installer le pilote nvidia&lt;/h2&gt;
&lt;p&gt;Si vous ne l&amp;rsquo;avez pas encore fait, on peut maintenant installer le pilote &amp;ldquo;nvidia&amp;rdquo; (propriétaire, c&amp;rsquo;est moche, mais pas le choix).&lt;/p&gt;
&lt;p&gt;Avec ces dépôt, on peut installer les pilotes nvidia. Passez par &lt;code&gt;akmod&lt;/code&gt; au lieu de &lt;code&gt;kmod&lt;/code&gt; pour assurer une compilation du module lors des mises à jour du kernel, c&amp;rsquo;est selon moi bien plus sécurisant.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;sudo dnf makecache
sudo dnf install akmod-nvidia
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Là je vous conseille de redémarrer l&amp;rsquo;ordinateur.&lt;/p&gt;
&lt;p&gt;Maintenant que le pilote nvidia est là, vous pouvez vérifier facilement si la carte est bien reconnue en tapant &lt;code&gt;nvidia-smi&lt;/code&gt; dans un terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.56       Driver Version: 418.56       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 106...  Off  | 00000000:02:00.0  On |                  N/A |
|  0%   42C    P8     5W / 120W |    111MiB /  3019MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      1795      G   /usr/libexec/Xorg                             39MiB |
|    0      1946      G   /usr/bin/gnome-shell                          69MiB |
+-----------------------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bien, tout est là. On passe à CUDA et cuDNN.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Notez bien la version de CUDA, on va en reparler&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;installer-cuda-et-cudnn&#34;&gt;Installer CUDA et cuDNN&lt;/h2&gt;
&lt;p&gt;C&amp;rsquo;est maintenant que vous allez installer les paquets qui changent tout pour TensorFlow, Blender, et certains jeux.&lt;/p&gt;
&lt;p&gt;La documentation du site nvidia est vraiment pas claire, alors que dans les faits&amp;hellip; c&amp;rsquo;est tellement plus simple de passer par une simple ligne d&amp;rsquo;ajout de dépôt à l&amp;rsquo;ancienne.&lt;/p&gt;
&lt;p&gt;En se basant sur la page &lt;a href=&#34;https://rpmfusion.org/Howto/CUDA#Machine_Learning_repository&#34;&gt;RPM Fusion, section &amp;ldquo;Machine Learning&amp;rdquo;&lt;/a&gt; comme me l&amp;rsquo;a indiqué Kwizart:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;# on installe les dépôts cuda et machine learning de nvidia
sudo dnf install https://developer.download.nvidia.com/compute/cuda/repos/fedora29/x86_64/cuda-repo-fedora29-10.1.105-1.x86_64.rpm
sudo dnf install https://developer.download.nvidia.com/compute/machine-learning/repos/rhel7/x86_64/nvidia-machine-learning-repo-rhel7-1.0.0-1.x86_64.rpm

# puis les paquets:
sudo dnf install cuda
sudo dnf install libcudnn7 libcudnn7-devel libnccl libnccl-devel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je vous préviens, CUDA prend presque 3.8Go sur votre disque, donc soyez prêt à attendre si vous avez une connexion internet compliquée. Mais en soit, enfin &lt;strong&gt;on évite une inscription sur le site Nvidia pour récupérer la lib CuDNN&lt;/strong&gt; !&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et là, ne cherchez pas plus loin, on va avoir un problème&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ce qui va vous faire un peu mal&amp;hellip; c&amp;rsquo;est que, à l&amp;rsquo;heure où j&amp;rsquo;écris ces lignes, TensorFlow 1.13 n&amp;rsquo;est pas compatible avec CUDA 10.1, mais CUDA 10.0 &amp;ndash; et pour parfaire la crise, il faut aller récupérer le dépôt nvidia pour&amp;hellip; Fedora 27. Sympa hein ?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En définitive, il faut installer une version antérieure de CUDA pour assurer la compatibilité avec TensorFlow&amp;hellip; régulièrement&amp;hellip;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Le plus simple, c&amp;rsquo;est de créer un fichier de dépôt à partir du premier, mais comme je voulais aussi changer le nom&amp;hellip; voilà le fichier à créer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;sudo tee /etc/yum.repos.d/cuda-f27.repo &amp;lt;&amp;lt; EOF
[cuda-old]
name=cuda-old
baseurl=http://developer.download.nvidia.com/compute/cuda/repos/fedora27/x86_64
enabled=1
gpgcheck=1
gpgkey=http://developer.download.nvidia.com/compute/cuda/repos/fedora27/x86_64/7fa2af80.pub
EOF

sudo dnf makecache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et d&amp;rsquo;installer la version antérieure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;sudo dnf install cuda-10-0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc, à patir de maintenant, vous avez deux versions de CUDA sur votre poste, une 10.0 et une 10.1. Heureusement pour nous, CUDA s&amp;rsquo;installe dans &lt;code&gt;/usr/local/&lt;/code&gt;, et sépare les versions en tant que telle en nommant les répertoires. Et c&amp;rsquo;est un lien sympbolique de &lt;code&gt;/usr/loca/cuda&lt;/code&gt; sur un des répertoires qui active la version &amp;ldquo;par défaut&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Cela veut dire que vous avez deux solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;soit vous forcez la variable &lt;code&gt;LD_LIBRARY_PATH=/usr/local/cuda-10.0&lt;/code&gt; &lt;strong&gt;avant&lt;/strong&gt; de démarrer un shell python, jupyter ou ce que vous voulez&amp;hellip;&lt;/li&gt;
&lt;li&gt;soit vous modifiez le lien sympbolique:
&lt;pre&gt;&lt;code&gt;ln -sf /usr/local/cuda-10.0 /usr/local/cuda
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans le second cas, vous allez donc changer la version par défaut pour tout le système. En soit ce n&amp;rsquo;est pas très grave, mais il faut le savoir.&lt;/p&gt;
&lt;h2 id=&#34;testons&#34;&gt;Testons&amp;hellip;&lt;/h2&gt;
&lt;p&gt;C&amp;rsquo;est bien beau d&amp;rsquo;avoir installé CUDA et CuDNN, mais il va falloir au moins vérifier que tout fonctionne. Ce que je vous propose est une simple session TensorFlow qui doit soit vous péter une erreur limite insultante, soit vous donner le sourire.&lt;/p&gt;
&lt;p&gt;La méthode la plus facile que j&amp;rsquo;ai à vous proposer est la suivante:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;python3 -mvenv /tmp/tftest
source /tmp/tftest/bin/activate
pip3 install -U tensorflow-gpu
python3
&amp;gt;&amp;gt;&amp;gt; import tensorflow as tf
&amp;gt;&amp;gt;&amp;gt; tf.Session()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous devez alors voir une ligne dire que le GPU est présent et utilisable, chez moi ça donne quelque chose du genre (en supprimant des lignes qui ne vous intéressent pas):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;2019-04-06 16:48:16.370115: I tensorflow/compiler/xla/service/service.cc:158]   StreamExecutor device (0): &amp;lt;undefined&amp;gt;, &amp;lt;undefined&amp;gt;
...

2019-04-06 16:48:16.635396: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1433] Found device 0 with properties: 
name: GeForce GTX 1060 3GB major: 6 minor: 1 memoryClockRate(GHz): 1.7085
pciBusID: 0000:02:00.0
totalMemory: 2.95GiB freeMemory: 2.77GiB
...
2019-04-06 16:48:16.635434: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1512] Adding visible gpu devices: 0
2019-04-06 16:48:16.637520: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1115] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 2541 MB memory) -&amp;gt; physical GPU (device: 0, name: GeForce GTX 1060 3GB, pci bus id: 0000:02:00.0, compute capability: 6.1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela signifie que mon GPU est bien reconnu et utilisable par TensorFlow, c&amp;rsquo;est donc gagné !&lt;/p&gt;
&lt;h1 id=&#34;pour-la-suite&#34;&gt;Pour la suite&lt;/h1&gt;
&lt;p&gt;Lorsque TensorFlow sera enfin à jour pour CUDA 10.1, vous pourrez supprimer le paquet &lt;code&gt;cuda-10-0&lt;/code&gt; et le fichier &lt;code&gt;/etc/yum.repos.d/cuda-f27.repo&lt;/code&gt;, puis remettre d&amp;rsquo;aplomb la base de packages: &lt;code&gt;sudo dnf makecache&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;TensorFlow est effectivmement un peu trop sensible à mon goût en ce qui concerne la version de CUDA. Cela étant dit, quand on connait un peu Fedora et la gestion de dépôts et de paquets, arriver à fire cohabiter deux versions de CUDA reste possible. Je dois bien l&amp;rsquo;admettre, c&amp;rsquo;est un peu trivial, mais je pense avoir donné les infos suffisantes ici pour arriver à travailler sans trop de souci.&lt;/p&gt;
&lt;p&gt;En ce qui concerne CuDNN, vous pouvez vous en passer, l&amp;rsquo;utilisation de conda permet de soulager un peu le travail. Voici un exemple d&amp;rsquo;environnement conda qui vous permttra de faire du tensorflow sans trop de mal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell-session&#34;&gt;# installez conda depuis les paquets
sudo dnf install conda

# créez un environnement nommé tf avec tensorflow-gpu
conda new --name tf tensorflow-gpu

# activez l&#39;environnement
conda activate tf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&amp;rsquo;intérêt ici est que conda fourni la librairie cuDNN et semble ne pas avoir de souci de gestion de versions. Cela évite donc l&amp;rsquo;installation d&amp;rsquo;une série de RPM qui restent dans le système et de séparer par conséquent ces librairies dans un environnement spécifique. Personnellement je suis un gros consommateur de solutions de séparation d&amp;rsquo;environnement (cf. VirtualEnv, conda, Docker&amp;hellip;) - j&amp;rsquo;évite &lt;strong&gt;le plus souvent possible&lt;/strong&gt; d&amp;rsquo;avoir à modifier des fichiers dans les répertoire système.&lt;/p&gt;
&lt;p&gt;À noter que, oui, je suis au courant de l&amp;rsquo;existence de &amp;ldquo;&lt;a href=&#34;https://github.com/NVIDIA/nvidia-docker&#34;&gt;nvidia-docker&lt;/a&gt;&amp;rdquo; mais que je suis en train de tenter de passer de docker 1.13 (des dépôts Fedora) qui (selon ce qu&amp;rsquo;on me dit) n&amp;rsquo;est plus vraiment supporté, contre &amp;ldquo;moby-engine&amp;rdquo; qui est la version Docker &amp;ldquo;communautaire sans marque&amp;rdquo;. Je n&amp;rsquo;ai pas encore eut le temps de me pencher sur le sujet, mais ça va vite arriver.&lt;/p&gt;
&lt;p&gt;Dans un prochain article, je vous montrerai comment créer un environnement &amp;ldquo;jupyter&amp;rdquo; capable de &amp;ldquo;voir&amp;rdquo; les autres environnements conda et par conséquent d&amp;rsquo;éviter la redondance d&amp;rsquo;installation de jupyeter pour les environnements séparés.&lt;/p&gt;
&lt;p&gt;Mais pour l&amp;rsquo;heure, nous avons au moins installé les pilotes, conda en deux versions, et le tout sans tout casser dans notre système.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Me contacter</title>
      <link>https://www.metal3d.org/contact/</link>
      <pubDate>Thu, 14 Feb 2019 18:53:28 +0100</pubDate>
      
      <guid>https://www.metal3d.org/contact/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Évidemment, je vais éviter de donner mon mail personnel ici, j&amp;rsquo;en ai un peu marre du spam,
mais vous pouvez le deviner. C&amp;rsquo;est &amp;ldquo;Metal3d&amp;rdquo; sur le service mail de Google.&lt;/p&gt;
&lt;p&gt;Sinon, je m&amp;rsquo;exprime sur Mastondo &lt;a href=&#34;https://techlover.eu/@metal3d&#34;&gt;https://techlover.eu/@metal3d&lt;/a&gt;. Si vous ne connaissez pas cet
équivalent plus sain à Twitter, j&amp;rsquo;ai écrit un truc ici : &lt;a href=&#34;https://www.metal3d.org/blog/2020/je-pars-de-twitter-et-je-rejoins-mastodon/&#34;&gt;Je quitte Twitter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Inutile de tenter Facebook, même si vous m&amp;rsquo;y trouverez, je n&amp;rsquo;y vais jamais (pas plus d&amp;rsquo;une ou deux fois par an).&lt;/p&gt;
&lt;p&gt;Et enfin, vous me trouverez certainement de temps à autre sur IRC, sur le canal &amp;ldquo;#fedora-fr&amp;rdquo; sur les
serveurs freenode. Sur discord aussi (Metal3d#9371), et certainement à droite à gauche sur des forums
Blender, Linux, et je ne sais quel autre service.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Qui c&#39;est ?</title>
      <link>https://www.metal3d.org/about/</link>
      <pubDate>Thu, 14 Feb 2019 18:53:28 +0100</pubDate>
      
      <guid>https://www.metal3d.org/about/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;h2 id=&#34;il-est-de-retour&#34;&gt;Il&amp;hellip; est de retour&lt;/h2&gt;
&lt;p&gt;Le retour du blog m&amp;rsquo;sieurs dames !&lt;/p&gt;
&lt;p&gt;Tout d&amp;rsquo;abord, un petit message sur le &amp;ldquo;retour&amp;rdquo; du blog. Ensuite, un coup de gueule face à des critiques qu&amp;rsquo;on me balance sur l&amp;rsquo;écriture inclusive que je n&amp;rsquo;utilise pas,
et enfin, je me présente.&lt;/p&gt;
&lt;p&gt;Il y a quelques mois, j&amp;rsquo;ai abandonné ce blog (mais je suis revenu hein).&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;ancien blog était développé en Go (comme aujourd&amp;rsquo;hui dans les faits), car je créais un petit framework rapide pour monter ce
genre de site. À côté de cela, j&amp;rsquo;avais monté des outils pour travailler et que je léguais à la communauté. Je précise, je donnais les
sources sous licence libre, mais aussi, j&amp;rsquo;hébergeais ces solutions :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un service PlantUML avec éditeur en ligne + partage&lt;/li&gt;
&lt;li&gt;un émulateur assembleur que pas mal de professeurs d&amp;rsquo;informatique utilisaient (je le sais par leurs messages de remerciement, j&amp;rsquo;étais très heureux)&lt;/li&gt;
&lt;li&gt;un outil de filtrage Bayesien (détection de commentaire spam via une API que je fournissais gratuitement)&lt;/li&gt;
&lt;li&gt;et 5 ou 6 autres trucs, plutôt bien utilisés&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bilan ⇒ Presque 3 000 connexions par jour sur ce serveur.&lt;/p&gt;
&lt;p&gt;Comprenez bien que j&amp;rsquo;y ai passé &lt;strong&gt;des heures&lt;/strong&gt;, que ce serveur n&amp;rsquo;était pas gratuit pour moi, et que je l&amp;rsquo;avais fait pour proposer
des choses qui pouvaient être hébergées par tout à chacun. Jamais je n&amp;rsquo;ai empêché qui que ce soit d&amp;rsquo;utiliser les sources,
l&amp;rsquo;outil ou de le modifier, c&amp;rsquo;est contre ma philosophie.&lt;/p&gt;
&lt;p&gt;Mais soyons clairs, j&amp;rsquo;avais donc besoin d&amp;rsquo;un serveur assez robuste, qui me coûtait assez cher.&lt;/p&gt;
&lt;p&gt;À un moment donné, ça me revenait assez cher, trop cher. Avec les visites sur mon blog, j&amp;rsquo;ai décidé de poser un seul encart de pub,
en bas de page, pour essayer de rentrer dans mes frais. Et ça n&amp;rsquo;a pas marché… Vous le sentez venir le coupable ?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Et oui, AdBlock…&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;De ce fait, j&amp;rsquo;affiche un message si je détecte le bloqueur pour demander de débloquer ce blog, que je ne cherche pas à polluer
la page, que ça reste discret etc. Et j&amp;rsquo;ai été&amp;hellip; comment dire&amp;hellip; déçu par les réponses. Parce que, pour commenter un billet,
il n&amp;rsquo;y a pas grand monde (je n&amp;rsquo;en veux à personne, je commente peu aussi), mais pour insulter l&amp;rsquo;auteur qui cherche à monétiser son site,
là, on prend bien son temps !&lt;/p&gt;
&lt;p&gt;Mails d&amp;rsquo;insultes, commentaires désobligeants, critique acerbes, tout… Voilà, j&amp;rsquo;en ai eu marre de modérer ces messages, de mettre des
filtres dans ma boite mail pour dégager les insultes… J&amp;rsquo;ai tout arrêté.&lt;/p&gt;
&lt;p&gt;Aujourd&amp;rsquo;hui, j&amp;rsquo;ai de nouveau envie. Je vais tenter une autre approche : faites un don de calcul (regardez sur la colonne de gauche).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je suis étonné, mais ça fonctionne, j&amp;rsquo;ai régulièrement quelques micro-XMR qui arrivent sur mon portefeuille de crypto, donc &lt;strong&gt;merci à vous !&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;écriture-inclusive-&#34;&gt;Écriture inclusive ?&lt;/h2&gt;
&lt;p&gt;Franchement, je n&amp;rsquo;ai pas le temps de reprendre mon contenu, et de toutes manières : je n&amp;rsquo;aime pas cette écriture.
Elle impose trop de contraintes, et je sais que le jour où je vais oublier un mot à mettre dans les deux genres, on va me tomber dessus. Donc autant être clair :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;J&amp;rsquo;utilise la forme &amp;ldquo;conventionnelle&amp;rdquo; (avec tout plein de fautes, je sais !!!)&lt;/li&gt;
&lt;li&gt;Je vous demande de prendre les termes masculins comme étant &lt;strong&gt;&amp;ldquo;neutres&amp;rdquo;.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je défends bec et ongle la cause féminine, je défends l&amp;rsquo;égalité, je respecte tout le monde de la même manière. Ne pas utiliser l&amp;rsquo;écriture
inclusive &lt;strong&gt;ne veut en aucun cas dire que je défends un discours patriarcal ou que je me fous des femmes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Partez du principe que, de base, je parle à tout le monde avec respect, que je ne différencie pas les capacités des lecteurs
(et donc lectrices, je précise une bonne fois pour toutes) en fonction de leur genre.
Quelle que soit la grammaire. Si par contre un de mes articles vous semble misogyne, n&amp;rsquo;hésitez pas à me le faire remarquer, je corrigerai au plus vite.&lt;/p&gt;
&lt;h2 id=&#34;à-propos-de-moi&#34;&gt;À propos de moi&lt;/h2&gt;
&lt;p&gt;Je vais enfin parler un peu de moi, rapidement, que vous sachiez qui est l&amp;rsquo;auteur du blog que vous lisez.&lt;/p&gt;
&lt;p&gt;Mon nom est Patrice Ferlet, je suis &amp;ldquo;ingénieur en informatique&amp;rdquo;, c&amp;rsquo;est large, ce n&amp;rsquo;est pas précis, c&amp;rsquo;est même trompeur. Bref, je travaille depuis l&amp;rsquo;an 2001 dans le domaine de l&amp;rsquo;informatique.&lt;/p&gt;
&lt;p&gt;Après un périple dans plusieurs entreprises, j&amp;rsquo;ai fini par me poser dans une boite qui me correspondait bien.&lt;/p&gt;
&lt;h3 id=&#34;mes-débuts&#34;&gt;Mes débuts&lt;/h3&gt;
&lt;p&gt;J&amp;rsquo;ai en réalité commencé ma carrière comme développeur PHP, j&amp;rsquo;ai beaucoup travaillé sur Copix (un des tout premier framework PHP à l&amp;rsquo;époque, et franchement il
marchait bien) dont je faisais partie de l&amp;rsquo;équipe de développement. Mais voilà… Nous avons pris notre envol sur d&amp;rsquo;autres sujets, nous
avons tous changé de boite plusieurs fois, nous nous sommes même perdus de vue. Dommage. Mais…&lt;/p&gt;
&lt;p&gt;Mais finalement, nous avons aussi tous pris des directions passionnantes. Pour ma part, j&amp;rsquo;ai clairement orienté mon métier vers une discipline qui me correspondait bien plus, à savoir l&amp;rsquo;architecture logiciel.&lt;/p&gt;
&lt;p&gt;Doucement j&amp;rsquo;ai basculé sur d&amp;rsquo;autres sujets.&lt;/p&gt;
&lt;p&gt;À force, j&amp;rsquo;ai surtout travaillé en Python, puis en Go, et j&amp;rsquo;ai ajouté dans mes bagages Docker, ensuite Kubernetes, et OpenShift
(devenu OKD en version communautaire). Ces sujets intéressaient mon ancien directeur, une chance pour moi, il me propose alors de rejoindre
l&amp;rsquo;équipe R&amp;amp;D pour travailler sur ces sujets.&lt;/p&gt;
&lt;p&gt;Et une chose en entrainant une autre, j&amp;rsquo;ai mis les mains dans le Machine Learning (Keras, TensorFlow&amp;hellip;), mais aussi dans
Angular (rien à voir hein), Java, Ruby, Gstreamer, OpenCV&amp;hellip; et même dans le WebGL&lt;/p&gt;
&lt;h3 id=&#34;les-années-smile&#34;&gt;Les années &amp;ldquo;Smile&amp;rdquo;&lt;/h3&gt;
&lt;p&gt;Je travaillais depuis (ho la vache&amp;hellip;) 2008 chez &lt;a href=&#34;https://smile.fr&#34;&gt;Smile&lt;/a&gt;. J&amp;rsquo;y travaillais, entre autre, dans le pôle de
&amp;ldquo;Direction de l&amp;rsquo;innovation&amp;rdquo; - mes sujets de prédilections sont à ce jour le DevOps et le Machine Learning (TensorFlow, Keras, Scikit-learn&amp;hellip; oui, tout en Python).
Mais à la base, il faut être précis : &lt;em&gt;j&amp;rsquo;étais développeur Web&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est au fil des ans que j&amp;rsquo;ai pris des responsabilités, que j&amp;rsquo;ai monté des équipes, que j&amp;rsquo;ai formé des gens, que j&amp;rsquo;ai
partagé mes connaissances. Et que j&amp;rsquo;ai appris des autres, que j&amp;rsquo;ai appris des erreurs, que j&amp;rsquo;ai appris à ne pas juger, que j&amp;rsquo;ai appris à écouter, que j&amp;rsquo;ai appris à comprendre.&lt;/p&gt;
&lt;p&gt;Les choses en entrainant d&amp;rsquo;autres, j&amp;rsquo;ai fini par être &amp;ldquo;Lead Tech&amp;rdquo; dans mon pôle, et j&amp;rsquo;ai pris des responsabilités sur des sujets. Et clairement, de &amp;ldquo;simple développeur&amp;rdquo; (et ne croyez pas que je dénigre ce métier, c&amp;rsquo;est un métier passionnant), j&amp;rsquo;ai fini par faire bien plus de choses que prévues.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je suis donc, à ce jour, ce que l&amp;rsquo;on appelle un &amp;ldquo;Lead Tech&amp;rdquo;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;laventure-klee-qui-commence&#34;&gt;L&amp;rsquo;aventure &amp;ldquo;Klee&amp;rdquo; qui commence&lt;/h3&gt;
&lt;p&gt;Et aujourd&amp;rsquo;hui, (depuis 2023), c&amp;rsquo;est chez &lt;a href=&#34;https://www.klee-group.com&#34;&gt;Klee&lt;/a&gt; que je travaille, toujours sur des sujets passionnants, en qualité de &amp;ldquo;Lead Tech, DevOps&amp;rdquo;. Mon rôle est
toujours de faire en sorte que les équipes techniques puissent travailler dans les meilleures conditions possibles, et que les projets soient menés à bien.
J&amp;rsquo;apporte donc mon lot de connaissances sur Linux, la conteneurisation, les langages que je maitrise, et je m&amp;rsquo;efforce de rester à jour sur les nouvelles technologies.&lt;/p&gt;
&lt;h3 id=&#34;autres-sujets&#34;&gt;Autres sujets&lt;/h3&gt;
&lt;p&gt;À côté du taff, c&amp;rsquo;est Blender qui me prend pas mal de temps &lt;span class=&#34;emoji&#34;&gt;🤘&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;En gros, ce métier n&amp;rsquo;est pas un labeur à mes yeux. C&amp;rsquo;est une passion dévorante, je ne vois souvent pas les heures passer et
je déborde pas mal sur mes nuits pour lire, tester, comprendre.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>KubeSpray, installer kubernetes et créer un utilisateur admin en quelques minutes</title>
      <link>https://www.metal3d.org/blog/2017/kubespray-installer-kubernetes-et-cr%C3%A9er-un-utilisateur-admin-en-quelques-minutes/</link>
      <pubDate>Mon, 04 Dec 2017 07:07:56 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2017/kubespray-installer-kubernetes-et-cr%C3%A9er-un-utilisateur-admin-en-quelques-minutes/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2017/kubespray-installer-kubernetes-et-cr%C3%A9er-un-utilisateur-admin-en-quelques-minutes/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Si vous voulez vous mettre à Kubernetes, sur des machines physiques ou virtuelles, il serait bon de lire ce petit article pour ne pas vous trouver en état de panique après quelques minutes. Essayons de faire les choses simplement.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://kubernetes.io/&#34;&gt;Kubernetes&lt;/a&gt; (développé par Google) est un orchestrateur de conteneurs qui permet une maitrise poussée du fonctionnement. Il fonctionne presque partout, apporte une normalisation des déploiements, une gestion des rôles poussés, etc&amp;hellip; Google a vraiment bien faut le boulot et a proposé cet outil sous licence libre.&lt;/p&gt;
&lt;p&gt;Red Hat a d&amp;rsquo;ailleurs refondu &lt;a href=&#34;https://www.openshift.com/&#34;&gt;OpenShift&lt;/a&gt; en surcouche de la solution, ce qui induit que si vous comprennez Kubernetes alors OpenShift vous semblera très familier après coup. Les objets Kubernetes sont d&amp;rsquo;ailleurs utilisables dans OpenShift.&lt;/p&gt;
&lt;p&gt;Bref, l&amp;rsquo;installation de Kubernetes est un sujet qui peut s&amp;rsquo;avérer un peu compliqué si on s&amp;rsquo;y prend mal, alors que la réalité est toute autre. Nous allons utiliser un projet nommé &amp;ldquo;kubespary&amp;rdquo; qui utilise Ansible pour le provionnement de machine.&lt;/p&gt;
&lt;p&gt;Ensuite, on passera au paramètrage de notre client &amp;ldquo;kubectl&amp;rdquo; pour arriver à utiliser le dashboard depuis notre ordinateur.&lt;/p&gt;
&lt;h1 id=&#34;préparation-de-lenvironnemnt&#34;&gt;Préparation de l&amp;rsquo;environnemnt&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Il vous faut 3 machines (VMs, ou phyique) sur lesquelles vous pouvez vous connecter en ssh avec une paire de clefs.&lt;/li&gt;
&lt;li&gt;Il vous faut Ansible version 2.4 (c&amp;rsquo;est ce que j&amp;rsquo;ai, ça marche peut-être avec une version antérieure).&lt;/li&gt;
&lt;li&gt;Et on aura besoin de kubectl sur le poste client&lt;/li&gt;
&lt;li&gt;Et enfin, il faut la commande &amp;ldquo;pip&amp;rdquo; (paquet python-pip sur Fedora) et que votre PATH ait &amp;ldquo;~/.local/bin&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Faites en sorte que le répertoire &lt;code&gt;~/.local/bin&lt;/code&gt; soit dans votre PATH, ce sera plus simple&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;téléchargement-de-kubectl&#34;&gt;Téléchargement de kubectl&lt;/h1&gt;
&lt;p&gt;Là c&amp;rsquo;est simple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
$ chmod +x kubectl
$ mv kubectl ~/.local/bin
$ kubectl version
Client Version: version.Info{Major:&amp;quot;1&amp;quot;, Minor:&amp;quot;8&amp;quot;, GitVersion:&amp;quot;v1.8.4&amp;quot;, GitCommit:&amp;quot;9befc2b8928a9426501d3bf62f72849d5cbcd5a3&amp;quot;, GitTreeState:&amp;quot;clean&amp;quot;, BuildDate:&amp;quot;2017-11-20T05:28:34Z&amp;quot;, GoVersion:&amp;quot;go1.8.3&amp;quot;, Compiler:&amp;quot;gc&amp;quot;, Platform:&amp;quot;linux/amd64&amp;quot;}
Server Version: version.Info{Major:&amp;quot;1&amp;quot;, Minor:&amp;quot;8&amp;quot;, GitVersion:&amp;quot;v1.8.3+coreos.0&amp;quot;, GitCommit:&amp;quot;f96beb2e925b0bb4e6ac0de746c7155dcd8fdc10&amp;quot;, GitTreeState:&amp;quot;clean&amp;quot;, BuildDate:&amp;quot;2017-11-13T10:18:37Z&amp;quot;, GoVersion:&amp;quot;go1.8.3&amp;quot;, Compiler:&amp;quot;gc&amp;quot;, Platform:&amp;quot;linux/amd64&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Un truc pratique à savoir, kubectl permet la completion dans Bash, ce que vous pouvez faire c&amp;rsquo;est d&amp;rsquo;ajouter la commande suivante dans votre fichier &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;source &amp;lt;(kubectl completion bash)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez aussi taper cette commande dans votre session bash actuelle por l&amp;rsquo;activer de suite.&lt;/p&gt;
&lt;h1 id=&#34;installation-de-kubspray&#34;&gt;Installation de kubspray&lt;/h1&gt;
&lt;p&gt;Le projet est sur la page github: &lt;a href=&#34;https://github.com/kubespray/kubespray-cli&#34;&gt;https://github.com/kubespray/kubespray-cli&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On commence par installer kubespray-cli - je préfère toujours utiliser &amp;ldquo;pip&amp;rdquo; en mode &amp;ldquo;user&amp;rdquo; pour que l&amp;rsquo;installation soit dans mon &amp;ldquo;home&amp;rdquo; (holalal que du franglais). C&amp;rsquo;est simplement que n&amp;rsquo;aime pas que mon utilisateur installe des trucs qui ne sont pas la distribution Linux que j&amp;rsquo;utilise, ailleurs dans le système. Et &amp;ldquo;pip&amp;rdquo; sait faire ça:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install --user kubespray
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si vous avez bien &lt;code&gt;~/.local/bin&lt;/code&gt; dans votre &amp;ldquo;PATH&amp;rdquo;, tout va bien se passer.&lt;/p&gt;
&lt;p&gt;Maintenant, on va créer un inventaire Ansible. Vous pouvez le faire manuellement mais kubespray sait le créer pour vous:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubespray-cli prepare --nodes node1 node2 node3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vérifiez ensuite l&amp;rsquo;inventaire que vous allez certainement vouloir modifier. Il se trouve à l&amp;rsquo;emplacement &lt;code&gt;~/.kubespray/inventory/inventory.cfg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Voici le mien:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;[kube-master]
origin-m1		
origin-m2		
origin-m3		

[all:vars]
ansible_user=root

[all]
origin-m1
origin-m2
origin-m3

[k8s-cluster:children]
kube-node		
kube-master		

[kube-node]
origin-m1		
origin-m2		
origin-m3		

[etcd]
origin-m1		
origin-m2		
origin-m3

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, j&amp;rsquo;ai donc 3 machines qui sont toutes master, node et noeud etcd.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai ajouté la partie &amp;ldquo;&lt;code&gt;[all:vars]&lt;/code&gt;&amp;rdquo; pour forcer l&amp;rsquo;utilsateur (root, c&amp;rsquo;est mal&amp;hellip;), et notez aussi que vous pouvez ajouter des variables à chaque noeud du groupe &amp;ldquo;all&amp;rdquo; pour, par exemple, donner l&amp;rsquo;adresse IP de la machine cible (eg. &lt;code&gt;origin-m1    ansible_ssh_host=xx.xx.xx.xx&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Bref, il faut quand même respecter deux ou trois choses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;etcd doit avoir 3 machines, c&amp;rsquo;est nécessaire pour le concensus&lt;/li&gt;
&lt;li&gt;avoir au moins un node et un master&lt;/li&gt;
&lt;li&gt;de l&amp;rsquo;aspirine si ça marche pas&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On teste si tout est ok:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ansible -i ~/.kubespray/inventory/inventory.cfg all -m ping
origin-m2 | SUCCESS =&amp;gt; {
    &amp;quot;changed&amp;quot;: false, 
    &amp;quot;ping&amp;quot;: &amp;quot;pong&amp;quot;
}
origin-m1 | SUCCESS =&amp;gt; {
    &amp;quot;changed&amp;quot;: false, 
    &amp;quot;ping&amp;quot;: &amp;quot;pong&amp;quot;
}
origin-m3 | SUCCESS =&amp;gt; {
    &amp;quot;changed&amp;quot;: false, 
    &amp;quot;ping&amp;quot;: &amp;quot;pong&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Toutes les machines doivent répondre par un pong. Si c&amp;rsquo;est pas le cas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vérifiez les hosts&lt;/li&gt;
&lt;li&gt;vérifier l&amp;rsquo;utilisateur&lt;/li&gt;
&lt;li&gt;avez vous bien envoyez vos clefs aux serveurs ?&lt;/li&gt;
&lt;li&gt;etc&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;provisionnement-des-machines&#34;&gt;Provisionnement des machines&lt;/h1&gt;
&lt;p&gt;Bon, on passe à l&amp;rsquo;installation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ kubespray deploy
# [...]

PLAY [lb] **************************************************************************************************************
skipping: no hosts matched

PLAY RECAP *************************************************************************************************************
xxx            : ok=64   changed=19   unreachable=0    failed=0   
yyy            : ok=64   changed=19   unreachable=0    failed=0   
zzz            : ok=64   changed=19   unreachable=0    failed=0 

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est parti. l&amp;rsquo;installation prend entre 10 et 20 minutes environ selon le nombre de machines, la puissance de votre ordinateur, la phase de la lune et le cours de rouble.&lt;/p&gt;
&lt;p&gt;Si tout est &amp;ldquo;ok&amp;rdquo; à la fin, bravo.&lt;/p&gt;
&lt;p&gt;Chez moi ça a pété&amp;hellip; j&amp;rsquo;ai relancé l&amp;rsquo;installation et cette fois tout est rentré dans l&amp;rsquo;ordre. Pour info, de mon coté, j&amp;rsquo;avais une perte de connexion entre un node et un master. Impossible de savoir pourquoi.&lt;/p&gt;
&lt;p&gt;Alors oui, j&amp;rsquo;ai omis le fait que vous pouvez modifier plein d&amp;rsquo;options, comme le type d&amp;rsquo;overlay network, ou les CIDR à utiliser. Là je vous la fait simple.&lt;/p&gt;
&lt;p&gt;Maintenant, il faut qu&amp;rsquo;on puisse se connecter au cluster pour le paramétrer. Et là&amp;hellip; ça devient moins marrant.&lt;/p&gt;
&lt;h1 id=&#34;paramétrer-un-utilisateur&#34;&gt;Paramétrer un utilisateur&lt;/h1&gt;
&lt;p&gt;Si vous connaissez bien OpenShift, vous savez que l&amp;rsquo;on a le moyen de s&amp;rsquo;authentifier directement sur la plateforme, comme ça, en allant sur la &amp;ldquo;console web&amp;rdquo;. Mais sur Kuberentes c&amp;rsquo;est un poil plus compliqué. Car Kubernetes est légèrement plus bas niveau que OpenShift (rien de péjoratif).&lt;/p&gt;
&lt;p&gt;On va donc faire en sorte que depuis votre PC vous puissiez vous connecter à votre cluster.&lt;/p&gt;
&lt;p&gt;La méthode que je choisi est la méthode &amp;ldquo;certificat client&amp;rdquo;. Je la trouve plus sécurisée et surtout plus claire.&lt;/p&gt;
&lt;p&gt;On va donc devoir créer une clef privée, puis une demande de signature (CSR), envoyer ce CSR à un master, signer la requête et récupérer le certificat.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-caf4cf72-32b3-4d9c-9c2a-61071094fb42.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Comme la version de Kubernetes actuelle est &amp;gt;= 1.8 on va se confronter au &amp;ldquo;role binding&amp;rdquo;, en gros c&amp;rsquo;est le fait que votre utilisateur a un certains nombre de droits associés. Là, on va carrément créer un admin.&lt;/p&gt;
&lt;h2 id=&#34;génération-de-clefs&#34;&gt;Génération de clefs&lt;/h2&gt;
&lt;p&gt;On commence par créer un répertoire où on va stocker notre clef privée, le CSR, et le certificat signé. Puis on génère la clef privée.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# on crée un répertoire de config
mkdir -p ~/.config/kubernetes/

# Choisissez un pseudo, et mettez le dans _username
_username=&amp;quot;votre pseudo&amp;quot;
_organisation=&amp;quot;toto.com&amp;quot;

# clef privé à ne faire qu&#39;une fois
openssl genrsa -out ${_username}.key 2048
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;création-dun-csr&#34;&gt;Création d&amp;rsquo;un CSR&lt;/h2&gt;
&lt;p&gt;Vous avez donc ici une clef privée &amp;ldquo;votre_user.key&amp;rdquo;, créons une requête de signature (CRS = Certificate Signing Request):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;openssl req -new \
    -key ${_username}.key \
    -out ${_username}.csr \
    -subj &amp;quot;/CN=${_username}/O=${_organisation}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui compte ici, c&amp;rsquo;est surtout l&amp;rsquo;entrée &amp;ldquo;CN&amp;rdquo; (Common Name) qui correspondra à votre nom d&amp;rsquo;utilisateur.&lt;/p&gt;
&lt;h2 id=&#34;génération-du-certificat&#34;&gt;Génération du certificat&lt;/h2&gt;
&lt;p&gt;Voilà, vous avez un &amp;ldquo;CSR&amp;rdquo;, &lt;strong&gt;on le dépose sur un des masters&lt;/strong&gt; et on va créer le certificat associé à la demande. Pour cela il faut utiliser le certificat de l&amp;rsquo;autorité (le cluster kubernetes) et la clef privée de cette autorité. On va créer un certificat valable 1 an:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;scp ${_username}.csr root@votre_master1:/tmp/${_username}.csr
ssh root@votre_master1 openssl x509 -req \
    -in /tmp/${_username}.csr \
    -CA /etc/kubernetes/ssl/ca.pem \
    -CAkey /etc/kubernetes/ssl/ca-key.pem \
    -CAcreateserial \
    -out /tmp/${_username}.crt \
    -days 365
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous avons créé le certificat &amp;ldquo;votre_user.crt&amp;rdquo; sur le serveur maitre, on va le récupérer sur notre machine.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;scp root@votre_master1:/tmp/${_username}.crt ~/.config/kubernetes/${_username}.crt
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;configuration-de-kubectl&#34;&gt;Configuration de kubectl&lt;/h1&gt;
&lt;p&gt;On fait le bilan:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on a une clef privé &amp;ldquo;votre_user.key&amp;rdquo;&lt;/li&gt;
&lt;li&gt;un certificat signé par le serveur &amp;ldquo;votre_user.crt&amp;rdquo;&lt;/li&gt;
&lt;li&gt;le tout dans ~/.config/kubernetes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On va maintenant paramétrer kubectl pour les utiliser.&lt;/p&gt;
&lt;p&gt;Il faut:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;créer une config de serveur (un cluster): indiquer quel machine on va interroger&lt;/li&gt;
&lt;li&gt;créer un user en se basant sur la clef et le certig&lt;/li&gt;
&lt;li&gt;créer un &amp;ldquo;contexte&amp;rdquo; qui rassemble cet user et ce &amp;ldquo;cluster&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ces commandes font le boulot:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# création d&#39;un config de cluster
kubectl config set-cluster NOM_DU_CLUSTER \
    --server=https://ip.du.master:6443 \
    --insecure-skip-tls-verify=true
    
# création d&#39;un utilisateur utilisant des certif
kubectl config set-credentials ${_username} \
    --client-certificate=~/.config/kubernetes/${_username}.crt \
    --client-key=~/.config/kubernetes/${_username}.key

# on lie le cluster à un utilisateur
kubectl config set-context NOM_DU_CONTEXXT \
    --cluster=NOM_DU_CLUSTER \
    --user=${_username}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;N&amp;rsquo;oubliez pas de changer les noms de cluster et de contexte avec un truc dont vous vous souviendrez.&lt;/p&gt;
&lt;p&gt;Bon, voilà, on peut utiliser ce context:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl config use-context NOM_DU_CONTEXXT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là, le drame:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl get nodes
# ERROR, pas les droits
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et oui, votre utilisateur n&amp;rsquo;est pas dans les petits papiers de Kubernetes, il n&amp;rsquo;a pas de droits associés. On va remédier à ça.&lt;/p&gt;
&lt;h2 id=&#34;donner-les-droits-admin-à-cet-utilisateur&#34;&gt;Donner les droits &amp;ldquo;admin&amp;rdquo; à cet utilisateur&lt;/h2&gt;
&lt;p&gt;Il existe deux &amp;ldquo;binding&amp;rdquo; de droits, les RoleBinding sont utililsés pour les namespaces, et les ClusterRoleBinding pour les droits du cluster entier. Ici, nous voulons donner un droit à un utilisateur sur tout le cluster.&lt;/p&gt;
&lt;p&gt;Si vous tapez &amp;ldquo;&lt;code&gt;kubectl get clusterrolebinding&lt;/code&gt;&amp;rdquo; vous verrez une liste conséquente d&amp;rsquo;associations de droits possibles. Celui qui nous intéresse est le binding &amp;ldquo;cluster-admin&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;Allez sur un master (via ssh), et faites simplement:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# si vous n&#39;aimez pas vi, tapez EDITOR=nano
# par exemple, avant cette commande
kubectl edit clusterrolebinding cluster-admin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela va ouvrir un éditeur &amp;ldquo;vi&amp;rdquo;, ajoutez dans &amp;ldquo;subjects&amp;rdquo; votre utilisateur:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;#...
subjects:
# ...
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: nom_de_votre_user
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sauvez, et revenez sur votre machine à vous. Relancez:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ kubectl get nodes
NAME        STATUS    AGE
origin-m1   Ready      X
origin-m2   Ready      X
origin-m3   Ready      X
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà !&lt;/p&gt;
&lt;p&gt;Evidemment, on est pas forcé d&amp;rsquo;ajouter l&amp;rsquo;utilisateur en admin, un &amp;ldquo;rolebinding&amp;rdquo; simple peut donner des droits à un utilisateur sur un namespacce en particulier, et il pourra au choix avoir le droit d&amp;rsquo;écriture, de lecture, etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Voir la page: &lt;a href=&#34;https://kubernetes.io/docs/admin/authorization/rbac/&#34;&gt;https://kubernetes.io/docs/admin/authorization/rbac/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;on-résume&#34;&gt;On résume&lt;/h1&gt;
&lt;p&gt;Ça parait bourrin et lourd, mais c&amp;rsquo;est en fait super simple quand on résume:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on crée une clef privée SSL&lt;/li&gt;
&lt;li&gt;on crée une demande de signature de certif&lt;/li&gt;
&lt;li&gt;on envoit ça à un serveur &amp;ldquo;master&amp;rdquo;&lt;/li&gt;
&lt;li&gt;le master signe le certificat&lt;/li&gt;
&lt;li&gt;on le récupère sur le pc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ensuite:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on crée une config de cluster sur le client pour indiquer où se trouve le ou les masters&lt;/li&gt;
&lt;li&gt;on crée une config utilisateur sur le client pour dire quelle clef et quel certif utiliser&lt;/li&gt;
&lt;li&gt;on lie les deux dans un &amp;ldquo;context&amp;rdquo; (un utilisateur + un cluster)&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et enfin:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on ajoute des droits à l&amp;rsquo;utilisateur sur le cluster (admin ou autre)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;ouvrir-le-dahsboard&#34;&gt;Ouvrir le dahsboard&lt;/h1&gt;
&lt;p&gt;On va maintenant utiliser kubectl pour avoir le dashboard. La méthode que je recommande est d&amp;rsquo;utiliser kubectl en mode &amp;ldquo;proxy&amp;rdquo;, cela permet de ne pas laisser le dashboard entre toutes les mains et de ne pas avoir à paramétrer le navigateur pour accepter certains certificats.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ kubectl proxy
Starting to serve on 127.0.0.1:8001
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ouvrez la page http://127.0.0.1:8001/ui et admirrez votre dashboard. Halalala ça fait plaisirs non ?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-353da604-2d76-463a-8b60-219beb0d25ee.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;en-bref&#34;&gt;En bref&lt;/h1&gt;
&lt;p&gt;Si vous venez de version inférieures à 1.8 de Kubernetes, les RBAC (Role-Based Access Control) vont vous dérouter un petit peu en premier lieu. Ceux qui viennent du monde OpenShift ont déjà pas mal travaillé avec la gestion de rôles ce qui leur donne un petit avantage.&lt;/p&gt;
&lt;p&gt;Cela-dit, kubespray est un outil super sympa depuis que le projet kubernetes-ansible n&amp;rsquo;esst plus supporté (devenu obsolète en faveur de kubespray) et je n&amp;rsquo;ai vraiment pas mis longtemps à adapter mes inventaires pour générer un cluster sur &lt;a href=&#34;https://www.scaleway.com/&#34;&gt;ScaleWay&lt;/a&gt; - j&amp;rsquo;ai encore des soucis avec GlusterFS et Heketi sur Scaleway dont les admin ont l&amp;rsquo;air de se foutre du fait que nous avons besoin de SELinux (ce qui m&amp;rsquo;a coincé pour OpenShift) malgré nos relances en ticket de support et sur l&amp;rsquo;issue Github &lt;a href=&#34;https://github.com/scaleway/image-centos/issues/19&#34;&gt;https://github.com/scaleway/image-centos/issues/19&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mais bon, avec un peu de lecture et quelques tests, j&amp;rsquo;ai un cluster qui fonctionne en moins d&amp;rsquo;une heure avec &lt;a href=&#34;https://github.com/kubernetes/heapster&#34;&gt;Heapster&lt;/a&gt;, &lt;a href=&#34;https://traefik.io/&#34;&gt;Traefik&lt;/a&gt; etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Reste &lt;a href=&#34;https://github.com/kubernetes/helm&#34;&gt;helm&lt;/a&gt; qui refuse de fonctionner à cette heure, mais je vais bien finir par y arriver !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Adieu TeamViewer</title>
      <link>https://www.metal3d.org/blog/2017/adieu-teamviewer/</link>
      <pubDate>Thu, 30 Nov 2017 00:17:54 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2017/adieu-teamviewer/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2017/adieu-teamviewer/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;J&amp;rsquo;en avais marre de TeamViewer, des soucis avec Wayland, incompatibilité de versions, et puis pour partager simplement un terminal ou un écran je ne voulais pas que le &amp;ldquo;client&amp;rdquo; ait quoique ce soit à installer. Et VNC est trop contraignant avec ces foutus ports. Voyons ce qu&amp;rsquo;on a sous la main pour virer ce logiciel.&lt;/p&gt;
&lt;p&gt;Oui, je sais, vous en avez rien à faire que TeamViewer soit un truc qui appartienne à une boite qui ne vous dit pas comment elle peut loguer tout ce qu&amp;rsquo;il se passe quand vous partagez une session avec quelqu&amp;rsquo;un, mais moi ça m&amp;rsquo;intrigue. Et puis en plus, je préfère largement avoir plusieurs solutions sous la main selon mon besoin (pourquoi partager un écran si je veux juste montrer un terminal ?).&lt;/p&gt;
&lt;p&gt;Bref, le but c&amp;rsquo;est que moi j&amp;rsquo;ouvre un logiciel et que celui à qui je veux partager mon terminal ou mon écran n&amp;rsquo;ait qu&amp;rsquo;à aller sur une page web. Et bha tu me crois, tu me crois pas, mais j&amp;rsquo;ai trouvé deux outils qui m&amp;rsquo;ont tué en terme de simplicité.&lt;/p&gt;
&lt;p&gt;Ces deux solutions vont vous rendre la vie plus simple. L&amp;rsquo;une est purement pour partager un simple terminal, et l&amp;rsquo;autre ressemble plus à TeamViewer, sauf que vous n&amp;rsquo;aurez pas besoin d&amp;rsquo;un client lourd pour vous y connecter. Et il va en plus vous donner un peu plus d&amp;rsquo;info et de possibilités que ce bon vieux TeamViewer ou VNC.&lt;/p&gt;
&lt;p&gt;On va donc parler de &lt;a href=&#34;tmate.io&#34;&gt;tmate&lt;/a&gt; et de &lt;a href=&#34;https://www.dwservice.net/&#34;&gt;DWservice&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;tmate---partager-un-terminal&#34;&gt;Tmate - partager un terminal&lt;/h1&gt;
&lt;p&gt;Vous allez me dire:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Oouais super, bha SSH avec &amp;ldquo;type&amp;rdquo; + &amp;ldquo;script&amp;rdquo; ça marche bien.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Oui&amp;hellip; mais il se passe quoi quand je suis derrière ma box et que le copain à qui je veux montrer du code, un terminal, une sortie de commande, se trouve à l&amp;rsquo;autre bout du globe (amis &lt;a href=&#34;https://fr.wikipedia.org/wiki/Flat_Earth_Society&#34;&gt;platistes&lt;/a&gt;, bonsoir)&lt;/p&gt;
&lt;p&gt;Il faut ouvrir un port, faire du NAT, créer un compte à l&amp;rsquo;utilisateur pour pas qu&amp;rsquo;il ait accès à votre home&amp;hellip; puis lancer une sessions &amp;ldquo;script&amp;rdquo; (&amp;quot;&lt;code&gt;script -f /tmp/session&lt;/code&gt;&amp;quot; par exemple) dans un fichier et demander à votre pote de lancer &amp;ldquo;&lt;code&gt;tail -f /tmp/session&lt;/code&gt;&amp;rdquo; bref&amp;hellip; c&amp;rsquo;est chiant.&lt;/p&gt;
&lt;p&gt;Et là, on a &amp;ldquo;tmate&amp;rdquo; qui arrive avec sa cape et ses super-pouvoirs pour éviter de passer un diplôme d&amp;rsquo;ingé juste pour permettre un partage de session. Tmate est un &amp;ldquo;simple&amp;rdquo; fork de &amp;ldquo;tmux&amp;rdquo; avec des options de &amp;ldquo;broadcast&amp;rdquo; vers un serveur.&lt;/p&gt;
&lt;p&gt;Ça va vous détendre&amp;hellip; croyez moi !&lt;/p&gt;
&lt;p&gt;On l&amp;rsquo;installe bêtement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# Fedora
dnf install tmate
# debian, ubuntu
apt-get isntall tmate
# freebsd
pkg install tmate
# etc...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensuite vous lancez une session:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;tmate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Là vous vous retrouvez dans un &amp;ldquo;tmux&amp;rdquo; qui peut partager (par défaut) votre session sur &amp;ldquo;tmate.io&amp;rdquo; avec des adresses secrètes.&lt;/p&gt;
&lt;p&gt;Pour connaitre ces infos, c&amp;rsquo;est simple, dans votre session tmate:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ tmate show-messages
Wed Nov 29 00:53:06 2017 [tmate] Connecting to ssh.tmate.io...
Wed Nov 29 00:53:06 2017 [tmate] Note: clear your terminal before sharing readonly access
Wed Nov 29 00:53:06 2017 [tmate] web session read only: https://tmate.io/t/ro-XXXXXXXXX
Wed Nov 29 00:53:06 2017 [tmate] ssh session read only: ssh ro-YYYYYYYYY@ln2.tmate.io
Wed Nov 29 00:53:06 2017 [tmate] web session: https://tmate.io/t/ZZZZZZZZZZZZ
Wed Nov 29 00:53:06 2017 [tmate] ssh session: ssh AAAAAAAAAAAAAA.tmate.io
Wed Nov 29 00:54:53 2017 [tmate] A mate has joined (77.XX.XX.XX) -- 1 client currently connected
Wed Nov 29 00:55:39 2017 [tmate] A mate has joined (77.XX.XX.XX) -- 2 clients currently connected
Wed Nov 29 00:55:51 2017 [tmate] A mate has left (77.XX.XX.XX) -- 1 client currently connected
Wed Nov 29 00:55:54 2017 [tmate] A mate has left (77.XX.XX.XX) -- 0 client currently connected
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(j&amp;rsquo;ai pensé à masquer les infos)&lt;/p&gt;
&lt;p&gt;Vous l&amp;rsquo;avez compris, il y a 4 façons de vous connecter à votre session:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;en ssh READ ONLY,&lt;/li&gt;
&lt;li&gt;en ssh normal (donc le participant peut contrôler le tmux)&lt;/li&gt;
&lt;li&gt;en web READ ONLY - une page web où le participant voit le terminal&lt;/li&gt;
&lt;li&gt;en web avec contrôle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, vous donnez le lient ssh, ou web, à votre pote, et vous tapez &amp;ldquo;&lt;code&gt;clear&lt;/code&gt;&amp;rdquo; dans le terminal pour cacher les infos si vous ne voulez pas qu&amp;rsquo;il voit la manière de se connecter en session &amp;ldquo;write&amp;rdquo; alors que vous lui avez donné que le droit de lire (vous suivez ? il est tard et je tape un peu à la va-vite là&amp;hellip;)&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/68a2adc5-78a1-444d-b9a5-4efae5fa3e90-tmate-session.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et si vous voulez, vous pouvez créer votre propre serveur tmate, du coup vous êtes sûr que les monsieurs derrière le serveur de tmate ne loguent rien, bien que j&amp;rsquo;ai confiance en leur outil et leurs intentions.&lt;/p&gt;
&lt;p&gt;Bref, l&amp;rsquo;outil est magistralement simple et efficace.&lt;/p&gt;
&lt;h1 id=&#34;dwservice---un-teamviewer-like-voir-mieux&#34;&gt;DWService - un TeamViewer like, voir mieux&lt;/h1&gt;
&lt;p&gt;Autre outil que j&amp;rsquo;adore, &lt;a href=&#34;https://www.dwservice.net&#34;&gt;DWService&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Là on a un outil qui déchire. Il existe plusieurs façons de l&amp;rsquo;utiliser et il offre des options de dingue.&lt;/p&gt;
&lt;p&gt;La première chose que vous voulez faire, c&amp;rsquo;est contrôler un PC à distance. Le serveur est &amp;ldquo;autonome&amp;rdquo; et peut être utilisé sans installation. &lt;strong&gt;Il marche sur Linux, Windows, RaspBerry et OSX.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Allez sur la page d&amp;rsquo;accueil et regardez à droite, vous avez un lien de téléchargement, cliquez ou suivez ce que je vous propose pour linux pour le faire en ligne de commande:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-0380c041-8645-4cc3-ba9f-b429537cb6df.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Sur linux, vous pouvez faire:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -sSL https://www.dwservice.net/download/dwagent_x86.sh -o /tmp/dwservice.sh
bash /tmp/dwservice.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Une fenêtre s&amp;rsquo;ouvre, vous acceptez &amp;ldquo;sans installation&amp;rdquo; et l&amp;rsquo;outil vous donne un identifiant et un mot de passe que vous donnez à votre ami. Et là, hop, il voit votre écran en ligne. Pas de plugin, pas de truc compliqué, c&amp;rsquo;est simple et efficace.&lt;/p&gt;
&lt;p&gt;En plus de l&amp;rsquo;affichage, vous avez à disposition:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;accès aux fichiers&lt;/li&gt;
&lt;li&gt;accès des des stats et des graphiques (utilisation cpu, mémoire, disque&amp;hellip;)&lt;/li&gt;
&lt;li&gt;création de terminal&lt;/li&gt;
&lt;li&gt;etc&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DWService peut aussi être installé en service (il faut les droits d&amp;rsquo;admin) et il va donc démarer un &amp;ldquo;daemon&amp;rdquo; qui vous permettra d&amp;rsquo;accéder à vos machines depuis votre navigateur. Alors, là, il faut un compte (mais c&amp;rsquo;est gratos hein) et vous loguer sur le site.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-305c32ad-1660-4768-8c9f-5d61a2d7bd05.png&#34; alt=&#34;Les choix&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-edad2158-4ee9-44d6-ab0f-8db23ffef23c.png&#34; alt=&#34;La page de ressource&#34;&gt;&lt;/p&gt;
&lt;p&gt;Mieux encore, vous pouvez créer un partage &amp;ldquo;limité&amp;rdquo; (ou pas) ces options. Par exemple si je veux partager mon écran en lecture seule:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-04bbae93-a5d2-43aa-9667-87a7eb02ff23.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Il me suffit d&amp;rsquo;aller décocher &amp;ldquo;accès complet&amp;rdquo; et décocher aussi &amp;ldquo;accès à la souris et au clavier&amp;rdquo;, et voilà, je peux montrer mon écran à qui je donnerai le lien. Et c&amp;rsquo;est pareil pour toutes les ressources (terminal, logs, etc&amp;hellip;)&lt;/p&gt;
&lt;p&gt;DWService est un véritable petit bijou, mais il a ses contraintes&amp;hellip; Le client est libre, mais pas le serveur - dans leur FAQ ils disent ne rien loguer, mais bon on doit compter sur votre confiance. Et puis, malheureusement, DWService est pas très rapide. Je soupçonne que le fait de passer dans un navigateur n&amp;rsquo;aide pas. Et comme le serveur est pas open source, et bien pas moyen de tenter d&amp;rsquo;aider.&lt;/p&gt;
&lt;p&gt;Dernier point, j&amp;rsquo;ai un bug avec Chromium (&lt;del&gt;ou Chrome&lt;/del&gt; EDIT: ha bha non, seulement chromium en fait), impossible d&amp;rsquo;ajouter des contraintes de contrôle comme dans la capture ci-dessus. Le bug n&amp;rsquo;est pas valable sous FireFox et Chrome. Bref, des &amp;ldquo;détails&amp;rdquo; qui peuvent vous gêner un poil.&lt;/p&gt;
&lt;p&gt;Mais, sans déconner, j&amp;rsquo;avais jamais imaginé que ça puisse être si simple. Je ne sais pas quoi leur dire à part &amp;ldquo;bravo&amp;rdquo;. Et ça donne envie de répondre à leur appel aux dons: &lt;a href=&#34;https://www.dwservice.net/fr/contribute-sponsorshipsdonations.html&#34;&gt;https://www.dwservice.net/fr/contribute-sponsorshipsdonations.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Franchement, le parrainage est pour les entreprises, mais donner 5-10€ en don (en bas de page) ce serait pas du luxe&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Bha que dire&amp;hellip; ?&lt;/p&gt;
&lt;p&gt;En général tmate suffit pour une démo rapide d&amp;rsquo;un outil avec un collègue.&lt;/p&gt;
&lt;p&gt;DWService sur mon raspberry et mon pc de la maison pour y accéder à l&amp;rsquo;extérieur (parce que je vous rapelle que c&amp;rsquo;est du pur web là) me permet de dégager TeamViewer.&lt;/p&gt;
&lt;p&gt;Et quand ma chère mère est bloquée sur son PC et bien elle lance simplement dwservice en cliquant sur l&amp;rsquo;icone et elle me donne les identifiant. Et sans me prendre le but, sans ouvrir des ports, sans rien avoir à faire de plus que d&amp;rsquo;aller sur le site, je peux l&amp;rsquo;aider sans rien installer de mon coté.&lt;/p&gt;
&lt;p&gt;Bref, TeamViewer, adieu. Même si, il faut l&amp;rsquo;admettre, TeamViewer a le mérite d&amp;rsquo;être rapide, je préfère de loin tmate et DWService selon le besoin. Pour leur vitesse, pour leur justesse et les options que je ne trouve pas ailleurs (terminal, ressources cpu/mem, partage de dossier, etc&amp;hellip;) je n&amp;rsquo;ai pas envie de passer à autre chose.&lt;/p&gt;
&lt;p&gt;Et oui, avec l&amp;rsquo;IPv6 je pourrait aussi me connecter à mon PC à la maison, mais beaucoup d&amp;rsquo;endroits d&amp;rsquo;où je bosse n&amp;rsquo;ont pas activé l&amp;rsquo;IPv6. Et je déteste le NAT&amp;hellip; donc voilà !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Angular, ReactJS, VueJS, comparer l&#39;incomparable</title>
      <link>https://www.metal3d.org/blog/2017/angular-reactjs-vuejs-comparer-lincomparable/</link>
      <pubDate>Sat, 11 Nov 2017 13:05:51 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2017/angular-reactjs-vuejs-comparer-lincomparable/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2017/angular-reactjs-vuejs-comparer-lincomparable/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Angular, VueJS et React sont souvent comparés entre eux - et la mode est de comparer les bananes et les carottes on dirait&amp;hellip;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Permière erreur, la plupart des articles que je lis sur les comparatifs React, Vue, Angular ne parlent pas d&amp;rsquo;Angular, mais de AngularJS. Seconde erreur très répandue: &lt;strong&gt;vouloir à tout prix comparer des technologies qui n&amp;rsquo;ont rien à voir&lt;/strong&gt; - on va donc décrire le problème ici. Mais avant tout, pourquoi on parle d&amp;rsquo;eux ?&lt;/p&gt;
&lt;h1 id=&#34;lapplication-monopage&#34;&gt;L&amp;rsquo;application monopage&lt;/h1&gt;
&lt;p&gt;JQuery fut à la mode et force est de constater que son reigne s&amp;rsquo;éffondre mois après mois. Aujourd&amp;rsquo;hui, on parle de SPA (Single Page Application) ou &amp;ldquo;application monopage&amp;rdquo; dans la langue de Jean-Pierre Pernaut. Et pour réussir à faire une application complète, active, réactive, structurée, il faut une technologie adaptée.&lt;/p&gt;
&lt;p&gt;Jusque là, on ne jurait que par &lt;a href=&#34;https://angularjs.org/&#34;&gt;AngularJS&lt;/a&gt;, développé par Google, ou &lt;a href=&#34;http://knockoutjs.com/&#34;&gt;Knockout&lt;/a&gt; qui ont apporté le &amp;ldquo;two way data binding&amp;rdquo; de manière magistrale. Sauf que voilà, quand un truc mache bien, tout le monde s&amp;rsquo;empresse de vouloir proposer sa vision, sa version, sa technique. Il y en a d&amp;rsquo;autres, plus ou moins bien fichus, mais je ne suis pas là pour les comparer.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://reactjs.org/&#34;&gt;ReactJS&lt;/a&gt; est venu remuer le sac pour proposer une autre façon de faire. Développée et utilisée par FaceBook, cette librairie est devenue vraiment populaire et on comprend pourquoi !&lt;/p&gt;
&lt;p&gt;Nous voilà donc avec deux grands noms (Google et Facebook) qui apportent tous les deux des technologies qui vont faire grand bruit.&lt;/p&gt;
&lt;p&gt;Au milieu de tout ça vient le David contre les Goliath, j&amp;rsquo;ai nommé &lt;a href=&#34;https://vuejs.org/&#34;&gt;VueJS&lt;/a&gt;. Développé par un ancien de chez Google (de l&amp;rsquo;équipe AngularJS), le dénommé Evan You, cette librairie apporte un vent frais dans la fournaise.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Les technologies du net changent. Aujourd&amp;rsquo;hui les projets se portent de plus en plus vers une séparation &amp;ldquo;API/client léger&amp;rdquo; - les sites qui génèrent des pages de manière plus ancêstrale sont en train de devenir &amp;ldquo;old school&amp;rdquo;. Cela permet de vraiment séparer deux partie: la vue frontale et l&amp;rsquo;acquisition de la donnée. Nous ne sommes plus obligé de gérer la vue du coté où sont traitées les données, et ça en tout honnêteté c&amp;rsquo;est un vrai grand pas en avant.&lt;/p&gt;
&lt;p&gt;Mais depuis quelques mois, AngularJS a changé&amp;hellip; en fait il n&amp;rsquo;est plus, enfin si&amp;hellip; mais non&amp;hellip; bon je m&amp;rsquo;explique.&lt;/p&gt;
&lt;h1 id=&#34;angularjs-nest-pas-angular-&#34;&gt;AngularJS n&amp;rsquo;est pas Angular !&lt;/h1&gt;
&lt;p&gt;Bien que ce soit la même équipe, le même éditeur (Google) et quasi le même site, les deux technologies sont différentes - et à tel point que beaucoup pense que le nom aurait du changer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Angular n&amp;rsquo;est pas un framework JS, ni une librairie.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est un &lt;strong&gt;framework&lt;/strong&gt; TypeScript, bien que vous puissiez développer en JS si ça vous chante, qui déporte littéralement tout le développement en dehors de la page. Car Angular &amp;ldquo;génère&amp;rdquo; votre application (en JS).&lt;/p&gt;
&lt;p&gt;Petite note: VueJS permet de développer en TypeScript, c&amp;rsquo;est pas répandu mais ça se fait.&lt;/p&gt;
&lt;p&gt;On est donc bel et bien en dehors de ce que propose ReactJS et VueJS qui, elles, sont des librairies qui s&amp;rsquo;intègrent dans une page. L&amp;rsquo;approche est différente, la techno est différente, alors pourquoi s&amp;rsquo;obstiner à les comparer ?&lt;/p&gt;
&lt;p&gt;Je comprend bien que vous puissiez faire le comparatif puisque, au final, on se retrouve à travailler dans la même optique. Mais le choix ne doit pas se faire sur la technologie, elle doit se faire sur l&amp;rsquo;orientation du développeur ou intégrateur qui va travailler.&lt;/p&gt;
&lt;p&gt;Certainement que dans l&amp;rsquo;esprit collectif, c&amp;rsquo;est AngularJS qui a laissé une empreinte trop persistente.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Imaginons que nous développions un Framework qui transforme tout mon PHP en une application JS/HTML&lt;/strong&gt;. Vous comprennez bien que l&amp;rsquo;approche est différente de ReactJS et VueJS ? Vous ne feriez pas la comparaison parce que d&amp;rsquo;un coté on développe une application dans un langage dont le rendu se fera dans un navigateur, et de l&amp;rsquo;autre vous développeriez avec des librairies JS une application qui est &amp;ldquo;intégrée&amp;rdquo; dans une page.&lt;/p&gt;
&lt;p&gt;Il n&amp;rsquo;y a rien de péjoratif dans le fait d&amp;rsquo;être une librairie JS, et il n&amp;rsquo;y a rien de gratifiant d&amp;rsquo;être un Framework TypeScript, ce sont juste deux approches très différentes, et &lt;strong&gt;pas comparables&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Et bien là c&amp;rsquo;est pareil ! Angular vous propose de développer des classes, des components, des services&amp;hellip; vous propose de l&amp;rsquo;injection de dépendance, des méthodes d&amp;rsquo;appel HTTP vers les API, etc&amp;hellip; et cela &amp;ldquo;compile&amp;rdquo; un site monopage qui saura utiliser un backend.&lt;/p&gt;
&lt;p&gt;Bref, la cible de développeur n&amp;rsquo;est pas la même, l&amp;rsquo;approche est différente.&lt;/p&gt;
&lt;h1 id=&#34;donc-on-choisi-comment-&#34;&gt;Donc on choisi comment ?&lt;/h1&gt;
&lt;p&gt;Il faut s&amp;rsquo;abstraire de la technique en premier lieu.&lt;/p&gt;
&lt;p&gt;Posez-vous les questions suivantes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;est-ce que c&amp;rsquo;est une application poussée qui demande du développement &amp;ldquo;applicatif&amp;rdquo; ?&lt;/li&gt;
&lt;li&gt;est-ce que ce sont des intégrateurs ou des développeur applicatifs qui vont bosser dessus ?&lt;/li&gt;
&lt;li&gt;est-ce que c&amp;rsquo;est l&amp;rsquo;interface qui prime ou le fonctionnement applicatif ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avec ces questions, vous allez ensuite chercher les compétences utilisée pour intégrer la page. Les &amp;ldquo;dev&amp;rdquo; préfèrent généralement Angular, puis VueJS, les intégrateurs vont plutôt préférer ReactJS, puis VueJS.&lt;/p&gt;
&lt;p&gt;Vous l&amp;rsquo;aurez compris: VueJS rassemble beaucoup, son approche mixe les deux concepts. Cela-dit, moi en tant que dev, j&amp;rsquo;ai une préférence pous le TypeScript (notamment pour son intégration dans un IDE qui est très avancée, sur Vim c&amp;rsquo;est vraiment bien fichu) - mais je ne choisi pas Angular parce qu&amp;rsquo;il est mieux que React ou Vue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Je choisi Angular parce que l&amp;rsquo;orientation MVC est, de mon point de vue, plus adaptée à mes travaux.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Je ne suis pas intéressé pour générer du HTML depuis JS/JSX, loin de moi l&amp;rsquo;idée de critiquer cette approche, elle ne convient juste pas dans mes développements. Mais vous, certainement que oui, et je le comprendrai sans souci. Au boulot, un de mes collègue a justement préféré React puisque sont besoin étaiat de travailer sur l&amp;rsquo;interface en tapant sur quelques enpoints d&amp;rsquo;API REST.&lt;/p&gt;
&lt;p&gt;Moi de mon coté, je développe une application qui demande 40 endpoints, du contrôle poussé dans le fonctionnement, et j&amp;rsquo;avais vraiment besoin d&amp;rsquo;injection de dépendance pour éviter le boxon inmaintenable vue la dose de code à engendrer. L&amp;rsquo;interfaçage est purement HTML (template) et je peux donner cette partie à une autre équipe, donc Angular est plus adapté à ce projet.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est l&amp;rsquo;approche qui est comparée, pas le fonctionnement&amp;hellip; vous me suivez ?&lt;/p&gt;
&lt;h1 id=&#34;comparer-autrement&#34;&gt;Comparer autrement&lt;/h1&gt;
&lt;p&gt;Ce qui m&amp;rsquo;agace finalement, ce n&amp;rsquo;est pas de vouloir comparer les technologies, c&amp;rsquo;est le fait de l&amp;rsquo;orienter sur la &amp;ldquo;technique&amp;rdquo; alors que la comparaison devrait se faire sur l&amp;rsquo;oritentation de développement.&lt;/p&gt;
&lt;p&gt;Si vous êtes un développeur orienté structure/class/objet (Java, Go, Python, PHP&amp;hellip;) vous allez certainement préferer apprendre le TypeScript (qui est franchement pas sorcier) et créer l&amp;rsquo;application dans une optique de &amp;ldquo;framework&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Si vous êtes plutôt intégrateur, que vous chercher à définir une interface plus &amp;ldquo;animée&amp;rdquo;, alors VueJS et ReactJS sont certainement plus adaptés à vos besoins.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est don inadapté de comparer les bananes et les carottes, demandez vous plutôt si vous en êtes au plat principal ou au dessert. Là, vous pourrez choisir entre des librairies JS ou des framework applicatif. Donc entre VueJS, React et Angular.&lt;/p&gt;
&lt;p&gt;Et surtout, gardez bien à l&amp;rsquo;esprit que &lt;strong&gt;Angular != AngularJS&lt;/strong&gt; pour ne pas comparer l&amp;rsquo;incomparable.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Golang, comment définir un destructeur</title>
      <link>https://www.metal3d.org/blog/2017/golang-comment-d%C3%A9finir-un-destructeur/</link>
      <pubDate>Thu, 16 Mar 2017 06:58:07 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2017/golang-comment-d%C3%A9finir-un-destructeur/</guid>
      <description>
        
        
           &lt;img src=&#34;https://www.metal3d.org/blog/2017/golang-comment-d%C3%A9finir-un-destructeur/cover.webp&#34; /&gt;&lt;br /&gt;
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Si vous avez un peu bourlingué sur Go, vous savez qu&amp;rsquo;il n&amp;rsquo;existe pas de &amp;ldquo;destructeur&amp;rdquo; sur les structures. Mais en cherchant un peu, vous allez pouvoir utiliser le garbage collector et &lt;em&gt;simuler&lt;/em&gt; un destructeur.&lt;/p&gt;
&lt;p&gt;Je me suis rendu compte en utilisant un peu Go pour créer mon blog, que si je ne faisais pas attention je pouvais avoir des centaines de connections à la base Mongo ouvertes et jamais fermées. C&amp;rsquo;est évidemment un truc à éviter, mais le design de Go n&amp;rsquo;aide pas des masses. Du moins quand il s&amp;rsquo;agit de bosser avec des connections persistantes.&lt;/p&gt;
&lt;p&gt;Il faut dire que le pattern très répandu en Go d&amp;rsquo;utiliser un &amp;ldquo;&lt;code&gt;defer xxx.Close()&lt;/code&gt;&amp;rdquo; peut parfois passer à la trappe, personne n&amp;rsquo;est parfait et on peut simplement oublier de le faire. Et le résultat peut être relativement lourd, voir dangereux pour votre RAM.&lt;/p&gt;
&lt;p&gt;Je vais réduire le problème à un cas simple, on va se créer un petit benchmark tout bête pour comprendre ce qu&amp;rsquo;il se passe.&lt;/p&gt;
&lt;h1 id=&#34;le-bout-de-code-qui-fait-mal&#34;&gt;Le bout de code qui fait mal&lt;/h1&gt;
&lt;p&gt;En premier je démarre une base mongo via Docker, c&amp;rsquo;est rapide à faire, ça fout pas en l&amp;rsquo;air le système, c&amp;rsquo;est jetable: j&amp;rsquo;aime ça!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker run --rm -it -p 27017:27017 mongo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La base est là, elle attend plus que vous.&lt;/p&gt;
&lt;p&gt;Voici maintenant un bout de code Go qui va exploser rapidement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
	&amp;quot;log&amp;quot;
	&amp;quot;time&amp;quot;

	mgo &amp;quot;gopkg.in/mgo.v2&amp;quot;
)

// Returns a NEW mongo session
func connect() *mgo.Session {
	s, _ := mgo.Dial(&amp;quot;127.0.0.1&amp;quot;)
	return s
}

// Create data in mongo
func createData(i interface{}) {
	s := connect()
	s.DB(&amp;quot;data&amp;quot;).C(&amp;quot;foo&amp;quot;).Insert(i)
	log.Println(&amp;quot;create done&amp;quot;)
}

func main() {
    for i := 0; i &amp;lt; 2000; i++ {
    	createData(map[string]string{
    		&amp;quot;user&amp;quot;: &amp;quot;foo&amp;quot;,
    		&amp;quot;date&amp;quot;: time.Now().String(),
    	})
    	time.Sleep(50 * time.Millisecond)
	}
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour la faire courte, la fonction &amp;ldquo;&lt;code&gt;connect()&lt;/code&gt;&amp;rdquo; crée une connexion à Mongo, la fonction &amp;ldquo;&lt;code&gt;createData()&lt;/code&gt;&amp;rdquo; va appeler cette fonction et ajouter du contenu en base. Sauf que&amp;hellip;&lt;/p&gt;
&lt;p&gt;Vous l&amp;rsquo;avez remarqué, j&amp;rsquo;oublie de fermer la session (pas de &amp;ldquo;&lt;code&gt;s.Close()&lt;/code&gt;&amp;rdquo;) ce qui a pour effet de laisser la connexion active.&lt;/p&gt;
&lt;p&gt;En lançant le programme (&lt;code&gt;go run main.go&lt;/code&gt;) après quelques secondes&amp;hellip; plantage ! la base n&amp;rsquo;arrive plus à gérer toutes les connexions !&lt;/p&gt;
&lt;p&gt;Il existe heureusement un moyen de virer la connexion si elle n&amp;rsquo;est plus utilisée, et ce en demandant au garbage collector (le &amp;ldquo;ramasse miette&amp;rdquo; dans notre chère langue) mais il faut chercher un peu.&lt;/p&gt;
&lt;h1 id=&#34;la-fonction-runtimesetfinalizer&#34;&gt;La fonction &lt;code&gt;runtime.SetFinalizer()&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;Dans le paquet &amp;ldquo;runtime&amp;rdquo;, dans lequel on trouve la fonction &amp;ldquo;GC&amp;rdquo; qui permet de forcer une passe de nettoyage, on trouve la fonction &amp;ldquo;SetFinalizer&amp;rdquo; qui peut nous aider.&lt;/p&gt;
&lt;p&gt;Le principe est simple, en premier argument on lui passe une référence (une adresse) et en second, une fonction qui prend en argument le pointeur.Je fais bien la distinction ici (même si au final c&amp;rsquo;est pareil), pour bien vous indiquer que le premier argument sert bien au GC à trouver &amp;ldquo;l&amp;rsquo;adresse&amp;rdquo; de la variable qui va être géré. La fonction doit quant à elle connaitre le type pointé. Mais arrêtez de vous embrouiller le cerveau et regarder le bout de code ci-dessous:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;runtime.SetFinalizer(s, func(s *mgo.Session) {
	log.Println(&amp;quot;Close db&amp;quot;)
	s.Close()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela indique au garbage collector que la référence &amp;ldquo;s&amp;rdquo; qu&amp;rsquo;il va pouvoir éliminer &amp;ldquo;s&amp;rdquo; de la mémoire en passant par la fonction indiquée. Et dans cette fonction, on appelle &amp;ldquo;&lt;code&gt;s.Close()&lt;/code&gt;&amp;rdquo; qui va fermer la connexion. Pratique hein, parce que si plus aucune fonction n&amp;rsquo;utilise ma connexion, alors elle sera coupée, purement et simplement.&lt;/p&gt;
&lt;p&gt;Comment ça se passe: La fonction &amp;ldquo;connect&amp;rdquo; crée un pointeur sur &amp;ldquo;mgo.Session&amp;rdquo;, l&amp;rsquo;adresse est donc référencée par cette fonction, le GC ne l&amp;rsquo;a pas encore collecté. Ensuite, elle est récupérée par &amp;ldquo;createData()&amp;rdquo;, l&amp;rsquo;adresse est donc encore référencée, toujours pas de collectage. Après que les deux fonctions aient utilisé cette adresse &amp;ldquo;s&amp;rdquo;, plus personne ne l&amp;rsquo;utilise. À partir de maintenant, le collector trouve cette adresse contenant un &amp;ldquo;&lt;em&gt;objet&lt;/em&gt;&amp;rdquo; que personne n&amp;rsquo;utilise, il va pouvoir utiliser la fonction qu&amp;rsquo;on lui a donné pour effectuer les opérations qu&amp;rsquo;on lui demande. Et par conséquent, ici, on ferme la connexion.&lt;/p&gt;
&lt;p&gt;Après cela, la mémoire est libérée.&lt;/p&gt;
&lt;p&gt;Mais y&amp;rsquo;a un effet de bord à prendre en compte&amp;hellip; Si jamais l&amp;rsquo;utilisateur a pensé à couper la connexion, le &amp;ldquo;finalizer&amp;rdquo; sera quand même appelé (car la référence est toujours présente lors de la passe de GC). On ajoute donc une opération (la fonction en argument) à exécuter lors d&amp;rsquo;une passe de GC. C&amp;rsquo;est le prix à payer&amp;hellip;&lt;/p&gt;
&lt;p&gt;Avant d&amp;rsquo;aller plus loin, &lt;strong&gt;je vous met en garde&lt;/strong&gt; et je vous demande de lire ce très bon article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lk4d4.darth.io/posts/finalizers/&#34;&gt;https://lk4d4.darth.io/posts/finalizers/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il faut effectivement utiliser cette fonction dans des cas précis, si et seulement si c&amp;rsquo;est vraiment nécessaire.&lt;/p&gt;
&lt;p&gt;Un autre article intéressant:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://making.pusher.com/golangs-real-time-gc-in-theory-and-practice/&#34;&gt;https://making.pusher.com/golangs-real-time-gc-in-theory-and-practice/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;le-nouveau-code&#34;&gt;le nouveau code&lt;/h1&gt;
&lt;p&gt;On a donc maintenant ce code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
	&amp;quot;log&amp;quot;
	&amp;quot;runtime&amp;quot;
	&amp;quot;time&amp;quot;

	mgo &amp;quot;gopkg.in/mgo.v2&amp;quot;
)

// Returns a NEW mongo session
func connect() *mgo.Session {
	s, _ := mgo.Dial(&amp;quot;127.0.0.1&amp;quot;)
    // clean session when GC pass
    runtime.SetFinalizer(s, func(s *mgo.Session) {
    	log.Println(&amp;quot;Close db&amp;quot;)
    	s.Close()
    })
	return s
}

// Create data in mongo
func createData(i interface{}) {
	s := connect()
	s.DB(&amp;quot;data&amp;quot;).C(&amp;quot;foo&amp;quot;).Insert(i)
	log.Println(&amp;quot;create done&amp;quot;)
}

func main() {
	for i := 0; i &amp;lt; 2000; i++ {
    	createData(map[string]string{
    		&amp;quot;user&amp;quot;: &amp;quot;foo&amp;quot;,
    		&amp;quot;date&amp;quot;: time.Now().String(),
    	})
    	time.Sleep(50 * time.Millisecond)
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Coupez le conteneur Docker mongo (parce qu&amp;rsquo;il est pété là hein&amp;hellip;) et relancez le. Puis on relance le programme: &lt;code&gt;go run main.go&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Après un certain temps (chez moi, après 200 connexions environs), je vois une pile de connexions se fermer. Je vous d&amp;rsquo;ailleurs le compteur de connexion active dans les logs Mongo descendre d&amp;rsquo;un coup.&lt;/p&gt;
&lt;p&gt;On a donc ici une manière assez élégante de nettoyer les connexions orphelines. Le but n&amp;rsquo;est pas de permettre à un utilisateur de ne pas fermer lui-même la connexion, mais si il l&amp;rsquo;a oublié alors on indique au GC qu&amp;rsquo;il pourra faire le ménage de cette manière. En gros, c&amp;rsquo;est &amp;ldquo;en cas d&amp;rsquo;oubli, on fixe plus tard&amp;rdquo;.&lt;/p&gt;
&lt;h1 id=&#34;aller-un-peu-plus-loin&#34;&gt;Aller un peu plus loin&lt;/h1&gt;
&lt;p&gt;Il est possible de forcer le garbage collector à nettoyer la mémoire en appelant &amp;ldquo;&lt;code&gt;runtime.GC()&lt;/code&gt;&amp;rdquo;, dans le cas où votre programme est relativement chargé, cela peut avoir son effet. Il faut simplement prévoir à quel moment lancer la commande, voir créer une goroutine qui le fait régulièrement.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est en général &lt;strong&gt;une mauvaise idée&lt;/strong&gt;, car normalement le GC passe nettoyer la mémoire tout seul comme un grand assez régulièrement. Mais il se peut que dans de rares cas ce soit vraiment nécessaire (j&amp;rsquo;ai eu ce cas malheureusement), et dans ce cas vous pouvez demander un nettoyage forcé.&lt;/p&gt;
&lt;h1 id=&#34;mais-on-le-fait-ou-pas-&#34;&gt;Mais on le fait ou pas ?&lt;/h1&gt;
&lt;p&gt;En général, il faut éviter. Gardez à l&amp;rsquo;esprit que le développeur qui bosse avec vous doit avoir les informations nécessaires pour faire ce qu&amp;rsquo;il faut. De ce fait, il faut préciser qu&amp;rsquo;après chaque connexion à la base de données il sera nécessaire de faire un bon vieux:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;defer s.Close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sauf que comme je le dis plus haut, un oubli peut arriver. Et si vous utilisez fortement des opérations qui ont un risque de faire un leak par oubli du développeur, là franchement je conseille de protéger l&amp;rsquo;exécution avec un &amp;ldquo;finalizer&amp;rdquo;. Si votre équipe a pensé à fermer les connexions, ça n&amp;rsquo;impactera pas les performances. Si ne serait-ce qu&amp;rsquo;une connexion est oubliée, au moins vous êtes certain qu&amp;rsquo;elle sera fermée du moment ou elle n&amp;rsquo;est plus du tout utilisée.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Je précise un point important quand même&lt;/strong&gt;: dans notre exemple, si je ferme la connexion après utilisation tout se passe bien. Mais si j&amp;rsquo;oublie de la fermer, elle reste active le temps que je GC fasse une passe. D&amp;rsquo;où l&amp;rsquo;intérêt de ne pas attendre que le GC passe et de fermer la connexion manuellement (avec un &lt;em&gt;defer&lt;/em&gt;). C&amp;rsquo;est bien plus optimal de faire cette opération au plus tôt !&lt;/p&gt;
&lt;p&gt;Ce que je dis, et je sais que j&amp;rsquo;insiste, c&amp;rsquo;est qu&amp;rsquo;un finalizer est une solution &amp;ldquo;de secours&amp;rdquo;, ça ne doit pas être le fonctionnement par défaut !&lt;/p&gt;
&lt;h1 id=&#34;simuler-un-destructeur&#34;&gt;Simuler un destructeur&lt;/h1&gt;
&lt;p&gt;Je suis pas fan de la simulation de POO en Go, mais comme le langage permet de faire du pseudo objet, on peut parfois avoir envie de travailler à la manière d&amp;rsquo;une classe avec destructeur.&lt;/p&gt;
&lt;p&gt;Dans l&amp;rsquo;absolu, un &amp;ldquo;finalizer&amp;rdquo; est aussi un moyen de simuler un destructeur, qu&amp;rsquo;on va appeler &amp;ldquo;Close&amp;rdquo; dans l&amp;rsquo;exemple ci-après:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type  User struc {
    Name string
}

func (u *User) Close() {
    log.Println(&amp;quot;Destruction of user&amp;quot;, u.Name)
}

func NewUser() *User {
    u := &amp;amp;User()
    runtime.SetFinalizer(u, func (u *User) {
        u.Close()
    })
    return u
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si on pense bien à utiliser &amp;ldquo;NewUser&amp;rdquo; (le constructeur), alors &lt;em&gt;l&amp;rsquo;objet&lt;/em&gt; retourné peut être détruit quand le garbage collector fera une passe, et seulement si l&amp;rsquo;adresse &amp;ldquo;u&amp;rdquo; n&amp;rsquo;est plus utilisée. Aussi, pensez à laisser la fonction de destruction accessible aux développeur pour qu&amp;rsquo;il puisse supprimer manuellement l&amp;rsquo;objet (ce qui est encore une fois la recommandation !).&lt;/p&gt;
&lt;p&gt;Donc, on pourra faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;// là le GC ne passera jamais (on a pas utilise de constructeur)
u := &amp;amp;User{}
defer u.Close()

// ou

u := NewUser()
// et laisser faire le GC 
// ou utiliser u.Close() manuellement
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La première méthode étant la plus judicieuse car elle n&amp;rsquo;attend pas une passe du GC. Mais encor faut-il que le développeur pense bien à utiliser &amp;ldquo;&lt;code&gt;u.Close()&lt;/code&gt;&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;une-autre-manière-de-voir-les-choses&#34;&gt;Une autre manière de voir les choses&lt;/h1&gt;
&lt;p&gt;Puisqu&amp;rsquo;on veut éviter de manipuler le GC, on peut adapter des pattern venus d&amp;rsquo;autres langages pour réduire l&amp;rsquo;impact. J&amp;rsquo;apprécie fortement le pattern &amp;ldquo;&lt;em&gt;with&lt;/em&gt;&amp;rdquo; de Python (qui existe dans d&amp;rsquo;autres langages) et on peut très facilement l&amp;rsquo;implémenter en Go.&lt;/p&gt;
&lt;p&gt;En premier lieu, on se prévoit une interface &amp;ldquo;&lt;code&gt;Closer&lt;/code&gt;&amp;rdquo; qui sait &amp;ldquo;fermer quelque chose&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Closer interface {
    Close()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reste à imaginer une fonction &amp;ldquo;With&amp;rdquo; qui va imiter ce que fait Python:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func With(c Closer, f func()) error {
    defer c.Close()
    f()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Admettez que c&amp;rsquo;était pas compliqué hein.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;mgo.Session&amp;rdquo; contient justement une méthode &amp;ldquo;Close&amp;rdquo; qui respecte l&amp;rsquo;interface, c&amp;rsquo;est voulu.&lt;/p&gt;
&lt;p&gt;Du coup, on peut coder notre fonction de création de données ainsi:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;sess := connect()
With(sess, func() {
	sess.DB(&amp;quot;data&amp;quot;).C(&amp;quot;foo&amp;quot;).Insert(i)
	log.Println(&amp;quot;create done&amp;quot;)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La cloture fonctionnera comme désiré, pas la peine de passer par de la manipulation de GC.&lt;/p&gt;
&lt;p&gt;Voilà l&amp;rsquo;exemple complet:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
	&amp;quot;log&amp;quot;
	&amp;quot;time&amp;quot;

	mgo &amp;quot;gopkg.in/mgo.v2&amp;quot;
)

type Closer interface {
	Close()
}

func With(c Closer, f func()) {
	defer c.Close()
	f()
}

func connect() *mgo.Session {
	s, _ := mgo.Dial(&amp;quot;127.0.0.1&amp;quot;)
	return s
}

func createData(i interface{}) {
	sess := connect()
	With(sess, func() {
		sess.DB(&amp;quot;data&amp;quot;).C(&amp;quot;foo&amp;quot;).Insert(i)
		log.Println(&amp;quot;create done&amp;quot;)
	})
}

func main() {
	for i := 0; i &amp;lt; 2000; i++ {
		createData(map[string]string{
			&amp;quot;user&amp;quot;: &amp;quot;foo&amp;quot;,
			&amp;quot;date&amp;quot;: time.Now().String(),
		})
		time.Sleep(50 * time.Millisecond)
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette fois, plus de problème, les connexions sont fermées en sortie de With, et le code est relativement clair.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;J&amp;rsquo;arrête pas de le dire, mais il faut que ce soit clair: il faut éviter d&amp;rsquo;utiliser SetFinalizer. Cela-dit, je sais que vous avez certainement rencontré un souci de variable non supprimée, de connexion qui persiste sans intérêt. Ainsi, on peut faire en sorte de contrôler le GC, de l&amp;rsquo;appeler explicitement et même de forcer des comportements de destruction.&lt;/p&gt;
&lt;p&gt;Dans la mesure du possible, j&amp;rsquo;estime qu&amp;rsquo;un développeur Go &lt;strong&gt;doit être informé des spécificités du langage&lt;/strong&gt; et par conséquent il doit connaitre les problématiques de fermeture de descripteurs. Prendre l&amp;rsquo;habitude d&amp;rsquo;ajouter un &amp;ldquo;&lt;code&gt;defer p.Close()&lt;/code&gt;&amp;rdquo; n&amp;rsquo;est pas une complexité insurmontable. Mais je suis aussi un défenseur du principe KISS, et je suis absolument d&amp;rsquo;accord avec le fait d&amp;rsquo;utiliser des patterns si et seulement si ils s&amp;rsquo;avèrent vraiment utiles et que le langage pose un souci. J&amp;rsquo;ai beau adorer Go, je ne peux pas défendre le fait qu&amp;rsquo;il soit aussi user-friendly que Python ou JS. Dans ce cas, j&amp;rsquo;admet volontier que certains patterns soient utiles et adaptés. Mais j&amp;rsquo;en reviens à mon &lt;a href=&#34;https://www.metal3d.org/ticket/les-designs-patterns-une-preuve-de-faiblesse-du-langage&#34;&gt;article sur les design patterns&lt;/a&gt; - donc, adapter un code source pour utiliser &amp;ldquo;With&amp;rdquo; tel que je le montre, ou ajouter un &amp;ldquo;finalizer&amp;rdquo; ne me pose pas plus de problème que ça à condition que ça ne devienne pas un foutoir ingérable et imbitable.&lt;/p&gt;
&lt;p&gt;Faites juste attention de ne pas en abuser. Soyez judicieux, simplement !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>POO vs Composinting</title>
      <link>https://www.metal3d.org/blog/2017/poo-vs-composinting/</link>
      <pubDate>Sat, 04 Mar 2017 21:11:15 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2017/poo-vs-composinting/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Si vous vous êtes intéressé au langage &amp;ldquo;Go&amp;rdquo;, alors vous devez savoir qu&amp;rsquo;il n&amp;rsquo;est pas un langage purement objet mais &amp;ldquo;de &lt;strong&gt;compositing&lt;/strong&gt;&amp;rdquo;. Mais quand vous codez en Go, vous avez cette impression de coder avec des classes et des objets. Alors c&amp;rsquo;est quoi la différence ?&lt;/p&gt;
&lt;p&gt;On va se réferer à la page de FAQ: &lt;a href=&#34;https://golang.org/doc/faq#Is_Go_an_object-oriented_language&#34;&gt;https://golang.org/doc/faq#Is_Go_an_object-oriented_language&lt;/a&gt; et on lit:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Is Go an object-oriented language?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;La réponse peut paraitre claire pour certains, pourtant la mailing-list est bourrée de la même question, &amp;ldquo;mais je comprend pas la différence puisqu&amp;rsquo;on peut hériter et utiliser des objets&amp;rdquo;. La phrase peut paraitre sensée mais elle est en opposition avec la réponse donnée dans la FAQ.&lt;/p&gt;
&lt;p&gt;Pour comprendre, il faut comparer Go avec un autre langage. On va utiliser Python, et faire de l&amp;rsquo;instrospection de type. Dans cet exemple, on va créer une classe Foo qui sait retourner le nom de la classe (donc &amp;ldquo;Foo&amp;rdquo;) et la dériver en Bar.&lt;/p&gt;
&lt;script src=&#34;https://repl.it/embed/GHXE/0.js&#34;&gt;&lt;/script&gt;
&lt;p&gt;Ok, donc en appelant la méthode &amp;ldquo;name&amp;rdquo; de l&amp;rsquo;instance de classe Bar, on appelle la méthode définie dans la classe &amp;ldquo;Foo&amp;rdquo;. Mais le résultat est bon: l&amp;rsquo;instance de classe Foo répond par &amp;ldquo;Foo&amp;rdquo; et l&amp;rsquo;instance de Bar répond par &amp;ldquo;Bar&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Faisons la même chose avec Go.&lt;/p&gt;
&lt;script src=&#34;https://repl.it/embed/GHX1/0.js&#34;&gt;&lt;/script&gt;
&lt;p&gt;Là, ça marche plus&amp;hellip; Les deux appels retournent &amp;ldquo;*main.Foo&amp;rdquo; !&lt;/p&gt;
&lt;p&gt;Mais pourquoi donc ?&lt;/p&gt;
&lt;p&gt;Le fait est que Go utilise le modèle &amp;ldquo;composite&amp;rdquo;, ce qui veut dire que la définition de Bar &lt;strong&gt;contient une instance de Foo&lt;/strong&gt;. Clairement, Bar n&amp;rsquo;hérite pas de Foo. Bar transporte une variable de type Foo (ou *Foo) dont on peut utiliser les méthodes sans utiliser le nom de variable. Mais en réalité, ce qu&amp;rsquo;il s&amp;rsquo;est passé, c&amp;rsquo;est que nous avons appelé &amp;ldquo;&lt;code&gt;new(Bar).Foo.Name()&lt;/code&gt;&amp;rdquo; - et c&amp;rsquo;est &lt;strong&gt;très différent&lt;/strong&gt; de l&amp;rsquo;héritage. Et on retrouve la réponse donnée dans la FAQ: &lt;strong&gt;les types n&amp;rsquo;héritent pas les uns des autres.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Alors qu&amp;rsquo;en Python, Bar &lt;strong&gt;a hérité de la méthode &amp;ldquo;name&amp;rdquo;&lt;/strong&gt;. Cela induit que Bar &amp;ldquo;réplique&amp;rdquo; la méthode pour son propre compte.&lt;/p&gt;
&lt;p&gt;En d&amp;rsquo;autres termes, il n&amp;rsquo;est pas possible pour une méthode de structure Foo, de connaitre la signature des objet &amp;ldquo;enfants&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est pour cela que le développement en Go &amp;ldquo;ressemble&amp;rdquo; à de la POO sans en être réellement.&lt;/p&gt;
&lt;p&gt;Et si vous pensez que ce modèle n&amp;rsquo;est pas une bonne idée, sachez qu&amp;rsquo;il a un sacré nombre d&amp;rsquo;avantages. Entre autre, le polymorphisme est plus facile à gérer, car en composant une structure avec d&amp;rsquo;autres structures, je peux m&amp;rsquo;assurer que la méthode que j&amp;rsquo;appelle provient bien du bon type qui la compose en appelant explicitement la méthode avec le nom du type inclu.&lt;/p&gt;
&lt;p&gt;Autre intérêt, je peux modifier l&amp;rsquo;instance du type qui est inclu ou encore l&amp;rsquo;enregistrer dans une autre instance. Ce qui veut dire que je peux faire en sorte que deux instances de Bar ait des propriétés différentes, mais qu&amp;rsquo;un appel à la méthode de Foo utilise le même &amp;ldquo;objet&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Et j&amp;rsquo;en passe.&lt;/p&gt;
&lt;p&gt;Donc, certes, quand on est habitué à de la POO &lt;em&gt;traditionnelle&lt;/em&gt; tout cela peut être déroutant, mais avec l&amp;rsquo;habitude on trouve son compte dans le modèle composite, et on trouve même ça très pratique. Ce n&amp;rsquo;est donc pas un défaut, ni une atout, c&amp;rsquo;est juste un design de langage différent avec ses spécifités à maitriser.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Faire une présentation avec deux écrans sous linux</title>
      <link>https://www.metal3d.org/blog/2016/faire-une-pr%C3%A9sentation-avec-deux-%C3%A9crans-sous-linux/</link>
      <pubDate>Wed, 02 Nov 2016 21:47:50 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/faire-une-pr%C3%A9sentation-avec-deux-%C3%A9crans-sous-linux/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Demain matin je vais présenter au &lt;a href=&#34;http://www.blendwebmix.com/&#34;&gt;BlendWebMix&lt;/a&gt; une petite conférence mais une question s&amp;rsquo;est posé à moi&amp;hellip; Comment faire en sorte d&amp;rsquo;utiliser un bureau étendu tout en faisant un miroir de certaines fenêtres ? Et bien il existe plein de solutions sans avoir besoin d&amp;rsquo;installer un client particulier. Suivez-moi.&lt;/p&gt;
&lt;p&gt;Cela fait&amp;hellip; houlala plusieurs mois que je n&amp;rsquo;ai rien posté. J&amp;rsquo;en parlais avec &lt;a href=&#34;http://www.xavierdeneux.fr/&#34;&gt;Xavier Deneux&lt;/a&gt;, un ami, ex-collègue, revu avec plaisirs ce mercredi, et je me suis dit ce soir que l&amp;rsquo;occasion est trop belle pour ne pas poster un truc que j&amp;rsquo;utilise régulièrement quand je donne des conférences.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;objet de la conférence de demain (le jeudi 3 novembre 2016 - Kubernetes et le déploiement continu) fera l&amp;rsquo;objet un autre billet.&lt;/p&gt;
&lt;h1 id=&#34;bon-comment-ça-se-passe-&#34;&gt;Bon, comment ça se passe ?&lt;/h1&gt;
&lt;p&gt;En général, si vous êtes conférencier, vous allez avoir à disposition un appareil de projection qui se trouve dans votre dos. Vous allez brancher votre PC et avoir deux possibilités:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;utiliser un mode &amp;ldquo;miroir&amp;rdquo;, donc l&amp;rsquo;écran de votre portable est projeté directement sur l&amp;rsquo;écran principal, et par conséquent ce que vous voyez sur votre ordinateur est ce que voit le public&lt;/li&gt;
&lt;li&gt;utiliser un mode &amp;ldquo;étendu&amp;rdquo;, ce qui vous permet d&amp;rsquo;avoir un écran différent de celui projeté&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans le premier cas, pas de souci, si ce n&amp;rsquo;est que vous ne pouvez pas mettre l&amp;rsquo;écran &amp;ldquo;secondaire&amp;rdquo; de LibreOffice ou RevealJS afin de voir la diapo suivante, vos notes, etc&amp;hellip; et ça me plait pas&lt;/p&gt;
&lt;p&gt;Dans le second cas, si vous n&amp;rsquo;avez que des slides à montrer, pas de souci. Mais moi je fais souvent des démos, et j&amp;rsquo;ai besoin d&amp;rsquo;un terminal (pour vim, des sorties de commandes, ou autre&amp;hellip;) ou encore d&amp;rsquo;un navigateur pour des interfaces Web&amp;hellip; Donc à moins de passer d&amp;rsquo;un mode à l&amp;rsquo;autre, rien de pratique.&lt;/p&gt;
&lt;p&gt;Il existe heureusement des solutions.&lt;/p&gt;
&lt;h1 id=&#34;terminal-mirroring-avec-script&#34;&gt;Terminal Mirroring avec &amp;ldquo;script&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;Le terminal mirroring est très simple, dans le terminal que vous voulez &amp;ldquo;partager&amp;rdquo;, vous tapez:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;script -f /tmp/term
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ouvrez ensuite un autre terminal (à montrer au public) dans lequel vous tapez simplement:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tail -f /tmp/term
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà, tout ce que vous tapez dans le terminal de base est vu en temps réel sur le terminal 2. Pour terminer, dans le premier terminal, vous pouvez taper CTRL+D, ce qui va couper la commande &amp;ldquo;script&amp;rdquo; et donc fermer &amp;ldquo;/tmp/term&amp;rdquo;. Simple et efficace.&lt;/p&gt;
&lt;h1 id=&#34;et-pour-nimporte-quelle-fenêtre-&#34;&gt;Et pour n&amp;rsquo;importe quelle fenêtre ?&lt;/h1&gt;
&lt;p&gt;Pour un terminal c&amp;rsquo;est simple, il existe une méthode plus &amp;ldquo;universelle&amp;rdquo; qui fonctionne vraiment très bien. Ça va vous paraitre fou, bête, vous allez même vous dire &amp;ldquo;mais pourquoi j&amp;rsquo;y ai pas pensé avant ?&amp;rdquo; (chose que je me suis dit la première fois): &lt;strong&gt;streamer une fenêtre&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Et gstreamer est là pour vous ! (encore lui ?!)&lt;/p&gt;
&lt;p&gt;Bon, l&amp;rsquo;idée est d&amp;rsquo;avoir l&amp;rsquo;identifiant d&amp;rsquo;une fenêtre, et d&amp;rsquo;utiliser le plugin &amp;ldquo;ximagesrc&amp;rdquo; puis de tout envoyer dans un &amp;ldquo;ximagesink&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://plant.metal3d.org/png/BQMQpgdgVwLgTmAlAAgLSoHzOAcwM7xgCGAtmHIgFCX6GnlpbAAeAliUTmHnAMZVsOXHr0bYAbqwAmYAPa9ZEceRhVJM+YuVwYY4OrkI8vIgBsklA7KMnzaTNkGdurCAGtEQA&#34; alt=&#34;Flux Gstreamer&#34;&gt;&lt;/p&gt;
&lt;p&gt;Le seul souci, avoir l&amp;rsquo;ID de fenêtre. Pour ça il faut utiliser une commande assez simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;xwininfo | awk &#39;/Window id/{print $4}&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Votre curseur se transforme en petite croix, cliquez sur une fenêtre pour sélectionner la fenêtre voulue.&lt;/p&gt;
&lt;p&gt;Ensuite coller cet id dans la commande suivante (à la place de ICI_ID):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gst-launch-1.0 ximagesrc xid=ICI_ID ! queue2 ! videoscale ! videoconvert ! queue2 ! ximagesink sync=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notez que j&amp;rsquo;ai ajouté deux &amp;ldquo;pads&amp;rdquo; nommés &amp;ldquo;queue2&amp;rdquo; pour soulager un peu le CPU. Je n&amp;rsquo;ai pas d&amp;rsquo;explication claire sur le sujet, j&amp;rsquo;ai juste remarqué que, voilà, ça souffle moins coté ventilo&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sync=false&lt;/code&gt; permet de laisser tomber des trames vidéo si la vidéo est trop &amp;ldquo;rapide&amp;rdquo; pour la lecture, et croyez-moi sans cette option vous aller avoir un paquet de warning dans la console. Et une fois de plus, cette option fera en sorte que votre CPU vous fera des bisous.&lt;/p&gt;
&lt;p&gt;Cela ouvre une fenêtre vidéo avec, vous l&amp;rsquo;avez compris, la vidéo de capture en temps-réel de la fenêtre choisie.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/455135ad-3ce0-40d2-ae36-91fb1f931e47-mirror.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Reste à se faire ça en script bash, en le placant dans votre PATH (moi j&amp;rsquo;utilise ~/.local/bin/mirror-this):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash

WID=$(xwininfo | awk &#39;/Window id/{print $4}&#39;)
gst-launch-1.0 ximagesrc xid=$WID ! queue2 ! videoscale ! videoconvert ! queue2 ! ximagesink sync=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pensez à faire un &lt;code&gt;chmod +x ~/.local/bin/mirror-this&lt;/code&gt; ou sur l&amp;rsquo;emplacement choisi pour le script !&lt;/p&gt;
&lt;p&gt;Du coup, quand je dois montrer une fenêtre, je lance ma commande, je clique sur la fenêtre à partager, je la place sur l&amp;rsquo;écran projeté au mur, et quand j&amp;rsquo;ai fini, hop un petit CTRL+C dans mon terminal.&lt;/p&gt;
&lt;p&gt;Et du coup, là, plus de commande &amp;ldquo;script&amp;rdquo;, je l&amp;rsquo;utilise aussi pour mes terminaux.&lt;/p&gt;
&lt;h1 id=&#34;résultat&#34;&gt;Résultat&lt;/h1&gt;
&lt;p&gt;Tout simplement, je peux utiliser deux écrans différents, un pour avoir mes notes, l&amp;rsquo;écran de contrôle de mes slides, et pourtant je peux partager certaines choses avec l&amp;rsquo;auditoire.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;espère vous avoir donné de quoi vous amuser :)&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Un beau document en LaTeX</title>
      <link>https://www.metal3d.org/blog/2016/un-beau-document-en-latex/</link>
      <pubDate>Sun, 08 May 2016 15:36:30 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/un-beau-document-en-latex/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Faire un joli document avec une couverture &amp;ldquo;design&amp;rdquo;, des pages de chapitre originales, le tout en LaTeX, c&amp;rsquo;est ce que
nous allons voir aujourd&amp;rsquo;hui. Quand on se penche sur la puissance de certains packages LaTeX on se rend compte que &amp;ldquo;tout
est possible&amp;rdquo;. C&amp;rsquo;est là la grande force de ce &amp;ldquo;langage&amp;rdquo; créé en 1983!&lt;/p&gt;
&lt;p&gt;Cet article va vous permettre de créer ce thème LaTeX :&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/66859127-ddc3-4fae-8d5f-b5f2b5563ecd-exemple-theme.png&#34; alt=&#34;Le fichier PDF final&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;latex-&#34;&gt;LaTeX ?&lt;/h2&gt;
&lt;p&gt;Pour commencer, résumons ce qu&amp;rsquo;est LaTeX. C&amp;rsquo;est un langage de &amp;ldquo;balise&amp;rdquo; qui permet de générer des documents de différents
types tels que des rapports, des articles, des livres, des présentations (slide type Beamer), etc. Il a surtout été
apprécié par les doctorants scientifiques par la qualité de présentation des équations.&lt;/p&gt;
&lt;p&gt;TeX est né en 1977 et LaTeX, la suite de macros pour faciliter son écriture, date lui de 1983. Il y a eu très peu
d&amp;rsquo;évolution majeure ce qui le rend très stable, presque dépourvu de bug, et cela assure aussi une pérennité de package
rarement égalée.&lt;/p&gt;
&lt;p&gt;Dans ces packages, on trouve des styles, des macros qui permettent de colorer du code tel que l&amp;rsquo;excellent &amp;ldquo;minted&amp;rdquo;, mais
aussi un package que j&amp;rsquo;adore : &amp;ldquo;&lt;a href=&#34;http://math.et.info.free.fr/TikZ/bdd/TikZ-Impatient.pdf&#34;&gt;TikZ&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;TikZ est un package de création de &amp;ldquo;dessin&amp;rdquo;. Cela va de simples formes (rectangle, cercles&amp;hellip;) à des diagrammes,
des graphiques, et du placement de nœuds (pour faire des mind-map par exemple). Plus on se penche sur les possibilités
du package, plus on s&amp;rsquo;étonne de la puissance de l&amp;rsquo;outil.&lt;/p&gt;
&lt;p&gt;Bref, je viens d&amp;rsquo;être amené à refaire du LaTeX. J&amp;rsquo;ai retrouvé ce plaisir de taper du texte sans me poser la question de
&amp;ldquo;comment ça va rendre au final&amp;rdquo;, sachant que je pouvais me poser sur le &amp;ldquo;design&amp;rdquo; après coup. Autant vous dire, je ne vais
pas vous mentir, quand vous décidez de passer à la phase &amp;ldquo;design&amp;rdquo; vous allez y passer un moment. Car, bien que je sois un
fervent défenseur de LaTeX et de sa productivité, je ne suis pas de ceux qui estiment que c&amp;rsquo;est magique. Non, il faut se
pencher sur de la documentation, il faut tester, se former, se planter 20 fois, recommencer&amp;hellip; Mais ce qui est
intéressant c&amp;rsquo;est que je ne crains jamais de tuer mon projet. Au pire, je supprime les packages qui m&amp;rsquo;embêtent, au mieux
je vire mes adaptations.&lt;/p&gt;
&lt;h2 id=&#34;pour-bien-faire-les-choses&#34;&gt;Pour bien faire les choses&lt;/h2&gt;
&lt;p&gt;Pour commencer, il faut toujours penser à séparer la description du document, le document et le style. Personnellement
je fais ainsi, un fichier &amp;ldquo;main.tex&amp;rdquo; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\documentclass[12pt]{book}
\usepackage[frenchb]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

% là je mets tout ce qui est voué
% à styliser mon document
\input{_monstyle}

\begin{document}

\maketitle
\tableofcontents

%% ici j&#39;inclu mon contenu
\input{_structure}

\end{document}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;N&amp;rsquo;oubliesz pas:&lt;/strong&gt; on inclue les fichiers sans donner l&amp;rsquo;extension, donc j&amp;rsquo;ai bien deux fichiers &amp;ldquo;_monsytle.tex&amp;rdquo; et &amp;ldquo;_structure.tex&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;intérêt de cette manière d&amp;rsquo;écrire un document est simplement que si jamais mon style est inutilisable, je peux commenter l&amp;rsquo;inclusion. Si mon fichier principal ne me plait plus, je peux refaire un &amp;ldquo;main.tex&amp;rdquo; simple et inclure ma structure. C&amp;rsquo;est donc bien &amp;ldquo;_structure.tex&amp;rdquo; qui est mon élément central, c&amp;rsquo;est-à-dire le contenu.&lt;/p&gt;
&lt;p&gt;Le fichier de structure est simplement une liste d&amp;rsquo;inclusion d&amp;rsquo;autre partie du document:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;% j&#39;inclue ici mes docments, ou
\input{partie1}
\input{les_caramels_mous}

% une autre manière que j&#39;utilise:
\chapter{Introduction}
    \section{Qui je suis}
    \input{moi}

    \section{Ce document}
    \input{apropos}

\chapter{Les caramels mous}
    \section{C&#39;est bon ou pas ?}
    \input{gouts_et_couleurs}

    \section{etc...}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A vous de choisir votre méthode de construction. Il y en a qui tape tout le document dans &amp;ldquo;_structure&amp;rdquo;, d&amp;rsquo;autres incluent
les fichiers les uns après les autres ou représentent la table des matières comme je le fais souvent. Là c&amp;rsquo;est une
question de méthode et de gout.&lt;/p&gt;
&lt;p&gt;Juste un petit point à préciser, j&amp;rsquo;utilise la classe de document &amp;ldquo;book&amp;rdquo;, mais si les alternances de marge de page
&amp;ldquo;doite-gauche&amp;rdquo; ne vous intéresse pas, passez la classe de document à &amp;ldquo;report&amp;rdquo;. Cette classe permet de faire une page de
couverture et des chapitres à l&amp;rsquo;inverse de &amp;ldquo;article&amp;rdquo; par exemple. Mais encore une fois, c&amp;rsquo;est à vous de voir.&lt;/p&gt;
&lt;p&gt;Bon, on va passer au design.&lt;/p&gt;
&lt;h2 id=&#34;packages-utiles-au-design&#34;&gt;Packages utiles au design&lt;/h2&gt;
&lt;p&gt;LaTeX génère par défaut un document très sobre. Personnellement je trouve la forme de base austère. Pas moche mais pas
sexy. À coté d&amp;rsquo;un document généré avec &lt;strike&gt;Word&lt;/strike&gt; LibreOffice, qui a des entêtes avec des images, des
logos&amp;hellip; on ne peut pas dire le contraire. Mais je vous le répète, LaTeX permet de faire énormément de choses.&lt;/p&gt;
&lt;p&gt;Pour nous amuser un peu, on va changer la police de caractères, et intégrer quelques packages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;fancyhdr&amp;rdquo; pour changer les entêtes et pieds de page avec aisance&lt;/li&gt;
&lt;li&gt;&amp;ldquo;TikZ&amp;rdquo; qui va nous permettre de dessiner sur nos pages&lt;/li&gt;
&lt;li&gt;&amp;ldquo;titlesec&amp;rdquo; pour changer la forme des titres plus facilement&lt;/li&gt;
&lt;li&gt;&amp;ldquo;color&amp;rdquo; pour créer des couleurs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ouvrons notre fameux fichier &amp;ldquo;_monstyle.tex&amp;rdquo;, et commençons:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;% change la fonte
\usepackage[default]{sourcesanspro}

% on insert TikZ et des sous-packages
% très utiles
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{tikzpagenodes}

% modification des titres
\usepackage{titlesec}

% création de couleurs
\usepackage{color}

% entêtes et pieds de page
\usepackage{fancyhdr}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ces packages donnent accès à des nouvelles directives (ou commandes) que nous allons pouvoir utiliser. Sachez que pour
90% de mes documents j&amp;rsquo;inclue ces packages.&lt;/p&gt;
&lt;h2 id=&#34;quelques-couleurs&#34;&gt;Quelques couleurs&lt;/h2&gt;
&lt;p&gt;On va définir une ou deux couleurs pour notre document, je vais définir un blanc pur et un bleu agréable à mes yeux.
Dans &amp;ldquo;_monstyle&amp;rdquo;, toujours, on insert:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\definecolor{white}{rgb}{1,1,1}
\definecolor{myblue}{RGB}{126,136,150}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;un-petit-mot-sur-tikz&#34;&gt;Un petit mot sur TikZ&lt;/h2&gt;
&lt;p&gt;À l&amp;rsquo;origine, TikZ sert surtout à dessiner des diagrammes, des graphiques, etc&amp;hellip; en tant que figure dans le document.
Mais son uilisation ne se limite pas à cela. TikZ est un &amp;ldquo;environnement&amp;rdquo;, c&amp;rsquo;est-à-dire qu&amp;rsquo;il définie sont propre espace
de travail. LaTeX propose d&amp;rsquo;autres environnements tels que les entêtes, les pieds de pages, les marges, la page de
titre, etc. Il suffit donc de placer l&amp;rsquo;environnement TikZ dans un environnement choisi pour dessiner où l&amp;rsquo;on veut.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est ce que nous allons faire.&lt;/p&gt;
&lt;h2 id=&#34;chapitres&#34;&gt;Chapitres&lt;/h2&gt;
&lt;p&gt;Les pages de chapitres sont franchement les plus difficiles à modifier. Mais avec un peu de bon sens et des tests, on y
arrive. De base, LaTeX propose une page de chapitre très simple mais j&amp;rsquo;avais envie de bien les &amp;ldquo;extraire&amp;rdquo; pour que le
lecteur ne s&amp;rsquo;ennuie pas.&lt;/p&gt;
&lt;p&gt;Le package &amp;ldquo;titlesec&amp;rdquo; fourni (entre autre) une commande un peu violente à lire, mais qui s&amp;rsquo;avère finalement plus simple
qu&amp;rsquo;on le pense:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\titleformat{command}[shape]{format}{label}{sep}{before}[after]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La &lt;a href=&#34;http://www.ctex.org/documents/packages/layout/titlesec.pdf&#34;&gt;documentation&lt;/a&gt; est très complète, je vous conseille d&amp;rsquo;aller la lire.&lt;/p&gt;
&lt;p&gt;Pour faire court, &amp;ldquo;command&amp;rdquo; est la commande titre à changer, on va utiliser &amp;ldquo;\chapter&amp;rdquo;, shape est optionnel, vient
alors le &amp;ldquo;format&amp;rdquo; qui défini comment afficher le titre (couleur, taille, etc.)&lt;/p&gt;
&lt;p&gt;Ensuite, le &amp;ldquo;label&amp;rdquo; pour définir le titre, &amp;ldquo;sep&amp;rdquo; défini la séparation vertical entre le document et le titre, et
&amp;ldquo;before&amp;rdquo; va nous permettre de définir ce qu&amp;rsquo;on place &amp;ldquo;avant&amp;rdquo; le chapitre. C&amp;rsquo;est ce dernier bloc qui va être le sujet de
toute notre attention.&lt;/p&gt;
&lt;p&gt;Comme &amp;ldquo;shape&amp;rdquo; est optionnel, tout comme &amp;ldquo;after&amp;rdquo;, on va juste faire ceci:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\titleformat{command}
    {format}
    {label}
    {sep}
    {before}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\titleformat{\chapter}
    {\Huge\color{white}}
    {\thechapter}
    {0}
    {Et là notre dessin... on y vient}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le dessin avec TikZ va être un peu déroutant au premier abord de la lecture, mais surtout ne paniquez pas.&lt;/p&gt;
&lt;h2 id=&#34;le-dessin-avec-tikz&#34;&gt;Le dessin avec TikZ&lt;/h2&gt;
&lt;p&gt;Dessiner avec TikZ n&amp;rsquo;est pas &amp;ldquo;intuitif&amp;rdquo;, bien que je ne vois pas comment on pourrait faire plus simple en LaTeX. L&amp;rsquo;idée est que vous allez définir un canva de travail et dessiner dedans. Vous pouvez y injecter des textes, des images, dessiner des traits, remplir des formes, relier des noeuds, etc.&lt;/p&gt;
&lt;p&gt;Bref, nous allons nous amuser à dessiner un quadrilatère qui va partir du coin haut-gauche, aller au coin haut-droit, descendre de quelques lignes, et aller rejoindre le bord gauche en oblique.&lt;/p&gt;
&lt;p&gt;Si je pars du principe que la page est en &amp;ldquo;position absolue&amp;rdquo; pour ne pas perdre les fans de CSS, on va rejoindre 4 points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0,0&lt;/li&gt;
&lt;li&gt;largeur de page, 0&lt;/li&gt;
&lt;li&gt;largeur de page, 8&lt;/li&gt;
&lt;li&gt;0, 10&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sauf que&amp;hellip;&lt;/p&gt;
&lt;p&gt;Si je pars de 0,0, TikZ va partir de la position &amp;ldquo;0,0&amp;rdquo; du texte&amp;hellip; Heureusement, TikZ propose des positions cardinales qui vont nous aider. Par exemple &amp;ldquo;current page.north west&amp;rdquo; correspond au coin haut-gauche de la page (nord ouest).&lt;/p&gt;
&lt;p&gt;Voilà le code de dessin, qu&amp;rsquo;on va injecter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\begin{tikzpicture}[remember picture,overlay]
  \draw[fill=myblue] (current page.north west)
    -- (current page.north east)
    -- ++ (0, -8)
    -- ++ (-\paperwidth, -2)
    -- cycle;
\end{tikzpicture}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, je vous vois somber. C&amp;rsquo;est pourtant super simple, j&amp;rsquo;ai ajouté un &amp;ldquo;environnement&amp;rdquo; tikzpicture et je lui dis de:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;se souvenir des positions des éléments et de leurs noms (on ne va pas trop en parler dans cet article)&lt;/li&gt;
&lt;li&gt;de ne pas se baser sur les positions du &amp;ldquo;document&amp;rdquo; mais de passer en mode &amp;ldquo;overlay&amp;rdquo;, c&amp;rsquo;est-à-dire &amp;ldquo;sur la page&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;utilise ensuite la commande &amp;ldquo;\draw&amp;rdquo; en lui disant que le dessin dois se remplir de la couleur &amp;ldquo;myblue&amp;rdquo; qu&amp;rsquo;on a défini
plus tôt. Et je lui donne les positions. Je joins les points par des &amp;ldquo;&amp;ndash;&amp;rdquo;, ce qui signifie &amp;ldquo;trace le trait&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;option &amp;ldquo;++&amp;rdquo; veut simplement dire: au lieu d&amp;rsquo;aller au point en position absolue, vas-y en position relative à la
position en cours.&lt;/p&gt;
&lt;p&gt;Ainsi, quand j&amp;rsquo;ai inséré &amp;ldquo;&lt;code&gt;-- ++ (0, -8)&lt;/code&gt;&amp;rdquo; je demande à TikZ de ne pas bouger en &amp;ldquo;x&amp;rdquo; mais de descendre de &amp;ldquo;8&amp;rdquo; vers le
bas. Alors que &amp;ldquo;&lt;code&gt;-- (current page.north east)&lt;/code&gt;&amp;rdquo; a demandé à TikZ de déplacer le crayon à ce point sans prendre en
considération d&amp;rsquo;où je viens.&lt;/p&gt;
&lt;p&gt;Enfin, la commande &amp;ldquo;cycle&amp;rdquo; demande simplement de fermer la forme. En gros, rejoindre &lt;strong&gt;le dernier point au premier point
déclaré&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ça ressemble à un peu à du &amp;ldquo;&lt;a href=&#34;http://tortue-logo.fr/fr/tortue-logo&#34;&gt;tortue logo&lt;/a&gt;&amp;rdquo; cette affaire :)&lt;/p&gt;
&lt;p&gt;Bon, et bien on a plus qu&amp;rsquo;à poser ce super dessin dans notre bloc de chapitre:&lt;/p&gt;
&lt;p class=&#34;image-block right&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-1f0b4fc1-8a66-47db-b852-7e0f800b5318.png&#34; alt=&#34;Le chapitre&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\titleformat{\chapter} % type
    {\Huge\color{white}} % style
    {\thechapter} % quoi afficher
    {0} % espacement
    {\begin{tikzpicture}[remember picture,overlay]
      \draw[fill=myblue] (current page.north west)
        -- (current page.north east)
        -- ++ (0, -8)
        -- ++ (-\paperwidth, -2)
        -- cycle;
    \end{tikzpicture}
    } % &amp;quot;before&amp;quot;

% on déplace un peu le titre vers le haut
\titlespacing*{\chapter}{-18pt}{0pt}{50pt}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oui, comme ça de visu ça peut faire peur, mais prenez le temps de repérer les blocs, de comprendre, vous allez voir que
ce n&amp;rsquo;est finalement pas si compliqué.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai déplacé le titre vers le haut de -18pt tout simplement parce que notre dessin se pose &amp;ldquo;avant&amp;rdquo; le titre, et par
conséquent le texte apparait après. C&amp;rsquo;est en quelque sorte un &amp;ldquo;margin-top&amp;rdquo; en CSS. Et le &amp;ldquo;0pt&amp;rdquo; c&amp;rsquo;est l&amp;rsquo;écart à gauche,
le &amp;ldquo;padding-left&amp;rdquo; en CSS. Le &amp;ldquo;50pt&amp;rdquo; est l&amp;rsquo;écart que je force &amp;ldquo;après&amp;rdquo; le titre, là on parle de &amp;ldquo;padding-bottom&amp;rdquo; en CSS.&lt;/p&gt;
&lt;p&gt;Et voilà ! On a dessiné notre page de chapitre. Passons aux autres pages.&lt;/p&gt;
&lt;h2 id=&#34;entêtes-et-pieds-de-pages&#34;&gt;Entêtes et Pieds de pages&lt;/h2&gt;
&lt;p&gt;La partie la plus compliquée est passée, on passe maintenant aux pages. J&amp;rsquo;ai envie de dessiner une image dans l&amp;rsquo;entête
qui fasse la taille de la page, bord à bord.&lt;/p&gt;
&lt;h3 id=&#34;entêtes&#34;&gt;Entêtes&lt;/h3&gt;
&lt;p&gt;J&amp;rsquo;ai donc créé une image à la va-vite dans Gimp, assez grande pour éviter de pixeliser. On va l&amp;rsquo;injecter encore une
fois grâce à TikZ. Le paquet &amp;ldquo;fancyhrd&amp;rdquo; va nous permettre de modifier l&amp;rsquo;entête.&lt;/p&gt;
&lt;p&gt;Petite astuce, pour initialiser les entête et pieds de page à vide, on utilise &amp;ldquo;\fancyhf{}&amp;rdquo; ce qui veut dire, mot pour
mot, &amp;ldquo;défini &lt;strong&gt;h&lt;/strong&gt;eader et &lt;strong&gt;f&lt;/strong&gt;ooter à vide&amp;rdquo;. Autre chose, la barre qui sépare l&amp;rsquo;entête et le pied de page du texte
nous gêne, donc on va la mettre à 0pt.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;% à placer après l&#39;insertion de
% package &amp;quot;fancyhdr&amp;quot;
\pagestyle{fancyplain} % ou &amp;quot;fancy&amp;quot;, testez
\fancyhf{} % vire le contenu de header et footer
\renewcommand{\headrulewidth}{0pt} % vire la barre de séparation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, on définie l&amp;rsquo;entête. Fancyhdr permet de placer des trucs à droite, à gauche et au centre. On va utiliser le
bloc central, mais c&amp;rsquo;est absolument arbitraire. On déclare notre entête avec &amp;ldquo;&lt;code&gt;\chead{...}&lt;/code&gt;&amp;rdquo; qui est un alias
de &amp;ldquo;&lt;code&gt;\fancyhead[C]&lt;/code&gt;&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\chead{%
    \begin{tikzpicture}[remember picture, overlay]
    \node [yshift=-100, inner sep=0, outer sep=0]
        at (current page.north){\includegraphics[width=\paperwidth]{head.png}};
    \end{tikzpicture}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bon je vous avoue que j&amp;rsquo;ai &amp;ldquo;tatoné&amp;rdquo; un peu pour trouver le &amp;ldquo;yshift&amp;rdquo;. Sans cela, l&amp;rsquo;image se place bien au &amp;ldquo;nord&amp;rdquo; de la
page, mais c&amp;rsquo;est le centre de l&amp;rsquo;image qui est placé à cet endroit, donc je demande à TikZ de &amp;ldquo;descendre&amp;rdquo; l&amp;rsquo;image un peu
(un genre de margin-top), ce qui fait que l&amp;rsquo;image est légèrement plus basse. Mon image est relativement large et haute,
donc &amp;ldquo;-100&amp;rdquo; est une très grosse valeur. Mais je voulais que l&amp;rsquo;image déborde sur le texte, l&amp;rsquo;effet est voulu.&lt;/p&gt;
&lt;p&gt;En ce qui concerne les &amp;ldquo;inner et outer sep&amp;rdquo;, ce sont en gros les marges et &amp;ldquo;paddings&amp;rdquo; que je le mets à 0. Encore une fois,
je ne peux que vous conseiller de modifier les valeurs pour comprendre le comportement.&lt;/p&gt;
&lt;p&gt;Pour que l&amp;rsquo;image prenne toute la largeur de la page, je lui donne une largeur qui vaut &amp;ldquo;&lt;code&gt;\paperwidth&lt;/code&gt;&amp;rdquo;. De cette manière si vous avez une partie de l&amp;rsquo;image qui doit bien atteindre le bord droit de la page, elle sera bien collée à droite.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est pas mal. Mais c&amp;rsquo;est vide.&lt;/p&gt;
&lt;p&gt;On va ajouter le titre du chapitre sur l&amp;rsquo;image avec un peu de couleur. À savoir que fancyhdr propose des commandes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;&lt;code&gt;\leftmark&lt;/code&gt;&amp;rdquo; est ce qu&amp;rsquo;il devrait afficher sur les pags de &amp;ldquo;gauche&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;&lt;code&gt;\rightmark&lt;/code&gt;&amp;rdquo; est ce qu&amp;rsquo;il devrait afficher sur les pags de &amp;ldquo;gauche&amp;rdquo;&lt;/li&gt;
&lt;li&gt;et d&amp;rsquo;autre, voir la documentation &lt;a href=&#34;http://texdoc.net/texmf-dist/doc/latex/fancyhdr/fancyhdr.pdf&#34;&gt;http://texdoc.net/texmf-dist/doc/latex/fancyhdr/fancyhdr.pdf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bref, voilà un exemple:&lt;/p&gt;
&lt;p class=&#34;image-block right&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-5cdb96ec-54ae-464a-8652-864555a1f79d.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\chead{%
    \begin{tikzpicture}[remember picture, overlay]
    \node [yshift=-100, inner sep=0, outer sep=0]
        at (current page.north){\includegraphics[width=\paperwidth]{head.png}};
    \node[yshift=-40,rotate=5, text width=0.5\paperwidth]
        at (current page.north) {%
            \Huge
            \textbf{``} % un guillemet à l&#39;envers
            \textcolor{myblue}{\leftmark} % le titre
            \textbf{&#39;&#39;} % un guillemet à l&#39;endroit
        };
    \end{tikzpicture}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et oui, si vous avez bien lu l&amp;rsquo;exemple, je fais tourner le texte de 5 degrés, j&amp;rsquo;aime bien :)&lt;/p&gt;
&lt;p&gt;Vous commencez à comprendre à quel point on peut aller loin ? Là j&amp;rsquo;ai mis une image, mais rien ne vous empêche de
dessiner avec TikZ dans l&amp;rsquo;entête.&lt;/p&gt;
&lt;p&gt;On passe au pied de page.&lt;/p&gt;
&lt;h3 id=&#34;pieds-de-pages&#34;&gt;Pieds de pages&lt;/h3&gt;
&lt;p&gt;On va faire un truc un peu comme pour les pages de titre de chapitre, on va dessiner une forme, et y coller du contenu.&lt;/p&gt;
&lt;p&gt;Alors, un petit rappel à ceux qui utilisent Word ou LibreOffice (je n&amp;rsquo;ai rien contre), n&amp;rsquo;oubliez pas que LaTeX est
vraiment un outil d&amp;rsquo;édition. De ce fait, on pense aux pages de droite et gauche, et notamment on les désigne comme étant
les pages &amp;ldquo;paires&amp;rdquo; et &amp;ldquo;impaires&amp;rdquo;. Rappelez-vous que impair en anglais ce dit &amp;ldquo;Odd&amp;rdquo;, et pair &amp;ldquo;Even&amp;rdquo;. C&amp;rsquo;est important pour
la suite.&lt;/p&gt;
&lt;p&gt;Je pars du principe que nous allons utiliser la classe de document &amp;ldquo;book&amp;rdquo;, qui change les marges des pages paires et
impaires. Donc, je vais moi aussi gérer les pieds de pages correctement en orientant la forme dessinées un coup dans un
sens, un coup dans l&amp;rsquo;autre, et placer mon numéro de page un coup au bord gauche (pages paires) et un coup au bord droit
(pages impaires)&lt;/p&gt;
&lt;p&gt;Je ne vais pas tout détailler, référez-vous à notre exemple sur les entêtes, je ne fais rien de différent à part
modifier les coordonnées :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\fancyfoot[CE]{%
\begin{tikzpicture}[remember picture, overlay]
    \draw [fill=myblue]
        (current page.south east)
        -- (current page.south west)
        -- ++ (0, 4)
        -- cycle;
    \node [yshift=28pt,xshift=50pt]
        at (current page.south west)
        {\color{white}\rightmark};
    \node [yshift=28pt]
        at (current page.south)
        {\color{white}\thepage};
\end{tikzpicture}
}


\fancyfoot[CO]{%
\begin{tikzpicture}[remember picture, overlay]
    \draw [fill=myblue]
        (current page.south west)
        -- (current page.south east)
        -- ++ (0, 2)
        -- cycle;
    \node [yshift=28pt, xshift=-50pt]
        at (current page.south east)
        {\color{white}\thepage};
\end{tikzpicture}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Que signifient les lettres &amp;ldquo;CE&amp;rdquo; et &amp;ldquo;CO&amp;rdquo; ?&lt;/p&gt;
&lt;p&gt;Et bien c&amp;rsquo;est simple. &amp;ldquo;C&amp;rdquo; veut dire que je définis le centre de mon &amp;ldquo;footer&amp;rdquo;, encore une fois c&amp;rsquo;est arbitraire puisque nous avons réinitialisé les contenus avec &amp;ldquo;&lt;code&gt;\fancyhf&lt;/code&gt;&amp;rdquo; et que par conséquent les blocs de droite et gauche sont vides.&lt;/p&gt;
&lt;p&gt;Par contre &amp;ldquo;O&amp;rdquo; signifie que je travaille sur le pied de page des pages &amp;ldquo;paires&amp;rdquo; (Odd), et &amp;ldquo;E&amp;rdquo; sur les pieds de pages &amp;ldquo;impaires&amp;rdquo; (Even). J&amp;rsquo;inverse donc la forme et le placement de &amp;ldquo;&lt;code&gt;\thepage&lt;/code&gt;&amp;rdquo; qui donne le numéro de page actuel.&lt;/p&gt;
&lt;p&gt;Voilà ce que me donne ces blocs:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-11a92533-f975-446e-9daf-3111fbeaad83.png&#34; alt=&#34;Pages impaires&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-cf556666-3ae3-427e-aaf5-e53712e53d06.png&#34; alt=&#34;Pages paires&#34;&gt;&lt;/p&gt;
&lt;p&gt;Oui, j&amp;rsquo;ai fait exprès de ne pas avoir la même forme à droite et à gauche.&lt;/p&gt;
&lt;p&gt;Nous y voilà, les pages sont terminées, on peut passer à la page de titre.&lt;/p&gt;
&lt;h2 id=&#34;la-page-de-titre&#34;&gt;La page de titre&lt;/h2&gt;
&lt;p&gt;LaTeX est un langage de &amp;ldquo;balise&amp;rdquo;, et chaque balise est en fait une macro &amp;ldquo;TeX&amp;rdquo;. Par conséquent, la balise &amp;ldquo;&lt;code&gt;\maketitle&lt;/code&gt;&amp;rdquo;
qui génère la page de titre est une macro.&lt;/p&gt;
&lt;p&gt;Et en LaTeX, on peut redéfinir des macros. C&amp;rsquo;est ce que nous allons faire pour la page de titre.&lt;/p&gt;
&lt;p&gt;De manière générale, voilà comment vous allez vous y prendre :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-tex&#34;&gt;\renewcommand{\maketitle}{%
    \begin{titlepage}
    % ici le contenu de la page titre
    \end{titlepage}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et encore une fois, TikZ va nous servir.&lt;/p&gt;
&lt;p&gt;Mon image d&amp;rsquo;entête est assez grande, vous n&amp;rsquo;avez vu qu&amp;rsquo;une petite partie de celle-ci. Je vais m&amp;rsquo;en servir dans ma page,
placer le titre, l&amp;rsquo;auteur, etc. Rien de bien compliqué (mais non).&lt;/p&gt;
&lt;p class=&#34;image-block right&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-accb6d6c-b6f7-4b49-8a4e-c7484354a11c.png&#34; alt=&#34;La page titre&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;% pour récupérer le titre...
\usepackage{titling}

% je définis le titre
\title{Mon example de thème}
% l&#39;auteur (moi)
\author{%
    Patrice FERLET\\
    metal3d@gmail.com
}

% et la page de titre
\renewcommand{\maketitle}{%
    \begin{titlepage}
    \begin{tikzpicture}[remember picture, overlay]

    \node [yshift=-230, inner sep=0, outer sep=0]
        at (current page.north)
        {\includegraphics[width=\paperwidth]{head.png}};

    \node [shape=rectangle,
            fill=myblue,
            anchor=west,
            minimum width=1.4\paperwidth,
            xshift=-0.7\paperwidth,
            inner sep=2cm,
            rotate=10]
        at (current page.center)
        {\Huge\textcolor{white}{\textbf{\thetitle}}};

    \node [yshift=120, text width=\paperwidth, align=center]
        at (current page.south)
        {\Huge\color{myblue}\theauthor};
    \end{tikzpicture}
    \end{titlepage}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est du vite-fait pour l&amp;rsquo;article, je vous laisse une fois de plus imaginer ce que vous pouvez faire avec un peu de
travail.&lt;/p&gt;
&lt;h2 id=&#34;on-peaufine&#34;&gt;On peaufine&lt;/h2&gt;
&lt;p&gt;Reste une ou deux choses à adapter, personnellement, j&amp;rsquo;ajoute déjà des règles de séparation de paragraphe ainsi que
l&amp;rsquo;indentation de paragraphe :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\setlength{\parindent}{12pt}
\setlength{\parskip}{6pt plus 2pt minus 1pt}
\setlength{\emergencystretch}{3em}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et en ce qui concerne l&amp;rsquo;entête, j&amp;rsquo;ajoute le &amp;ldquo;titre&amp;rdquo; du chapitre seulement à partir des pages &amp;gt; 2. Sinon, on se retrouve
avec des guillemets qui ne contiennent rien à la page 2 (page de garde).&lt;/p&gt;
&lt;p&gt;Donc revenez sur le code &amp;ldquo;&lt;code&gt;\chead&lt;/code&gt;&amp;rdquo; et ajouter un test:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-latex&#34;&gt;\chead{%
\begin{tikzpicture}[remember picture, overlay]
    \node [yshift=-100, inner sep=0, outer sep=0] at (current page.north) {\includegraphics[width=\paperwidth]{head.png}};
    \node[yshift=-40,rotate=5, text width=0.5\paperwidth] at (current page.north) {%
    \ifnum\value{page}&amp;gt;2
        \Huge
        \textbf{``}
        \textcolor{myblue}{\leftmark}
        \textbf{&#39;&#39;}
    \fi
    };
\end{tikzpicture}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà, on a fini.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas du grand art, je ne suis pas un fin designer, mais ça vous donne une idée de la possibilité qu&amp;rsquo;offre LaTeX
et TikZ.&lt;/p&gt;
&lt;p&gt;Je suis conscient que LaTeX n&amp;rsquo;est pas &amp;ldquo;aisé&amp;rdquo; quand on entre dans ce genre de détail. Mais rappelez-vous que vous allez
certainement créer un thème (template) une fois et le garder pour le reste de vos travaux. Je peux aussi utiliser
différents thèmes pour le même article, livre, rapport&amp;hellip;&lt;/p&gt;
&lt;p&gt;Apprendre LaTeX est souvent considéré comme désuet face à markdown ou RST, mais si je prends l&amp;rsquo;exemple de PanDoc
(que j&amp;rsquo;adore) il passe bien par LaTeX pour générer le document. Et par conséquent, créer un template pour pandoc revient
à faire ce que j&amp;rsquo;ai fait dans cet article. Alors un bon conseil, penchez-vous dessus.&lt;/p&gt;
&lt;p&gt;Un dernier point, si vous maitrisez LaTeX, vous allez pouvoir générer des rapports en PDF en utilisant des templates à
vous. Comparer l&amp;rsquo;utilisation d&amp;rsquo;un fichier .doc, ou .odt et la génération de document en PDF avec LibreOffice (sans le
critiquer, je le trouve très bien), et LaTeX, vous vous rendrez compte que c&amp;rsquo;est peut-être bien plus évident de se
taper un thème et des macros que de se battre avec les thèmes et règles d&amp;rsquo;un outil &amp;ldquo;de traitement de texte&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Personnellement, j&amp;rsquo;utilise LaTeX pour pas mal de travaux et je vous invite à passer un peu de temps à vous former
dessus pour gagner beaucoup de temps à l&amp;rsquo;avenir.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Gogs - le vrai clone de github</title>
      <link>https://www.metal3d.org/blog/2016/gogs---le-vrai-clone-de-github/</link>
      <pubDate>Tue, 26 Apr 2016 11:26:02 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/gogs---le-vrai-clone-de-github/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Vous trouvez gitlab lourd et pas facile à installer, je vais vous parler de &lt;a href=&#34;https://gogs.io&#34;&gt;Gogs&lt;/a&gt; ! Le service Git qui est, pour le coup, vraiment un clone très proche de &lt;a href=&#34;https://github.com/gogits/gogs&#34;&gt;GitHub&lt;/a&gt; et très facile à mettre en place.&lt;/p&gt;
&lt;p&gt;Apparement je suis super à la bourre niveau news parce que le projet Gogs a plus de 2 ans. Mais c&amp;rsquo;est pas grave, j&amp;rsquo;ai envie de vous parler de mes tests.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est au détour d&amp;rsquo;une conversation que &lt;a href=&#34;https://twitter.com/fredix&#34;&gt;@fredix&lt;/a&gt; me parle de ce &lt;a href=&#34;https://gogs.io&#34;&gt;Gogs&lt;/a&gt;. Je trollais grassement Ruby et je disais que pourtant j&amp;rsquo;utilisais des outils développés en Ruby comme GitLab. Et, sachant que j&amp;rsquo;adore le langage Go, il me glisse un &amp;ldquo;tu devrais regarder Gogs alors&amp;rdquo;&amp;hellip; Et effectivement il a retweeté ceci, que j&amp;rsquo;ai zappé sans le faire exprès:&lt;/p&gt;
&lt;div style=&#34;justify-content: center; display: flex;&#34;&gt;
&lt;blockquote class=&#34;twitter-tweet&#34; data-lang=&#34;fr&#34;&gt;&lt;p lang=&#34;fr&#34; dir=&#34;ltr&#34;&gt;Gogs, un serveur Git avec Docker // &lt;a href=&#34;https://twitter.com/gogitservice&#34;&gt;@gogitservice&lt;/a&gt; &lt;a href=&#34;https://twitter.com/docker&#34;&gt;@docker&lt;/a&gt; &lt;a href=&#34;https://t.co/BOgKGHMXUY&#34;&gt;https://t.co/BOgKGHMXUY&lt;/a&gt; &lt;a href=&#34;https://t.co/tW9jqpIgyF&#34;&gt;pic.twitter.com/tW9jqpIgyF&lt;/a&gt;&lt;/p&gt;&amp;mdash; Valentin Ouvrard (@Valentin_NC) &lt;a href=&#34;https://twitter.com/Valentin_NC/status/722330865854861312&#34;&gt;19 avril 2016&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;p&gt;Arrivé le soir à la maison, je ne peux m&amp;rsquo;empêcher de le tester. Et ça m&amp;rsquo;arrive rarement ces derniers temps, mais j&amp;rsquo;ai bien assisté à un &amp;ldquo;effet waouh&amp;rdquo;.&lt;/p&gt;
&lt;h1 id=&#34;installation&#34;&gt;Installation&lt;/h1&gt;
&lt;p&gt;Deux solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le binaire, puisque le projet est développé en Go, il suffit de lancer l&amp;rsquo;exécutable. Pas de dépendances. C&amp;rsquo;est ce qu&amp;rsquo;il y a de bien avec Golang :)&lt;/li&gt;
&lt;li&gt;ou le &lt;a href=&#34;https://github.com/gogits/gogs/tree/master/docker&#34;&gt;conteneur Docker&lt;/a&gt; qui permet d&amp;rsquo;isoler tout ça et de faire &amp;ldquo;mumuse&amp;rdquo; avec des BDD différentes en un coup de cuillère à pot.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le binaire se suffit à lui-même et pèse 32Mo complété d&amp;rsquo;une dizaine de megas d&amp;rsquo;images, de templates et de css en tout genre. Le conteneur pèse lui environ 81Mo (normal, on inclut ici un FS).&lt;/p&gt;
&lt;p&gt;Bref, rien à voir avec les 700Mo de l&amp;rsquo;image Gitlab&amp;hellip; Cela peut passer comme un troll ou une critique mais ça n&amp;rsquo;en est pas une. Gitlab a énormément d&amp;rsquo;options et d&amp;rsquo;outils, et les dépendances nécessaires sont assez nombreuses. Donc rien de surprenant. Mais au-delà de cela, il y a &amp;ldquo;Ruby&amp;rdquo;. Pour rappel: Go permet de compiler en binaire natif le projet, alors que des sources Ruby ont besoin de tout le runtime. Cela alourdi considérablement une image qui en a besoin.&lt;/p&gt;
&lt;p&gt;On évite de troller et on passe à la suite.&lt;/p&gt;
&lt;p&gt;Pour démarrer le conteneur Gogs, c&amp;rsquo;est simple, je pars du principe que vous avez Docker 1.9 minimum pour créer un volume, sinon vous devrez monter les &amp;ldquo;data&amp;rdquo; dans un répertoire local.&lt;/p&gt;
&lt;p class=&#34;image-block right&#34;&gt;&lt;/p&gt;
![En deux lignes c&#39;est prêt](/static/upload/9c5ce5c7-3fae-4181-b332-3e06b04332a4-gogs.gif)
&lt;p&gt;Deux petites commandes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# création d&#39;un volume pour garder les
# fichiers de conf
docker volume create --name gogs-data

# on utilisera le port 10022 pour ssh
# et 10080 pour l&#39;interface web
docker run --name gogs -p 10022:22 -p 10080:3000 -v gogs-data:/data gogs/gogs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bon, la page de configuration est pas compliquée, il suffit de renseigner quelques broutilles comme le serveur de base de données (j&amp;rsquo;ai pris Sqlite pour tester) et j&amp;rsquo;ai tout laissé par défaut sauf une entrée:&lt;/p&gt;
&lt;p&gt;Application URL: http://localhost:10080&lt;/p&gt;
&lt;p&gt;Simplement parce que je le fais tourner sur Docker et que j&amp;rsquo;ai &amp;ldquo;bindé&amp;rdquo; le port ici. Sinon, la prochaine étape ne fonctionne pas.&lt;/p&gt;
&lt;p&gt;En cas d&amp;rsquo;erreur, no panic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;coupez le conteneur &amp;ldquo;&lt;code&gt;docker stop gogs&lt;/code&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;supprimez le avec &amp;ldquo;&lt;code&gt;docker rm gogs&lt;/code&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;supprimez le volume &amp;ldquo;&lt;code&gt;docker volume rm gogs-data&lt;/code&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;refaite le volume et lancez le projet comme expliqué ci-dessus&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Après cette étape terminée, vous voilà avec un serveur Gogs. Créez un utilisateur en passant pas &amp;ldquo;sing-up&amp;rdquo;, le premier compte est administrateur (à moins que vous ayez utilisé les options avancées de l&amp;rsquo;installeur juste avant).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://camo.githubusercontent.com/050c3e0938c634688f1eac460b2133440b725f7a/68747470733a2f2f676f67732e696f2f696d672f73637265656e73686f74732f322e706e67&#34; alt=&#34;Idem pour la présentation des sources&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;et-le-contenu&#34;&gt;Et le contenu&lt;/h1&gt;
&lt;p&gt;J&amp;rsquo;ai cru que ce projet n&amp;rsquo;allait pas me donner beaucoup d&amp;rsquo;options. C&amp;rsquo;est une erreur. Presque tout est là !&lt;/p&gt;
&lt;p&gt;Créer un dépôt, paramétrer les webhooks, ajouter des clefs SSH, etc. Tout est là.&lt;/p&gt;
&lt;p&gt;Les captures tirées du dépôt parlent d&amp;rsquo;elles même:&lt;/p&gt;
&lt;h1 id=&#34;impressions-personnelles&#34;&gt;Impressions (personnelles)&lt;/h1&gt;
&lt;p&gt;C&amp;rsquo;est fluide, propre, ça ressemble vraiment à github (à s&amp;rsquo;y méprendre) et je suis franchement étonné qu&amp;rsquo;un si &amp;ldquo;petit&amp;rdquo; projet (dans le sens &amp;ldquo;poids&amp;rdquo;) puisse faire tant de choses et si bien.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai joué presque 4h avec, j&amp;rsquo;ai créé des dépôts, ouvert plusieurs comptes, créé des pull-requests, foutu le boxon pour voir ce que ça pouvait engendrer. Rien à redire ça répond bien à tout ce dont j&amp;rsquo;ai besoin.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://camo.githubusercontent.com/f1c2f6b0fc8b962c3b2f3f20cb3e88e975d686af/68747470733a2f2f676f67732e696f2f696d672f73637265656e73686f74732f382e706e67&#34; alt=&#34;Le principe de release est aussi présent&#34;&gt;&lt;/p&gt;
&lt;p&gt;Je ne sais pas à quel point c&amp;rsquo;est scallable et je n&amp;rsquo;ai pas encore d&amp;rsquo;avis sur la question &amp;ldquo;sécurité&amp;rdquo;, mais j&amp;rsquo;ai largement moins galéré à utiliser SSL via un reverse proxy qu&amp;rsquo;avec Gitlab (y compris avec des images Docker super bien faites). Je reste franchement très impressionné.&lt;/p&gt;
&lt;p&gt;Seul hic, mais c&amp;rsquo;est certainement parce que le projet est encore jeune, je n&amp;rsquo;ai pas trouvé l&amp;rsquo;équivalent de &amp;ldquo;gitlab-ci&amp;rdquo; (les fameux runners) qui me permettrait de faire des &amp;ldquo;builds&amp;rdquo; automatiques. Mais je compte bien sur la communauté pour bosser sur le sujet. Dans tous les cas, un Jenkins à côté (ou un truc plus léger&amp;hellip;) fera bien l&amp;rsquo;affaire.&lt;/p&gt;
&lt;h1 id=&#34;et-coté-addons&#34;&gt;Et coté &amp;ldquo;addons&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;En premier lieu, l&amp;rsquo;interface est traduite dans un certain nombre de langages y compris le français.&lt;/p&gt;
&lt;p&gt;Il y a ce qu&amp;rsquo;il faut pour le minimum:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;webhooks,&lt;/li&gt;
&lt;li&gt;service mail,&lt;/li&gt;
&lt;li&gt;issues,&lt;/li&gt;
&lt;li&gt;connexion LDAP,&lt;/li&gt;
&lt;li&gt;api&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;De ce que j&amp;rsquo;en ai vu, il est possible de connecter &lt;a href=&#34;https://drone.io/&#34;&gt;Drone.io&lt;/a&gt; mais ayant investigué dans le forum, il apparait qu&amp;rsquo;à ce jour il n&amp;rsquo;y a pas moyen de valider une pull-request via un service externe type Jenkins.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://camo.githubusercontent.com/4846236d332b35a347edc4ff935ca50b2be418e2/68747470733a2f2f676f67732e696f2f696d672f73637265656e73686f74732f342e706e67&#34; alt=&#34;La page de profile ressemble vraiment à celle de Github&#34;&gt;&lt;/p&gt;
&lt;p&gt;Pour le moment j&amp;rsquo;en suis à la phase de test et je découvre un peu l&amp;rsquo;outil. Donc tout ce que je dis ici ne doit pas être pris comme paroles d&amp;rsquo;évangile. Il se peut que cela soit possible mais je ne trouve pas l&amp;rsquo;info.&lt;/p&gt;
&lt;p&gt;Autre &amp;ldquo;addon&amp;rdquo; si on peut appeler cela comme ça, l&amp;rsquo;API est peu documentée. Elle est pourtant bien là et permet de faire quelques opérations. Je n&amp;rsquo;en dis pas plus, je n&amp;rsquo;ai pas franchement creusé le sujet.&lt;/p&gt;
&lt;h1 id=&#34;mais-qui-sen-servira-&#34;&gt;Mais qui s&amp;rsquo;en servira ?&lt;/h1&gt;
&lt;p&gt;Alors, on change ? On passe de GitLab à Gogs ? Attendez un peu quand même&amp;hellip;&lt;/p&gt;
&lt;p&gt;Je ne vais pas vous dire de l&amp;rsquo;utiliser comme dépôt ouvert au public. Ce serait vite m&amp;rsquo;avancer. Mais pour un &amp;ldquo;github like&amp;rdquo; d&amp;rsquo;entreprise c&amp;rsquo;est souvent bien suffisant. Un indépendant qui gère pas mal de dépôts et qui veut laisser l&amp;rsquo;accès proprement à ses clients et collaborateurs trouvera aussi son bonheur avec Gogs. Il est moins compliqué à gérer, très simple à installer et ne dépayse pas les utilisateurs qui connaissent déjà GitHub.&lt;/p&gt;
&lt;p&gt;Par contre, il est encore jeune, n&amp;rsquo;offre pas tout d&amp;rsquo;un gitlab et n&amp;rsquo;est pas, pour l&amp;rsquo;heure, assez éprouvé. À ce jour, il faut savoir dans quoi on met les pieds en l&amp;rsquo;utilisant. Mais je suis persuadé qu&amp;rsquo;il va vraiment faire des prouesses rapidement.&lt;/p&gt;
&lt;p&gt;Je suis même en train de me demander si je ne devrai pas tenter de créer l&amp;rsquo;équivalent de &amp;ldquo;gitlab runner&amp;rdquo; sur Gogs&amp;hellip; D&amp;rsquo;après les auteurs le fait de &amp;ldquo;bloquer&amp;rdquo; une PR et de la rendre &amp;ldquo;mergeable&amp;rdquo; par la suite est un peu complexe à l&amp;rsquo;heure actuelle.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://camo.githubusercontent.com/f35175847954a398ae1b3b73b2099862cf8eda02/68747470733a2f2f676f67732e696f2f696d672f73637265656e73686f74732f372e706e67&#34; alt=&#34;Les pull-request sont facile à voir&#34;&gt;&lt;/p&gt;
&lt;p&gt;Une chose est sûre, les auteurs de Gogs sont ouverts à la discussion, le forum est actif et le projet est en vie depuis déjà assez longtemps pour imaginer avoir un support (communautaire) conséquent.&lt;/p&gt;
&lt;h1 id=&#34;ressources&#34;&gt;Ressources:&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gogs.io/&#34;&gt;https://gogs.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/gogits/gogs&#34;&gt;https://github.com/gogits/gogs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://try.gogs.io/&#34;&gt;https://try.gogs.io/&lt;/a&gt; (démo dont les projets sont supprimés régulièrement)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://discuss.gogs.io/&#34;&gt;https://discuss.gogs.io/&lt;/a&gt; (le forum)&lt;/li&gt;
&lt;/ul&gt;

      </description>
    </item>
    
    <item>
      <title>Validation de pull request Github avec &#34;lgtm&#34;</title>
      <link>https://www.metal3d.org/blog/2016/validation-de-pull-request-github-avec-lgtm/</link>
      <pubDate>Fri, 22 Apr 2016 11:35:03 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/validation-de-pull-request-github-avec-lgtm/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Les &amp;ldquo;code reviews&amp;rdquo; sont souvent sous-estimées alors qu&amp;rsquo;elles sont une des phases les plus importantes pour la pérennité d&amp;rsquo;un projet et son bon fonctionnement. &lt;a href=&#34;http://lgtm.co&#34;&gt;LGTM.co&lt;/a&gt; propose une solution pour renforcer un peu cette phase.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-27745777-feb4-4ba7-9ca3-c4cfc0fa5bd9.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Avant toute chose, ce que je vais vous montrer ne remplace pas &lt;a href=&#34;https://www.gerritcodereview.com/&#34;&gt;Gerrit&lt;/a&gt;. Mais il arrive que, par manque de temps, manque de moyens ou d&amp;rsquo;infrastructure, vous ne puissiez pas en profiter. C&amp;rsquo;est donc un outil super intéressant que j&amp;rsquo;ai testé pour vous: LGTM qui est un commentaire fréquemment utilisé pour dire &amp;ldquo;Looks Good To Me&amp;rdquo; (traduction: &amp;ldquo;ça semble correct&amp;rdquo;). Ce commentaire va être utilisé par un outil externe &lt;a href=&#34;http://lgtm.co&#34;&gt;lgtm.co&lt;/a&gt; qui se connecte en tant qu&amp;rsquo;application tierce sur GitHub.&lt;/p&gt;
&lt;h1 id=&#34;comment-ça-marche-&#34;&gt;Comment ça marche ?&lt;/h1&gt;
&lt;p&gt;Il suffit de se connecter avec son compte github sur lgtm.co et d&amp;rsquo;activer la gestion de code-review:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-81f7a5de-a6f6-4df1-9ae7-6213ec50c3c9.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Il faut ensuite &lt;strong&gt;impérativement&lt;/strong&gt; ajouter un fichier &amp;ldquo;MAINTAINERS&amp;rdquo; qui doit lister les mainteneurs dont les commentaires seront comptabilisés. Voir: &lt;a href=&#34;https://lgtm.co/docs/maintainers/&#34;&gt;https://lgtm.co/docs/maintainers/&lt;/a&gt; Il en faut un par ligne.&lt;/p&gt;
&lt;p&gt;Là par contre j&amp;rsquo;ai eu un léger bug et la forme suivante ne passait pas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Patrice FERLET &amp;lt;metal3d@...&amp;gt; (@metal3d)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc pour le moment j&amp;rsquo;utilise:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;metal3d &amp;lt;metal3d@...&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si besoin, vous pouvez aussi ajouter un fichier &amp;ldquo;.lgtm&amp;rdquo; pour modifier le nombre de review minimale (2 par défaut), la forme du commentaire (LGTM par défaut), etc. Voir &lt;a href=&#34;https://lgtm.co/docs/customize/&#34;&gt;https://lgtm.co/docs/customize/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pour le test, j&amp;rsquo;ai demandé à un collègue de se prêter au jeu et j&amp;rsquo;ai limité le nombre de review à 1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;approvals = 1
pattern = &amp;quot;(?i)LGTM&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notez que j&amp;rsquo;ai bien aussi le &amp;ldquo;pattern&amp;rdquo; &lt;code&gt;pattern = &amp;quot;(?i):\\+1:&amp;quot;&lt;/code&gt; qui fait apparaitre un pouce jaune :)&lt;/p&gt;
&lt;p&gt;Bref, à vous de décider.&lt;/p&gt;
&lt;h1 id=&#34;phase-de-review&#34;&gt;Phase de review&lt;/h1&gt;
&lt;p&gt;La phase est assez simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;quand une pull-request est créé, LGTM va &amp;ldquo;bloquer&amp;rdquo; la possibilité de la merger&lt;/li&gt;
&lt;li&gt;quand un nombre de review positive est atteint, le bouton &amp;ldquo;merge&amp;rdquo; est déverrouillé&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L&amp;rsquo;idée est super simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vous paramétrez lgtm pour définir le nombre de reviews positives minimales à trouver&lt;/li&gt;
&lt;li&gt;qui sont les &amp;ldquo;mainteneurs&amp;rdquo; dont les commentaires seront comptabilisé&lt;/li&gt;
&lt;li&gt;vous laissez faire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-3ef73324-bf0b-46a9-a084-e4760be7c5a6.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;et-pour-les-dépôts-privés-ou-entreprise-&#34;&gt;Et pour les dépôts privés ou entreprise ?&lt;/h1&gt;
&lt;p&gt;Là c&amp;rsquo;est un peu plus compliqué, mais ce n&amp;rsquo;est pas non plus l&amp;rsquo;enfer. La licence du projet est assez claire:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Licensing&lt;/p&gt;
&lt;p&gt;This product is free for small teams and non-profits, however, we will require a modest licensing fee for on-premise enterprise use in the near future. Pricing to be published soon.&lt;/p&gt;
&lt;p&gt;&amp;ndash; &lt;a href=&#34;https://lgtm.co/docs/install/&#34;&gt;https://lgtm.co/docs/install/&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et je ne connais pas vraiment le tarif&amp;hellip; Mais bon, passons au test.&lt;/p&gt;
&lt;p&gt;En gros, il suffit de faire tourner le conteneur Docker:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker pull lgtm/lgtm
$ docker run \
	--volume /var/lib/lgtm:/var/lib/lgtm \
	--env-file /etc/lgtm/lgtmrc \
	--restart=always \
	--publish=80:8000 \
	--detach=true \
	--name=lgtm \
	lgtm/lgtm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il faut bien entendu mettre les clefs d&amp;rsquo;accès GITHUB dans le fichier &amp;ldquo;&lt;code&gt;/etc/lgtm/lgtmrc&lt;/code&gt;&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;GITHUB_URL=https://github.mycompany.com
GITHUB_SCOPE=user:email,read:org,repo
GITHUB_CLIENT=
GITHUB_SECRET=
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;bilan&#34;&gt;Bilan&lt;/h1&gt;
&lt;p&gt;On est en train de proposer ça à nos équipes, le but étant d&amp;rsquo;éviter l&amp;rsquo;auto-validation de masse et d&amp;rsquo;ajouter un côté ludique à la phase de &amp;ldquo;review&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Je le répète, ça ne remplace pas Gerrit, mais ça peut franchement ajouter une valeur de plus à votre gestion de projet.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>docker - arrêtez d&#39;utiliser des conteneurs data-only</title>
      <link>https://www.metal3d.org/blog/2016/docker---arr%C3%AAtez-dutiliser-des-conteneurs-data-only/</link>
      <pubDate>Mon, 18 Apr 2016 11:51:18 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/docker---arr%C3%AAtez-dutiliser-des-conteneurs-data-only/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Le voilà le titre racoleur. Et en le lisant vous êtes en train de vous dire que je vais aller à l&amp;rsquo;encontre des bonnes pratiques. Vous vous trompez !&lt;/p&gt;
&lt;p&gt;Non mais sérieusement, je sais qu&amp;rsquo;on vous a répété maintes fois &amp;ldquo;utilisez un conteneur data-only&amp;rdquo;, et je comprends bien l&amp;rsquo;intérêt. Cela-dit, je vais faire durer un peu le suspens et commencer par réexpliquer ce qu&amp;rsquo;est un &amp;ldquo;volume&amp;rdquo; et ce que fais un conteneur &amp;ldquo;data-only&amp;rdquo;. Je vous donnerai enfin la meilleure option pour vous en passer, et pour éviter de vous torturer la tête: non je ne vais pas non plus faire un montage de volume local tout bête. Mais je ne vais pas non plus rendre plus complexe l&amp;rsquo;opération.&lt;/p&gt;
&lt;p&gt;En clair, on va se passer de conteneur data-only, que ce soit avec docker ou docker-compose tout en gardant la souplesse de l&amp;rsquo;opération.&lt;/p&gt;
&lt;p&gt;EDIT: Merci à Adirelle, dans les commentaires, d&amp;rsquo;avoir précisé que les volumes nommés sont une &amp;ldquo;nouveauté&amp;rdquo; de Docker 1.9, ce qui explique l&amp;rsquo;utilisation massive de conteneurs data-only avant cette version&lt;/p&gt;
&lt;p&gt;EDIT: Merci aussi à &lt;a href=&#34;https://twitter.com/cedvanet&#34;&gt;cedvanet&lt;/a&gt; pour sa question: Effectivement je n&amp;rsquo;ai pas été clair sur le fait que les conteneurs &amp;ldquo;host:conteneurs&amp;rdquo; ne sont pas le sujet de cet article, ils sont bel et bien très utiles, par exemple pour développer en partageant les sources entre votre IDE et votre conteneur, ou pour placer de la configuration. De plus, effectivement, &lt;strong&gt;il arrive que des &amp;ldquo;data-only containers&amp;rdquo; soient utiles&lt;/strong&gt;, le titre du billet est peut-être trop brutal. Ce que je tente de vous démontrer n&amp;rsquo;est pas qu&amp;rsquo;un data-only container est &amp;ldquo;inutile&amp;rdquo; ou &amp;ldquo;à ne jamais utiliser&amp;rdquo;, mais qu&amp;rsquo;il faut les utiliser que certains cas.&lt;/p&gt;
&lt;h1 id=&#34;cest-quoi-un-volume-&#34;&gt;C&amp;rsquo;est quoi un volume ?&lt;/h1&gt;
&lt;p&gt;Docker propose des volumes depuis le début. Le principe est parfois mal compris et très souvent mal utilisé. Il existe deux types de volumes très utilisés:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les volumes &amp;ldquo;host:conteneur&amp;rdquo;&lt;/li&gt;
&lt;li&gt;les volumes &amp;ldquo;conteneur:conteneur&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ces noms sont donnés à titre indicatif et n&amp;rsquo;existent pas dans la documentation de docker, c&amp;rsquo;est juste pour vous donner une définition simple.&lt;/p&gt;
&lt;p&gt;Le premier, &amp;ldquo;host-conteneur&amp;rdquo; est un bête montage de répertoire local que vous choisissez vers le conteneur. Pour faire simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker run -it --rm -v ~/Documents:/opt/docs alpine sh
&amp;gt; ls -la /opt/docs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous voyez dans le répertoire &amp;ldquo;/opt/docs&amp;rdquo; du conteneur les fichiers et répertoires &amp;ldquo;~/Documents&amp;rdquo;. C&amp;rsquo;est simple et c&amp;rsquo;est pratique. Par exemple quand on travaille en PHP, Python ou je ne sais quel autre langage, il suffit de monter les sources du projet dans le conteneur qui aura loisir de démarrer votre projet.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;autre type de volume est plus sournois. Une image va définir un volume qui pourra alors être monté dans d&amp;rsquo;autres conteneurs. Outre le fait que la majeure partie des gens n&amp;rsquo;ont pas conscience de ce qui découle de déclarer un volume dans une image (et, brisons le suspens, c&amp;rsquo;est la finalité de l&amp;rsquo;article), c&amp;rsquo;est certainement l&amp;rsquo;une des notions les plus intéressantes de Docker.&lt;/p&gt;
&lt;p&gt;Prenons une image simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM alpine
# on déclare un volume
VOLUME /opt/data
# pas la peine le de le laisser tourner
# les volumes sont accessibles même
# quand le conteneur ne tourne plus
CMD [&amp;quot;true&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant démarrons un conteneur avec cette image:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker build -t $USER/dataexample .
$ docker run -it -d --name data $USER/dataexample
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et lions les volumes de ce conteneur dans un nouveau conteneur:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker run --rm -it --volumes-from data alpine sh
&amp;gt; touch /opt/data/foo
&amp;gt; exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, créons un nouveau conteneur qui monte, une fois de plus, les volumes du conteneur &amp;ldquo;data&amp;rdquo;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker run --rm -it --volumes-from data alpine sh
&amp;gt; ls /opt/data
&amp;gt; foo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous l&amp;rsquo;avez compris. Le fait de déclarer un volume dans une image permet de partager un répertoire depuis le conteneur vers d&amp;rsquo;autres conteneurs.&lt;/p&gt;
&lt;p&gt;Maintenant, pour bien poser l&amp;rsquo;idée, &lt;strong&gt;le conteneur &amp;ldquo;data&amp;rdquo; est un conteneur &amp;ldquo;data-only&amp;rdquo;&lt;/strong&gt;. Il ne fait rien d&amp;rsquo;autre que de garder les données d&amp;rsquo;un répertoire qu&amp;rsquo;il a déclaré comme étant un volume.&lt;/p&gt;
&lt;p&gt;Sans passer par la case &amp;ldquo;image&amp;rdquo;, on peut tout à faire créer un conteneur &amp;ldquo;data-only&amp;rdquo; directement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker run -it --volume /opt/data --name data2 busybox
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le fait de ne pas donner de point de montage local va créer un volume.&lt;/p&gt;
&lt;h1 id=&#34;ok-mais-il-stocke-où-alors-&#34;&gt;Ok, mais il stocke où alors ?&lt;/h1&gt;
&lt;p&gt;Et justement&amp;hellip; Voilà ce qui me chagrine avec l&amp;rsquo;utilisation de conteneurs &amp;ldquo;data-only&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Le volume est bel et bien stocké, par défaut, sur votre système. Sauf que peu d&amp;rsquo;utilisateurs en ont conscience.&lt;/p&gt;
&lt;p&gt;Si je ne déclare pas de volume, les fichiers sont stockés dans le conteneur lui-même (en fait, dans le répertoire /var/lib/docker/SHA_IMAGE&amp;hellip;). Mais quand on déclare un volume, docker va créer un répertoire prévu à cet effet. Que l&amp;rsquo;on soit bien clair, &lt;strong&gt;Docker déclare un volume&lt;/strong&gt;, et vous pouvez le voir par vous-même:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker -d run -it --name data -v /opt/data busybox
$ docker inspect  --format &amp;quot;{{ .Mounts }}&amp;quot; data
[{ab099654cc0be... /var/lib/docker/volumes/ab099654cc0be.../_data /opt/data local  true }]

$ docker volume inspect ab099654cc0be...
[
    {
        &amp;quot;Name&amp;quot;: &amp;quot;ab099654cc0be...&amp;quot;,
        &amp;quot;Driver&amp;quot;: &amp;quot;local&amp;quot;,
        &amp;quot;Mountpoint&amp;quot;: &amp;quot;/var/lib/docker/volumes/ab099654cc0be.../_data&amp;quot;
    }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà où je veux en venir. Supprimez le conteneur &amp;ldquo;data&amp;rdquo;, et relancez la commande d&amp;rsquo;inspection de volume:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker stop data
docker rm data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous venez de supprimer le conteneur, mais son volume est toujours là&amp;hellip;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker volume inspect ab099654cc0bef6c8b0f18e3fb8efe2b564b39832cd1db6b277f4c92bf84272c
[
    {
        &amp;quot;Name&amp;quot;: &amp;quot;ab099654cc0be...&amp;quot;,
        &amp;quot;Driver&amp;quot;: &amp;quot;local&amp;quot;,
        &amp;quot;Mountpoint&amp;quot;: &amp;quot;/var/lib/docker/volumes/ab099654cc0be.../_data&amp;quot;
    }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On vient de créer un orphelin.&lt;/p&gt;
&lt;p&gt;Autre souci, je ne peux plus monter ce volume:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker run --rm -it --volumes-from data busybox
docker: Error response from daemon: No such container: data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc, voilà la première raison qui me pousse à ne pas utiliser des conteneurs &amp;ldquo;data-only&amp;rdquo;, mais plutôt d&amp;rsquo;utiliser un volume.&lt;/p&gt;
&lt;h1 id=&#34;ok-utilisons-un-volume&#34;&gt;Ok, utilisons un volume&lt;/h1&gt;
&lt;p&gt;Maintenant que vous avez compris le principe, sachez que docker peut vous permettre de créer un volume &lt;strong&gt;sans passer par un conteneur&lt;/strong&gt;. C&amp;rsquo;est là que je veux en venir !&lt;/p&gt;
&lt;p&gt;Prenons un exemple que j&amp;rsquo;utilise beaucoup, un conteneur mongo dont je ne veux pas perdre les données. J&amp;rsquo;utilisais, avant, un répertoire perso pour y stocker les données.&lt;/p&gt;
&lt;p&gt;Plus tard, j&amp;rsquo;ai utilisé un conteneur data-only, ce qui fonctionne bien, je ne dis pas le contraire. L&amp;rsquo;image mongo déclare un volume &amp;ldquo;/data/db&amp;rdquo;, donc je n&amp;rsquo;avais qu&amp;rsquo;à faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker run --name mongodata -v /data/db -it -d busybox
docker run --rm -it --volumes-from mongodata mongo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oui, mais c&amp;rsquo;est bête !&lt;/p&gt;
&lt;p&gt;Voilà la meilleure façon de faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker volume create --name mongodata
docker run --rm -it -v mongodata:/data/db mongo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette fois, je définis où je monte le volume, et je n&amp;rsquo;ai qu&amp;rsquo;un seul conteneur qui tourne: le conteneur mongo. Le volume est local (par défaut) et se trouve ici:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker volume inspect mongodata
[
    {
        &amp;quot;Name&amp;quot;: &amp;quot;mongodata&amp;quot;,
        &amp;quot;Driver&amp;quot;: &amp;quot;local&amp;quot;,
        &amp;quot;Mountpoint&amp;quot;: &amp;quot;/var/lib/docker/volumes/mongodata/_data&amp;quot;
    }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est bien plus efficace et ça limite largement le nombre de conteneurs à démarrer. On notera aussi la facilité à retrouver les volumes par leurs noms.&lt;/p&gt;
&lt;h1 id=&#34;et-avec-docker-compose-&#34;&gt;et avec docker-compose ?&lt;/h1&gt;
&lt;p&gt;Docker-compose est vraiment un outil pratique et j&amp;rsquo;attendais avec impatience l&amp;rsquo;arrivée d&amp;rsquo;une version &amp;ldquo;2&amp;rdquo; qui me permettais de jouer avec un peu plus d&amp;rsquo;options. Et justement, depuis cette version, nous pouvons créer des volumes.&lt;/p&gt;
&lt;p&gt;Exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;version: &#39;2&#39;
services:
	database:
		image: mongo
		volumes:
		- mongodata:/data/db
volumes:
	mongodata:
		driver: local
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà, un conteneur, un volume, pas la peine de créer un conteneur simplement pour garder des données au chaud.&lt;/p&gt;
&lt;h1 id=&#34;les-drivers&#34;&gt;Les drivers&lt;/h1&gt;
&lt;p&gt;Par défaut nous utilisons des volumes locaux. Mais il existe d&amp;rsquo;autres drivers qui permettent de monter le volume autrement que sur votre disque.&lt;/p&gt;
&lt;p&gt;La liste est déjà conséquente et se trouve ici: &lt;a href=&#34;https://docs.docker.com/engine/extend/plugins/&#34;&gt;https://docs.docker.com/engine/extend/plugins/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai utilisé, pour le fun &lt;a href=&#34;https://github.com/gondor/docker-volume-netshare&#34;&gt;https://github.com/gondor/docker-volume-netshare&lt;/a&gt; qui permet d&amp;rsquo;utiliser NFS (entre autre). Ça marche bien !&lt;/p&gt;
&lt;h1 id=&#34;suites-aux-commentaires&#34;&gt;Suites aux commentaires&lt;/h1&gt;
&lt;p&gt;J&amp;rsquo;ai reçu pas mal de commentaires sur Twitter, Google plus et ici qui sont trés intéressants. Je vais donc repréciser un point:&lt;/p&gt;
&lt;p&gt;Effectivement, il peut arriver que, à l&amp;rsquo;inverse, un &amp;ldquo;volume nommé&amp;rdquo; tel que je le présente ne soit pas la panacée. Un &amp;ldquo;data-only container&amp;rdquo; peut avoir tout son intérêt, par exemple pour partager un volume avec beaucoup de conteneurs avec en plus un accès &amp;ldquo;host&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Clairement, je ne suis pas là pour juger les utilisateurs de conteneurs data-only, ni pour définir une règle. Ce que je tenais à démontrer ici, dans ce billet de blog, c&amp;rsquo;est simplement que pour pas mal de cas, la plupart même, un conteneur &amp;ldquo;data-only&amp;rdquo; n&amp;rsquo;est pas une évidence.&lt;/p&gt;
&lt;p&gt;En clair:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vous développez et devez avoir accès au répertoire de travail localement: &lt;em&gt;faites un volume host:container&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;vous voulez partager un volume avec beaucoup de conteneurs &lt;strong&gt;et&lt;/strong&gt; avoir accès localement aux données: &lt;em&gt;faites un conteneur &amp;ldquo;data-only&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;vous voulez persister des données mais y avoir accès localement n&amp;rsquo;est que ponctuel ou anecdotique: &lt;em&gt;créez un volume nommé&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;donc&#34;&gt;Donc&lt;/h1&gt;
&lt;p&gt;Donc voilà, le titre, je le répète, &amp;ldquo;arrêtez d&amp;rsquo;utiliser des conteneurs data-only&amp;rdquo;, c&amp;rsquo;est en fait rarement utile et surtout c&amp;rsquo;est moins efficace. Docker gère très bien les volumes en tant qu&amp;rsquo;entité.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Docker, Xvfb et links</title>
      <link>https://www.metal3d.org/blog/2016/docker-xvfb-et-links/</link>
      <pubDate>Sun, 17 Apr 2016 10:38:02 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/docker-xvfb-et-links/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Un conteneur = un service, c&amp;rsquo;est une règle qui peut parfois vous donner du fil à retordre. Et justement, un cas nous est tombé dessus au travail: un service qui doit utiliser &lt;a href=&#34;http://www.x.org/archive/X11R7.6/doc/man/man1/Xvfb.1.xhtml&#34;&gt;Xvfb&lt;/a&gt;. Ça fait bien deux services, et il faut les séparer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-822d95d3-2445-4164-b78e-36a656a96381.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Docker, l&amp;rsquo;outil de la décennie, le projet qui change la face du monde de l&amp;rsquo;informatique, et son lot de mauvaises idées par exemple, &lt;em&gt;utiliser un conteneur qui lance plusieurs services&lt;/em&gt; à coup de supervisord.&lt;/p&gt;
&lt;p&gt;Je comprends que parfois ça puisse permette de faire &amp;ldquo;d&amp;rsquo;une pierre 1568 coups&amp;rdquo;, mais ce genre de pratique va à contre-courant du principe de &amp;ldquo;micro-service&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avant de lire la suite !&lt;/strong&gt; je sais que certains vont me dire &amp;ldquo;bha c&amp;rsquo;est évident !!!&amp;rdquo;, ou alors &amp;ldquo;super&amp;hellip; Tu découvres qu&amp;rsquo;on peut lier des conteneurs&amp;hellip;&amp;rdquo;. &lt;strong&gt;Stop&lt;/strong&gt;, j&amp;rsquo;estime connaitre assez bien docker, et je milite depuis très longtemps sur le fait de séparer les services. Mais je ne suis pas infaillible (et vous non plus). Il arrive que l&amp;rsquo;on passe à côté d&amp;rsquo;un chose évidente et ce fut le cas ici. Je ne vais pas prétendre vous révéler un truc incroyable, mais plutôt vous montrer que parfois on peut oublier la règle de l&amp;rsquo;art et passer à côté d&amp;rsquo;une notion simple qui vous sauve. Utiliser Xvfb dans un conteneur séparé n&amp;rsquo;était pas &amp;ldquo;évident&amp;rdquo; dans mon esprit, cela ne veut pas dire que je ne savais pas le faire :) bon là je me suis bien brossé, on va pouvoir passer à l&amp;rsquo;article !&lt;/p&gt;
&lt;h1 id=&#34;pourquoi-séparer-les-services-&#34;&gt;Pourquoi séparer les services ?&lt;/h1&gt;
&lt;p&gt;Si vous ne séparez pas les services en plusieurs images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;d&amp;rsquo;abord la taille de l&amp;rsquo;image va être conséquente car vous allez y injectez tous les services&lt;/li&gt;
&lt;li&gt;vous forcez le service principal à utiliser une version gelée des services dépendants&lt;/li&gt;
&lt;li&gt;vous risquez d&amp;rsquo;avoir des soucis si un des services plante sans couper les autres (on va y venir)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je vais reprendre les deux premiers point. Prenons un exemple &amp;ldquo;bête et méchant&amp;rdquo; d&amp;rsquo;un service web utilisant:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nginx&lt;/li&gt;
&lt;li&gt;mysql&lt;/li&gt;
&lt;li&gt;php&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si vous créez une image qui embarque tout ça, alors vous allez avoir besoin des 3 logiciels, de lancer le service nginx et mysql en parallèle (via supervisord) et vous ne laissez pas le choix des versions. Si par exemple je décide d&amp;rsquo;utiliser MariaDB à la place de MySQL, alors je vais devoir reconstruire l&amp;rsquo;image moi-même et par conséquent perdre un temps précieux. Alors que Docker permet de lier des conteneurs et donc de ne pas m&amp;rsquo;imposer telle ou telle version de MySQL.&lt;/p&gt;
&lt;p&gt;Bref, vous l&amp;rsquo;aurez compris, la bonne pratique est de créer 3 images et &lt;strong&gt;de lier les conteneurs&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id=&#34;mais-comment-ça-se-passe-avec-xvfb-&#34;&gt;Mais comment ça se passe avec Xvfb ?&lt;/h1&gt;
&lt;p&gt;Si vous ne connaissez pas Xvfb, je vous fais un résumé rapide. C&amp;rsquo;est un serveur d&amp;rsquo;affichage, sans affichage. Voilà&amp;hellip; En gros, c&amp;rsquo;est un serveur Xorg qui n&amp;rsquo;a pas besoin d&amp;rsquo;écran. On peut alors utiliser la variable d&amp;rsquo;environnement &amp;ldquo;DISPLAY&amp;rdquo; pour lancer un programme graphique. Inutile ? Si vous avez envie de lancer des tests Selenium par exemple, dans un conteneur, et bien sans ce bijou ce sera une douleur.&lt;/p&gt;
&lt;p&gt;Bref. Ici au boulot, nous utilisons un service que j&amp;rsquo;ai développé, nommé &lt;a href=&#34;https://hub.docker.com/r/metal3d/belphegor/&#34;&gt;Belphegor&lt;/a&gt;, qui permet de prendre une capture de site et nous renvoyer l&amp;rsquo;image en png, jpeg, etc. Nous utilisons Docker, et j&amp;rsquo;ai donc simplement intégré à l&amp;rsquo;image un service Xvfb pour lancer le service sur l&amp;rsquo;affichage virtuel. Erreur de ma part, j&amp;rsquo;utilise Xvfb-run, un script fourni avec le service, qui a la fâcheuse tendance à ne pas éteindre mon service Belphegor quand Xvfb plante.&lt;/p&gt;
&lt;p&gt;Cyril Compigne, collègue sysadmin, me dit alors &amp;ldquo;bha dois bien y avoir un moyen de séparer les deux services en deux conteneurs, comme ça quand Xvfb plante, on le relance sans couper Belphegor&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mais bon sang!!! Pourquoi j&amp;rsquo;y ai pas pensé ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Il a raison, et voilà la méthode dite &amp;ldquo;de bourrin&amp;rdquo; que vous pourrez améliorer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conteneur 1:&lt;/strong&gt; Xvfb. On crée une image depuis une distribution qui offre Xvfb. &lt;a href=&#34;https://www.metal3d.org/ticket/alpine-ou-comment-faire-une-image-docker-tres-legere&#34;&gt;Alpine&lt;/a&gt; (la distribution que j&amp;rsquo;adore pour les conteneurs docker) propose un paquet &amp;ldquo;xvfb&amp;rdquo; qui fonctionne très bien.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM alpine
MAINTAINER Patrice FERLET &amp;lt;metal3d gmail.com&amp;gt;

RUN set -xe; \
    apk add --update xvfb;\
    rm -rf /var/cache/apk/*;
    
CMD [&amp;quot;Xvfb&amp;quot;, &amp;quot;-ac&amp;quot;, &amp;quot;-listen&amp;quot;, &amp;quot;tcp&amp;quot;, &amp;quot;:99&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Juste une petite explication. L&amp;rsquo;option &amp;ldquo;-ac&amp;rdquo; de la commande veut dire &amp;ldquo;accepte tout le monde&amp;rdquo;. Vous pourrez l&amp;rsquo;améliorer plus tard. Vous pourrez, plus tard, adapter cela en sécurisant le service pour n&amp;rsquo;accepter que les connexions de conteneur choisi. Je n&amp;rsquo;ai pas poussé plus que ça pour trouver la solution, on n&amp;rsquo;avait pas trop le temps&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conteneur 2:&lt;/strong&gt; Belphegor à qui j&amp;rsquo;ai viré l&amp;rsquo;installation et le lancement de Xvfb. On utilisera la variable &amp;ldquo;&lt;code&gt;DISPLAY&lt;/code&gt;&amp;rdquo; pour définir où se trouve le serveur d&amp;rsquo;affichage.&lt;/p&gt;
&lt;p&gt;Et voilà !&lt;/p&gt;
&lt;p&gt;On build et on lance le serveur:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker build -t $USER/xvfb .
docker run -it --rm --name xvfbserveur $USER/xvfb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, prenons l&amp;rsquo;image &amp;ldquo;metal3d/belphegor:noxvfb&amp;rdquo;, ou tout autre conteneur ayant besoin de X:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker run --rm -it -p 8000:8000 -e DISPLAY=xvfbserver:99 metal3d/belphegor:noxvfb 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le nom &amp;ldquo;xvfbserver&amp;rdquo; est celui de mon conteneur Xvfb, docker va résoudre ce nom. Mais c&amp;rsquo;est pas tout&amp;hellip;&lt;/p&gt;
&lt;p&gt;Si jamais je coupe le conteneur xvfbserver alors Belphegor n&amp;rsquo;arrive plus à s&amp;rsquo;y connecter. Sauf que depuis deux versions, Docker utilise une gestion réseau qui me permet de relancer le conteneur Xvfb sans avoir à relancer le conteneur Belphegor car la résolution de nom ne se fait plus (par défaut) via le fichier &amp;ldquo;/etc/hosts&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Du coup, en utilisant l&amp;rsquo;option &amp;ldquo;restart=always&amp;rdquo;, le conteneur xvfbserver redémarre sans intervention humaine. Le conteneur Belphegor, quant à lui, retrouve le nouveau serveur fraichement redémarré. Bref, un QOS amélioré.&lt;/p&gt;
&lt;h1 id=&#34;le-mot-de-la-fin&#34;&gt;Le mot de la fin&lt;/h1&gt;
&lt;p&gt;Quel que soit le type de conteneur que vous voulez créer, par pitié, évitez supervisord. Je sais que c&amp;rsquo;est pratique en soi, que ça permet de faire &amp;ldquo;un seul conteneur&amp;rdquo; qui gère tout comme un seul service. Mais cela va à l&amp;rsquo;encontre de la philosophie de micro-service.&lt;/p&gt;
&lt;p&gt;À la place, proposez un fichier &amp;ldquo;docker-compose&amp;rdquo; et séparez vos services. Le monde en sera que meilleur !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Go-Pipe, streamez à la unix/like en Go</title>
      <link>https://www.metal3d.org/blog/2016/go-pipe-streamez-%C3%A0-la-unix_like-en-go/</link>
      <pubDate>Mon, 11 Apr 2016 20:01:23 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/go-pipe-streamez-%C3%A0-la-unix_like-en-go/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Vous connaissiez les “pipelines” de Gulp, et bien voici celles de Go grâce à un boulot intéressant (mais datant d’un an déjà) de Gustavo Niemeyer sur sa page Go.pipe - un package permettant de traiter des flux, d’ajouter ses propres traitements, et surtout sans trop de douleurs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-82977c15-9a18-4847-b333-5b6d7ddcdbe0.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;L’idée est simple. En bash, on utilise des pipelines. C’est une notion courante qui utilise une notation spéciale qui permet de rediriger des entrée/sorties de commandes en commandes. L’idée de Gustavo est de permettre une notion de “pipeline” tout comme le fait Gulp-pipe.&lt;/p&gt;
&lt;h1 id=&#34;comment-ça-marche-&#34;&gt;Comment ça marche ?&lt;/h1&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;/h2&gt;
&lt;p&gt;Pas de grande surprise:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;go get gopkg.in/pipe.v2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;exemple-simple&#34;&gt;Exemple simple&lt;/h2&gt;
&lt;p&gt;Prenons l’exemple en bash:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cat /etc/passwd | sort | cut -f 1 -d &amp;quot;:&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette commande lit le fichier “/etc/password”, envoit la sortie dans la commande “sort” qui va classer le contenu. Enfin, on envoit ceci dans la commande “cut” pour récupéérer la première partie (parties séparées par des “:“).&lt;/p&gt;
&lt;p&gt;Et bien en Go, grâce à “pipe”, le code va être:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
    &amp;quot;fmt&amp;quot;

    &amp;quot;gopkg.in/pipe.v2&amp;quot;
)

func main() {
    p := pipe.Line(
        pipe.ReadFile(&amp;quot;/etc/passwd&amp;quot;),
        pipe.Exec(&amp;quot;sort&amp;quot;),
        pipe.Exec(&amp;quot;cut&amp;quot;, &amp;quot;-f&amp;quot;, &amp;quot;1&amp;quot;, &amp;quot;-d&amp;quot;, &amp;quot;:&amp;quot;),
    )

    if output, err := pipe.CombinedOutput(p); err != nil {
        fmt.Printf(&amp;quot;%v\n&amp;quot;, err)
    } else {
        fmt.Printf(&amp;quot;%s&amp;quot;, output)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour faire simple: “pipe.Line” attend autant d’argument que de partie de pipeline à brancher entre eux. À la fin, vous pouvez récupérer la sortie “combinée”. Vous pouvez aussi bien utiliser “pipe.DividedOutput()” qui retournera la sortie standard, la sortie d’errer et une “erreur” (dans cet ordre).&lt;/p&gt;
&lt;p&gt;Bref, c’est jolie.&lt;/p&gt;
&lt;h1 id=&#34;et-quoi-dautre-&#34;&gt;Et quoi d’autre ?&lt;/h1&gt;
&lt;p&gt;On ne s’arrête pas aux simples “commandes brnachées entre elles”, car Gustavo a pensé à ajouter deux trois fonctions bien sympatiques.&lt;/p&gt;
&lt;p&gt;Par exemple, on trouvera:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pipe.ReadFile(path) qui lit le contenu d’un fichier&lt;/li&gt;
&lt;li&gt;pipe.WriteFile(path) qui écrit dans un fichier&lt;/li&gt;
&lt;li&gt;pipe.Filter() qui prend en paramètre une fonction pour filtrer la sortie&lt;/li&gt;
&lt;li&gt;pip.Tee() pour permettre la double redirection (comme le fait la commande “tee”)&lt;/li&gt;
&lt;li&gt;et d’autres…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vous pouvez voir la liste des fonctions ici: &lt;a href=&#34;https://godoc.org/gopkg.in/pipe.v2&#34;&gt;https://godoc.org/gopkg.in/pipe.v2&lt;/a&gt; - inutile de vous préciser qu’il est important de lire la doc !.&lt;/p&gt;
&lt;h1 id=&#34;créer-sa-propre-commande&#34;&gt;Créer sa propre commande&lt;/h1&gt;
&lt;p&gt;Au lieu d’appeler des commande “sh”, vous pouvez créer votre propre fonction qui peut être injectée dans le pipeline. C’est très simple (ou presque).&lt;/p&gt;
&lt;p&gt;L’idée c’est que “pipe.Line()” attend des “pipe.Pipe”. Et justement, une fonction du package permet de créer cette structure en fonction de l’état du pipeline. Si c’est pas génial ça !&lt;/p&gt;
&lt;p&gt;Admettons que je veuille créer une fonction “ToUpper” qui puisse chopper le flux et mettre les caractères en lettre capitale.&lt;/p&gt;
&lt;p&gt;La fonction sera alors:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func ToUpper() pipe.Pipe {
    // returns the pipe.Pipe created by
    // a new Task
    return pipe.TaskFunc(func(s *pipe.State) error {
        var err error
        b := make([]byte, bytes.MinRead)

        // read stream
        for err == nil {
            if _, err = s.Stdin.Read(b); err != nil {
                break
            }

            // create buffer with uppercase letters
            // and copy it to output
            buff := bytes.NewBuffer(nil)
            buff.Write(bytes.ToUpper(b))
            _, err = io.Copy(s.Stdout, buff)
        }

        if err != io.EOF {
            return err
        }
        return nil
    })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je clarifie un point: une tâche (pipe.Task) utilise un état (pipe.State) dans lequel les entrées/sorties sont définies. Le “pipe.Pipe” aura donc le contexte du pipeline en cours. C’est évident quand on y réfléchis.&lt;/p&gt;
&lt;p&gt;J’aurai put utiliser “ioutil” et faire un “ReadAll” sur “s.Stdin”, mais l’idée c’est de “streamer”, donc de récupérer “coup par coup” la sortie de la tâche qui fourni le flux. Donc, je préfère lire par paquet de 512 octets (bytes.MinRead), et envoyer les données dans “s.Stdout” au fur et à mesure.&lt;/p&gt;
&lt;p&gt;Il ne reste plus qu’à injecter ma fonction dans le pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;p := pipe.Line(
    pipe.ReadFile(&amp;quot;/etc/passwd&amp;quot;),
    pipe.Exec(&amp;quot;sort&amp;quot;),
    ToUpper(),
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et ça marche super bien !&lt;/p&gt;
&lt;p&gt;Vous imaginez maintenant comment faire des choses sympas ? Par exemple, lire le contenu d’un flux HTTP, l’envoyer dans une commande de traitement et l’enregistrer dans un fichier tout en l’envoyant dans un port ouvert.&lt;/p&gt;
&lt;p&gt;Ça ressemblerai à ça:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;p := pipe.Line(
    ReadHTTP(&amp;quot;http://url.to.read&amp;quot;),
    ToUpper(),
    Tee(portWriter),
    WriteFile(&amp;quot;save.stream.txt&amp;quot;),
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En gros, ça va soulager pas mal de projets qui ont des flux à balancer un peu partout.&lt;/p&gt;
&lt;p&gt;En bref, c’est du “pipeline” propre et sans bavure.&lt;/p&gt;
&lt;h1 id=&#34;bref-bilan-&#34;&gt;Bref, bilan ?&lt;/h1&gt;
&lt;p&gt;Inutile de préciser que certaines choses ne peuvent pas marcher partout. Par exemple la commande “Exec” devra utiliser des commandes qui existent sur l’OS cible.&lt;/p&gt;
&lt;p&gt;L’auteur ne précise pas franchement comment créer un “pipe.Pipe” proprement, j’ai du lire le code pour comprendre. C’est un peu dommage mais honnêtement un peu de lecture de la doc plus approfondie m’aurait permi de trouver, de suite, la section qui en parle&lt;/p&gt;
&lt;p&gt;Voilà, personnellement je vais garder ça dans un coin parce que je suis persuadé que ça va me servir très bientôt.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>HubiC le service de stockage en ligne qui me fait presque aimer OVH</title>
      <link>https://www.metal3d.org/blog/2016/hubic-le-service-de-stockage-en-ligne-qui-me-fait-presque-aimer-ovh/</link>
      <pubDate>Mon, 11 Apr 2016 20:01:10 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/hubic-le-service-de-stockage-en-ligne-qui-me-fait-presque-aimer-ovh/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Avoir un service de stockage sympa, gérant les sauvegardes, pour environ 30Go à 50€/an, c’est quand même une idée intéressante que je ne pensais pas voir apparaitre. Mais OVH a réussi à me créer la surprise avec Hubic qui fonctionne suffisamment bien pour que je fasse ce que je veux. À savoir, non pas avoir un disque monté (ce qu’il sait faire) mais me permettre des sauvegardes incrémentales versionnées de différents répertoires. Un peu de scripting pour améliorer tout ça, et voilà mon billet qui vous explique.&lt;/p&gt;
&lt;p&gt;Avant toute chose, j’ai tout de même fouillé pour voir si DropBox ou Google pouvaient me permettre ce genre de chose.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DropBox: version “pro” pour 1To à 99€/an non mais sérieusement ?!&lt;/li&gt;
&lt;li&gt;GoogleDrive: 15Go gratuit (ce que je trouve très bien), mais 299€/an pour 30To, je ne plaisante pas c’est quand même une fortune !&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sachant qu’en plus Google ne propose pas de client Linux (officiel), ne permet pas la sauvegarde de répertoire choisis, ni de version… non sérieusement c’est pas ce dont j’avais été habitué de la part de la firme. Et ne me parlez pas du montage que propose Gnome, la lenteur d’accès est limite honteuse, à tel point que je l’ai désactivé.&lt;/p&gt;
&lt;p&gt;Bref, Xavier D. encore lui, m’a parlé de HubiC - quand j’ai appris que ça venait de OVH j’ai un peu déchanté, mais je suis bon joueur et je me dis qu’il faut tester ce produit avant de baver sur leur tête. Et force est de constaté que pour le prix, c’est pas mal. À part un point crucial, le débit.&lt;/p&gt;
&lt;p&gt;10To pour 50€/an c’est un offre intéressante, certainement la moins chère que j’ai vu. Restait alors à tester le client Linux et de voir si je pouvais sauvegarder une partie de mon serveur et mes fichiers locaux de mon poste de travail sans trop avoir mal.&lt;/p&gt;
&lt;h1 id=&#34;installer-le-client&#34;&gt;Installer le client&lt;/h1&gt;
&lt;p&gt;L’installation est simple pour Debian, un peu moins simple sur les autres distributions.&lt;/p&gt;
&lt;p&gt;Grosso-modo, soit vous installez le .deb depuis l’espace Linux, soit vous êtes sur une autre distribution et vous devez passer par le .tar.gz&lt;/p&gt;
&lt;p&gt;Je vais me pencher sur Fedora et CentOS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo dnf install mono-core mono-data-sqlite mono-data
cd /tmp
wget http://mir7.ovh.net/ovh-applications/hubic/hubiC-Linux/2.1.0/hubiC-Linux-2.1.0.53-linux.tar.gz
tar xf hubiC-Linux-2.1.0.53-linux.tar.gz
cd hubic
sudo make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, c’est pas si compliqué mais ça méritait d’être montré.&lt;/p&gt;
&lt;p&gt;Maintenant on va lancer le service, se loguer et voir ce que ça donne&lt;/p&gt;
&lt;h1 id=&#34;premier-coup-dœil&#34;&gt;Premier coup d’œil&lt;/h1&gt;
&lt;p&gt;Il suffit de démarrer le service:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hubic start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puis de se loguer avec, optionnellement, le répertoire de stockage de base à monter localement.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir ~/HubiC
hubic login mon_mail@domain.tld ~/HubiC
# ou si vous n&#39;aver pas envie de ce répertoire
hubic login mon_mail@domain.tld
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez omettre le répertoire si vous ne voulez utiliser que le système de sauvegarde que je vais vous expliquer après.&lt;/p&gt;
&lt;p&gt;Si vous voulez utiliser hubic sur un serveur sur lequel vous vous êtes connectés en SSH, il faut exporter les variables DBUS. La technique la plus simple est de mettre cette ligne dans votre “~/.bashrc“:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;export $(dbus-launch)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez aussi lancer cette commande manuellement. Maintenant ça va fonctionner.&lt;/p&gt;
&lt;p&gt;Petit édit: vous pouvez enregistrer votre mot de passe dans un fichier et utiliser “&amp;ndash;password_path=/chemin/vers/ce/fichier” à la commande de login pour automatiser le login&lt;/p&gt;
&lt;h1 id=&#34;permier-test&#34;&gt;Permier test…&lt;/h1&gt;
&lt;p&gt;Hou bordel… c’est super lent. Un débit inadmissible ! Mon épouse a payé 50€ pour avoir le même débit, grosso-modo on table sur du 100ko/s en moyenne, c’est presque honteux !&lt;/p&gt;
&lt;p&gt;Je parle d’une synchro faite sur mon serveur de prod, donc je n’ai pas de contrainte de débit. En fouillant un peu, j’ai appris que HubiC limite la bande passante upload à 10Mps/s soit 1200ko/s donc 1Mo/s environ… (si je ne me trompe pas hein, 10Mpbs/s = (10.000.000/8)/1024 ≈ 1220ko/s)&lt;/p&gt;
&lt;p&gt;Sauf que là je suis 10 fois en dessous du débit possible !&lt;/p&gt;
&lt;p&gt;Bref, passons sur ce sujet en priant pour que les 2 ans de messages du forum d’utilisateurs énervés fassent réagir l’équipe de OVH.&lt;/p&gt;
&lt;p&gt;Au passage: &lt;a href=&#34;http://travaux.ovh.com/?do=details&amp;amp;id=17305&#34;&gt;http://travaux.ovh.com/?do=details&amp;amp;id=17305&lt;/a&gt; et &lt;a href=&#34;http://travaux.ovh.com/?do=details&amp;amp;id=17008&#34;&gt;http://travaux.ovh.com/?do=details&amp;amp;id=17008&lt;/a&gt; montrent que c’est le sacré merdier en ce moment.&lt;/p&gt;
&lt;h1 id=&#34;les-sauvegardes&#34;&gt;Les sauvegardes&lt;/h1&gt;
&lt;p&gt;HubiC, outre le répertoire de partage qui ne me sert pas pour le moment, me permet de faire des sauvegardes. J’ai vu le client graphique tourner sur Windows et l’intégration est pas mal. En gros sur Windows vous pouvez faire un clique droit sur un dossier et paramétrer sa sauvegarde.&lt;/p&gt;
&lt;p&gt;Sur Linux, c’est dans un terminal que ça se passe. Et en fait ça m’arrange, surtout sur mon serveur, car j’ai vu un paquet d’options qui m’ont vraiment rendu service.&lt;/p&gt;
&lt;p&gt;Voilà comment créer une procédure de sauvegarde de manière “basique”:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;hubic backup create --name=&amp;quot;un_nom_unique&amp;quot; \
    --kept_versions=3 \
    --frequency=&amp;quot;Daily&amp;quot; \
    /répertoire
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Les options sont franchement explicites.&lt;/p&gt;
&lt;p&gt;Ce qui m’arrange, c’est le fait de nommer mes sauvegardes. J’utilise par exemple un nom “metal3d.org_www” pour sauver mon répertoire “/var/www”, et je l’identifie bien.&lt;/p&gt;
&lt;p&gt;Sans option “&amp;ndash;frequency” la sauvegarde ne se lancera pas. Et c’est tout aussi intéressant car je peux la déclencher “à la Unix” via un crontab. Du coup, je peux par exemple lancer un dump de base de données avant de lancer ma sauvegarde.&lt;/p&gt;
&lt;p&gt;Bref, pour ne pas avoir de synchro programmée:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;hubic backup create --name=&amp;quot;un_nom_unique&amp;quot; \
    --kept_versions=3 \
    /répertoire
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et à moi de faire un crontab pour réellement lancer une sauvegarde via la commande “update”:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;hubic backup update un_nom_unique
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez voir l’état de vos backup de cette manière:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;hubic backup info
  Name                 Attached  Local path   Last backup      Size
  metal3d.org_etc      Yes       /etc/                        -  30,44 MB
  metal3d.org_opt      Yes       /opt/                        -  42,32 MB
  metal3d.org_www      Yes       /var/www/        -           0 B
  pferlet_Documents    No        -           06/04/2016 13:31  15,92 MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qu’il faut bien comprendre, c’est que “pferlet_Documents” n’est pas attaché à mon serveur, il provient de mon poste de travail. Du coup je ne connais pas son chemin “local”.&lt;/p&gt;
&lt;p&gt;Reparlons du débit. La sauvegarde de mon “/etc” qui doit prendre 30Mo a pris (accrochez-vous) 2h !&lt;/p&gt;
&lt;h1 id=&#34;monitorer&#34;&gt;Monitorer&lt;/h1&gt;
&lt;p&gt;Pour voir ce qu’il se passe, il y a au moins deux points d’accès: les logs et la commande “status”.&lt;/p&gt;
&lt;p&gt;La commande de status est simplement “hubic status”.&lt;/p&gt;
&lt;p&gt;Personnellement j’aime “watcher” l’état de ma synchro de cette manière:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ watch -n0 hubic status

State: Busy
Up: 0 B/s (0 B/s)       Down: 0 B/s (0 B/s)

Account: XXX@XXX.XX
Synchronized directory: /rep/hubic/
Usage: 88,62 MB/25 GB

Queue:
        Uploads: 1998 (5,23 MB) + 1 running
        Downloads: 0 (0 B) + 0 running
        Misc: 0 + 1 running

Running operations:
        Upload for /opt/XXX/YYYY
        RemoteMkdir for /opt/XXX/YYY

Last events:
        [06/04/2016 18:51:21|Warning] Skip synchronisation for XXXXXXX
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et l’autre méthode:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;tail -f ~/.config/hubiC/application.log 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En clair, ils ont pensé à respecter les chemins standard freedesktop, ça à le mérite d’être salué.&lt;/p&gt;
&lt;h1 id=&#34;revenons-au-débit&#34;&gt;Revenons au débit…&lt;/h1&gt;
&lt;p&gt;Bon sang, je me tatais à taper mon numéro de carte pour avoir les 10To mais honnêtement j’ai du mal. Ce débit en m’enchante pas car le jour où je veux récupérer une sauvegarde pour vite remettre un serveur en place, je sens que je vais pleurer pendant 3 jours avant d’avoir le backup en main.&lt;/p&gt;
&lt;p&gt;J’ai suivi quelques idées trouvées par-ci par-là, notamment un “tweak” à faire sur votre installation.&lt;/p&gt;
&lt;p&gt;Éditez le fichier “~/.mono/registry/CurrentUser/software/ovh/hubic-sync/values.xml” et ajoutez une valeur juste avant la balise fermante (&lt;code&gt;&amp;lt;/values&amp;gt;&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;value name=&amp;quot;ParallelUploads&amp;quot; type=&amp;quot;string&amp;quot;&amp;gt;1&amp;lt;/value&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette option est censée permettre des uploads en parallèle… mouais… ça n’a rien changé pour moi, ou alors c’est tellement ridicule comme optimisation que je ne la perçois pas.&lt;/p&gt;
&lt;h1 id=&#34;bilan&#34;&gt;Bilan&lt;/h1&gt;
&lt;p&gt;Il ne manque que du débit. Sérieusement ! Je me fiche de l’interface graphique manquante ou de leur interface Web, elles ne me servent pas. L’outil en ligne de commande est largement suffisante et a un grand nombre d’options.&lt;/p&gt;
&lt;p&gt;Le mode “sauvegarde” est vraiment bien pensé. Mais ce foutu débit m’a directement fait ranger HubiC dans un tirroir “à voir plus tard”. Sachant que depuis 3 ans tout le monde se plaint du débit minable que propose OVH sur ce service, j’ai peur que ça ne bouge pas pendant plusieurs mois, voir années.&lt;/p&gt;
&lt;p&gt;Donc, pour dresser le bilan:&lt;/p&gt;
&lt;h2 id=&#34;points-faibles&#34;&gt;Points faibles:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;lent, mais leeeeeennnnt&lt;/li&gt;
&lt;li&gt;des bugs… parfois le “update” refuse le “nom de sauvegarde” et me demande de donner le chemin&lt;/li&gt;
&lt;li&gt;pas de RPM (sérieux, je vais en proposer un en copr parce que pffff….)&lt;/li&gt;
&lt;li&gt;utilisation en ligne de commande seulement pour le moment&lt;/li&gt;
&lt;li&gt;l’interface Web est infâme, lente, plante, refuse parfois de s’afficher&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;points-forts&#34;&gt;Points forts:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;le rapport espace/prix, indéniablement&lt;/li&gt;
&lt;li&gt;utilisation en ligne de commande, je peux l’utiliser sur mon serveur&lt;/li&gt;
&lt;li&gt;la gestion de backup versionnées en dehors de l’espace de base&lt;/li&gt;
&lt;li&gt;récupérer une sauvegarde depuis le site, ou juste un répertoire, ou juste un fichier: bravo&lt;/li&gt;
&lt;li&gt;les options pour voir l’état du service local (status, backup info…)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;ressources&#34;&gt;Ressources&lt;/h1&gt;
&lt;p&gt;Ces liens m’ont aidé:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://hubic.com/fr/&#34;&gt;https://hubic.com/fr/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://fr.louis-fradin.net/tutoriels/install-hubic-fedora-22/&#34;&gt;http://fr.louis-fradin.net/tutoriels/install-hubic-fedora-22/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://mir7.ovh.net/ovh-applications/hubic/hubiC-Linux/&#34;&gt;http://mir7.ovh.net/ovh-applications/hubic/hubiC-Linux/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://forums.hubic.com/showthread.php?230-hubic-Linux-sortie-de-la-version-b%EAta&#34;&gt;https://forums.hubic.com/showthread.php?230-hubic-Linux-sortie-de-la-version-b%EAta&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.ovh.com/fr/&#34;&gt;https://www.ovh.com/fr/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://support.google.com/drive/answer/2375123?hl=fr&#34;&gt;https://support.google.com/drive/answer/2375123?hl=fr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.dropbox.com/pro&#34;&gt;https://www.dropbox.com/pro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </description>
    </item>
    
    <item>
      <title>Monter un partage webdav sharepoint sur Linux</title>
      <link>https://www.metal3d.org/blog/2016/monter-un-partage-webdav-sharepoint-sur-linux/</link>
      <pubDate>Mon, 11 Apr 2016 20:00:58 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/monter-un-partage-webdav-sharepoint-sur-linux/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Quand on m’a posé cette question, au boulot, chez un client “full Microsoft”, j’ai bêtement cru que tout allait se passer comme sur des roulettes. Et en fait non… Alors, comment créer un point de montage Microsoft sharepoint (qui utilise webdav) sur Linux avec le moins de douleur ? La solution est expliquée ici.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://www.metal3d.org/static/upload/08ee0a69-1faa-42ae-8c68-d4d38cade7e4-linuxsharepoint.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;on-prépare-la-machine&#34;&gt;On prépare la machine&lt;/h1&gt;
&lt;p&gt;D’abord, il vous faut “davfs2”. Je ne sais pas comment ça se passe sur Ubuntu mais sur Fedora je passe par la ligne:&lt;/p&gt;
&lt;p&gt;sudo dnf install davfs2
Quand ce paquet est installé, je passe mon utilisateur dans le groupe “davfs2”&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo usermod -a -G davfs2 $USER
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, deux possibilités:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;soit vous reconnectez votre utilisateur à sa session Gnome (quitter, revenir)&lt;/li&gt;
&lt;li&gt;soit vous ouvrez un nouveau shell avec la commande bash su - VOTRE_USER&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le fait est qu’à partir de là, il faudra que le shell soit au courant que votre utilisateur est dans le groupe “davfs2”, et malheureusement sous UNIX/Linux il faut “reloguer” le compte. C’est-à-dire “ouvrir une nouvelle session”.&lt;/p&gt;
&lt;h1 id=&#34;premier-test&#34;&gt;Premier test&lt;/h1&gt;
&lt;p&gt;Normalement, vous devez connaitre l’adresse Web du sharepoint. On va prendre un exemple: http://monsharepoint/Sites/&lt;/p&gt;
&lt;p&gt;Si tout va bien, vous avez accès à un dossier, on doit donc être capable de visiter http://monsharepoint/Sites/MonDossier&lt;/p&gt;
&lt;p&gt;Bref, le but maintenant c’est de monter cet espace dans un répertoire local.&lt;/p&gt;
&lt;p&gt;On va créer un répertoire “sharepoint” et monter le dossier dedans.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir -p ~/sharepoint
sudo mount -t davfs  http://monsharepoint/Sites/MonDossier ~/sharepoint -o user,rw,nosuid,noauto,uid=$(id -u),gid=$(id -g)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Normalement, la commande vous demande:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;votre mot de passe pour sudo&lt;/li&gt;
&lt;li&gt;votre compte de domaine (compte active directory)&lt;/li&gt;
&lt;li&gt;votre mot de passe de domaine&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et, malgré un warning, vous devriez avoir des fichiers dans le répertoire “~/sharepoint”.&lt;/p&gt;
&lt;p&gt;Si tout est ok, on va passer au mode “automatique”. Démontez le dossier avant de passer à la suite:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo umount ~/sharepoint
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;automatisation&#34;&gt;Automatisation&lt;/h1&gt;
&lt;p&gt;C’est bien beau de taper une ligne de commande longue comme une compilation Java, mais on a pas envie de se taper constamment un login et un mot de passe tous les jours, juste pour monter le dossier. Bref, l’idée est de créer un point de montage propre qui, en plus, permettra de ne pas se battre avec les uid/gid.&lt;/p&gt;
&lt;p&gt;Et en plus, on pourra se passer de “sudo”, elle est pas belle la vie ?&lt;/p&gt;
&lt;p&gt;On va donc éditer le fichier “/etc/fstab”. Partons du principe que je suis l’utilisateur “FOO”, vous aurez donc à remplacer “FOO” par votre nom d’utilisateur:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://monsharepoint/Sites/MonDossier /home/FOO/sharepoint davfs noauto,user 0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Petit rappel:&lt;/strong&gt; les zéros veulent dire, dans l’ordre:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pas de montage au démarrage de la machine&lt;/li&gt;
&lt;li&gt;si le montage plante, on s’en fout&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bref, tout ça ne suffit pas.&lt;/p&gt;
&lt;p&gt;Dans un terminal, créez un répertoire “~/.davfs2” dans lequel on place un fichier “secrets” qui ne doit être lisible que par l’utilisateur:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir ~/.davfs2
touch ~/.davfs2/secrets
chmod 600 ~/.davfs2/secrets
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant éditez ce fichier et entrez les informations suivantes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;/home/FOO/sharepoint LOGIN_AD PASS_AD 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans ces lignes, remplacez bien:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FOO par votre nom d’utilisateur Linux&lt;/li&gt;
&lt;li&gt;LOGIN_AD par votre login sur le domaine&lt;/li&gt;
&lt;li&gt;PASS_AD par votre mot de passe sur le domaine&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On y est !&lt;/p&gt;
&lt;p&gt;Tapez maintenant:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mount ~/sharepoint
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hop, plus de sudo, plus de mot login/pass à taper. C’est sympa hein !&lt;/p&gt;
&lt;h1 id=&#34;automatiser-le-tout&#34;&gt;Automatiser le tout&lt;/h1&gt;
&lt;p&gt;Reste que vous avez certainement envie de monter ce dossier automatiquement.&lt;/p&gt;
&lt;p&gt;Si vous mettez un “1” à la place du premier “0” dans le fichier “/etc/fstab” alors il peut que ça fonctionne. Mais personnellement j’ai plusieurs utilisateurs sur mon pc et je préfère que le montage se fasse au login de mon utilisateur et seulement si il n’est pas déjà monté.&lt;/p&gt;
&lt;p&gt;Il y a deux méthodes. La première utilise la commande “mountpoint” que je trouve fabuleusement utile mais qui peut ne pas exister sur votre système; la seconde méthode utilise “mount” combiné à “grep”.&lt;/p&gt;
&lt;p&gt;Pensez à bien adapter les commandes et scripts à votre installation !&lt;/p&gt;
&lt;p&gt;Ici “sharepoint” est le nom du répertoire, mais cela peut tout aussi bien être un mot contenu dans l’url webdav qui identifie, de manière unique, ce point de montage. Bref, je vous fais confiance.&lt;/p&gt;
&lt;h1 id=&#34;méthode-avec-mountpoint&#34;&gt;Méthode avec “mountpoint”&lt;/h1&gt;
&lt;p&gt;Dans “~/.bashrc“:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mountpoint -q ~/sharepoint || mount ~/sharepoint
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sinon pour les utilisateurs de “fish”, dans “~/.config/fish/config.fish“:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mountpoint -q ~/sharepoint; or mount ~/sharepoint
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;méthode-avec-mount-et-grep&#34;&gt;Méthode avec “Mount” et “Grep”&lt;/h2&gt;
&lt;p&gt;Si vous n’avez pas la commande “mountpoint” vous pouvez quand même vous débrouiller en combinant “mount” et “grep”.&lt;/p&gt;
&lt;p&gt;Dans “~/.bashrc“:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mount | grep sharepoint 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null || mount ~/sharepoint &amp;amp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sinon pour les utilisateurs de “fish”, dans “~/.config/fish/config.fish“:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mount | grep sharepoint 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null; or mount ~/sharepoint &amp;amp;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;et-donc&#34;&gt;Et donc…&lt;/h1&gt;
&lt;p&gt;Bha, ça fonctionne. C’est ce qu’on demandait.&lt;/p&gt;
&lt;h1 id=&#34;bilan&#34;&gt;Bilan&lt;/h1&gt;
&lt;p&gt;Peu de documentation, bien que le manpage de “mount.davfs” est particulièrement fourni, il faut se creuser les méninges avant de comprendre ce qui foire.&lt;/p&gt;
&lt;p&gt;Par exemple, ici, on a eut ce genre de problème:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;certains utilisateur ont besoin de renseigner leur “utilisateur” avec le domaine (domain\username)&lt;/li&gt;
&lt;li&gt;si on montre un répertoire “trop haut”, alors l’accès est refusé
ouvrir un fichier xlsx ou docx demande “deux ouvertures”, une qui plante avec une erreur “entrée/sortie”, et une qui enfin fonctionne&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bref, je ne sais pas si c’est WebDav ou Active Directory mais franchement c’est pas la technologie du siècle, et Linux souffre fortement d’une absence de support en ce qui concerne ce protocole.&lt;/p&gt;
&lt;p&gt;Soit dit en passant, il parait que Nautlilus, le navigateur de fichier Gnome, arriverait à ouvrir un emplacement Webdav avec un “scheme” de la forme “dav://“. L’explication est sur ce site. Mais ça refuse de fonctionner chez moi, certainement une histoire de certificat ou je ne sais quoi…&lt;/p&gt;
&lt;p&gt;Le souci n’est pas webdav en soi. J’ai testé à la maison avec un montage webdav simple (apache) et je n’ai pas eut de souci. Inutile d’utiliser un point de montage, nautilus fonctionne bien. C’est bel et bien une des deux raisons ci-dessous qui est la cause de tous nos soucis:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l’installation est pas bonne&lt;/li&gt;
&lt;li&gt;Microsoft ne sait pas utiliser correctement le protocole&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Misez sur la raison qui vous semble la plus juste :)&lt;/p&gt;
&lt;h1 id=&#34;ressources&#34;&gt;Ressources&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://wiki.archlinux.org/index.php/Davfs&#34;&gt;https://wiki.archlinux.org/index.php/Davfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://linux.die.net/man/8/mount.davfs&#34;&gt;http://linux.die.net/man/8/mount.davfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.webdavsystem.com/server/access/gnome_nautilus&#34;&gt;http://www.webdavsystem.com/server/access/gnome_nautilus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://savannah.nongnu.org/projects/davfs2&#34;&gt;http://savannah.nongnu.org/projects/davfs2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </description>
    </item>
    
    <item>
      <title>Utiliser les closures en Javascript</title>
      <link>https://www.metal3d.org/blog/2016/utiliser-les-closures-en-javascript/</link>
      <pubDate>Mon, 28 Mar 2016 18:40:32 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/utiliser-les-closures-en-javascript/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;J&amp;rsquo;en ai parlé &lt;a href=&#34;https://www.metal3d.org/ticket/l-interet-des-closures-en-go&#34;&gt;il y peu de temps en Go&lt;/a&gt; mais je sais que beaucoup d&amp;rsquo;entre vous utilise Javascript. Et justement, l&amp;rsquo;utilisation de closure en JS est très intéressante. On va voir dans ce billet que bien souvent on devrait réfléchir avec ce pattern que ce soit avec du JS pur ou avec Angular.&lt;/p&gt;
&lt;p&gt;Petit rappel: une closure est une fonction. Cette fonction sait garder en mémoire l&amp;rsquo;état de variables. Ainsi, on peut créer des générateurs et/ou retourner des fonctions. Car en JS, comme en Go ou en Python (pour ne citer qu&amp;rsquo;eux), &lt;strong&gt;une fonction est un type&lt;/strong&gt; et donc peut être assignée à une variable.&lt;/p&gt;
&lt;h1 id=&#34;douleur-aux-yeux&#34;&gt;Douleur aux yeux&lt;/h1&gt;
&lt;p&gt;Combien de fois j&amp;rsquo;ai vu ça:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;
function foo(){
    function bar(){
        console.log(&amp;quot;coucou&amp;quot;);
    }

    document.querySelector(&#39;a&#39;).addEventListener(&#39;click&#39;, onClick);
    
    
    var self = this;
    function onClick(){
        self.bar()
    });
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous le repérez ce foutu souci qu&amp;rsquo;on a en JS (ES5) ? Et oui, le fameux &amp;ldquo;&lt;code&gt;this&lt;/code&gt;&amp;rdquo; perdu dans des fonctions appelantes qui nous force à garder une référence dans une variable. Ici, c&amp;rsquo;est &amp;ldquo;&lt;code&gt;self&lt;/code&gt;&amp;rdquo; qui fait office de gardien.&lt;/p&gt;
&lt;p&gt;Tous les développeurs un peu expérimentés et ayant bossé un temps soit peu dans les profondeurs de JS ont eut ce souci au moins une fois. C&amp;rsquo;est agaçant, on perd en lisibilité et franchement c&amp;rsquo;est moche.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Oui on peut utiliser &amp;ldquo;&lt;code&gt;bind()&lt;/code&gt;&amp;rdquo; dans pas mal de cas&lt;/strong&gt;, l&amp;rsquo;idée n&amp;rsquo;est pas d&amp;rsquo;outrepasser cette méthode, mais souvent on ne peut pas l&amp;rsquo;utiliser.&lt;/p&gt;
&lt;p&gt;Une autre façon de faire est d&amp;rsquo;utiliser les &lt;strong&gt;closures&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id=&#34;les-closures-pensez-y-&#34;&gt;Les closures, pensez-y !&lt;/h1&gt;
&lt;p&gt;Il suffit de générer la fonction &amp;ldquo;&lt;code&gt;onClick&lt;/code&gt;&amp;rdquo;. C&amp;rsquo;est tout bête mais c&amp;rsquo;est franchement plus logique. Surtout si vous avez dans l&amp;rsquo;idée que &amp;ldquo;self&amp;rdquo; puisse représenter d&amp;rsquo;autres classes.&lt;/p&gt;
&lt;p&gt;Voici donc un premier exemple plus &amp;ldquo;propre&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;
function foo(){
    function bar(){
        console.log(&amp;quot;coucou&amp;quot;);
    }

    document.querySelector(&#39;a&#39;).addEventListener(&#39;click&#39;, getOnClick(this));
    
    // génère une fonction qui utilise &amp;quot;obj&amp;quot;
    function getOnClick(obj) {
        return function (){
            obj.bar()
        });
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ça parait con comme ça, mais ça règle le souci. D&amp;rsquo;une part nous n&amp;rsquo;avons plus besoin de &amp;ldquo;self&amp;rdquo;, mais en plus la fonction retournée peut utiliser n&amp;rsquo;importe quel objet qui contient la méthode &amp;ldquo;bar&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Et c&amp;rsquo;est pas fini !&lt;/p&gt;
&lt;h1 id=&#34;et-en-plus&#34;&gt;Et en plus&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Je me suis retrouvé à devoir utiliser du &amp;ldquo;jsonp&amp;rdquo;. Il n&amp;rsquo;y a pas de faute de frappe, le &amp;ldquo;p&amp;rdquo; à la fin de &amp;ldquo;jsonp&amp;rdquo; est bien là pour une raison. JsonP est une manière détournée de pouvoir utiliser un appel &amp;ldquo;asynchrone&amp;rdquo; d&amp;rsquo;une ressource externe sans se prendre une exception si la ressource est en dehors du domaine appelant, et que ce dernier ne gère pas les CORS (Cross Origin Resource Sharing).&lt;/p&gt;
&lt;p&gt;Prenons un exemple.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai plusieurs articles qui affichent des &amp;ldquo;tags&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Je veux appeler une API qui récupère des images à insérer dans chaque article à partir de leurs tags. Sauf que voilà, cette API ne me permet pas d&amp;rsquo;utiliser XHTTPRequest (faussement appelée AJAX), et je dois donc fournir le nom d&amp;rsquo;une fonction en &amp;ldquo;callback&amp;rdquo;. Callback qui prend en argument les données que l&amp;rsquo;appel doit me retourner.&lt;/p&gt;
&lt;p&gt;En gros, je crée une balise &amp;ldquo;script&amp;rdquo; qui pointe sur &amp;ldquo;&lt;code&gt;api.to.call/?&amp;amp;callback=TOTO&lt;/code&gt;&amp;rdquo; =&amp;gt; le contenu de cette appel va me retourner quelque chose du genre:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;TOTO({
    // ... du contenu
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et donc, comme c&amp;rsquo;est une balise &amp;ldquo;script&amp;rdquo; que j&amp;rsquo;ai utilisé, la fonction &amp;ldquo;TOTO&amp;rdquo; sera appelée.&lt;/p&gt;
&lt;p&gt;Je simplifie mais c&amp;rsquo;est grosso-modo ceci que je vais faire et qui ne marchera pas.&lt;/p&gt;
&lt;p&gt;Il va falloir:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stocker une fonction par article sinon on ne saura pas où injecter l&amp;rsquo;image - on va les appeler &amp;ldquo;addImage0&amp;rdquo;, puis &amp;ldquo;addImage1&amp;rdquo; etc. On les gardera dans &amp;ldquo;window&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;appeler l&amp;rsquo;api en question en fournissant le nom de la fonction à appeler quand on a un résultat&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Qu&amp;rsquo;on soit bien clair sur la question des fonctions &amp;ldquo;addImageN&amp;rdquo;, je dois les enregistrer dans &amp;ldquo;window&amp;rdquo; parce que la balise d&amp;rsquo;appel en jsonp va appeler la fonction &lt;strong&gt;globale&lt;/strong&gt; &amp;ldquo;addImageN&amp;rdquo;; or je génère tout ça depuis une fonction (addEventListener), donc je force l&amp;rsquo;insertion de ces fonctions de manière globale. C&amp;rsquo;est tordu mais c&amp;rsquo;est quasiement inévitable.&lt;/p&gt;
&lt;p&gt;Et je suis &lt;strong&gt;obligé&lt;/strong&gt; de créer une fonction par article parce que l&amp;rsquo;API ne me permet pas de définir un argument personnel qui me permet de savoir dans quel article je dois injecter l&amp;rsquo;image.&lt;/p&gt;
&lt;p&gt;Bref, c&amp;rsquo;est le boxon. Mais c&amp;rsquo;est gérable. Sauf que ce code ne va pas marcher:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;
/*CODE FOIREUX, NE MARCHERA PAS !!!*/

document.addEventListener(&#39;load&#39;, function(){

    var head = document.querySelector(&#39;head&#39;);
    var articles = document.querySelectorAll(&#39;.article&#39;)
    var i, j, script, article, tags, fncName;
    
    for (i=0; i&amp;lt;articles.length; i++) {
        article=articles[i];
        tags = article.querySelector(&#39;.tag&#39;);
        
        // le nom de la fonction à appeler
        fncName = &#39;addImage&#39; + i;
        
        // on crée la fonction globale qui ajoute l&#39;image dans &amp;quot;article&amp;quot;
        window[fncName] = function(data){
            var img = document.createElemen(&#39;img&#39;);
            img.src = data;
            article.appendChild(img);
        };
        
        // pour chaque tag
        for (k=0; k&amp;lt;tags.length; k++) {
            script = document.createElement(&#39;script&#39;);
            script.setAttribute(&#39;src&#39;, &#39;http://api.to.call/?&amp;amp;callback&#39;+fncName+&#39;&amp;amp;q=&#39;+tags[k]);
            head.appendChild(script); // on inject le script
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et bien rien ne marche comme on le veut. Dans mon cas (si j&amp;rsquo;ai pas merdé mon exemple), toutes les images apparaissent dans le dernier article. Et c&amp;rsquo;est très logique.&lt;/p&gt;
&lt;p&gt;La variable &amp;ldquo;article&amp;rdquo; change à chaque itération. Quand la fonction &amp;ldquo;fncName&amp;rdquo; sera appelée, par exemple &amp;ldquo;addImage1&amp;rdquo;, et bien la variable &amp;ldquo;article&amp;rdquo; est positionné sur l&amp;rsquo;article &amp;ldquo;4&amp;rdquo; par exemple. Donc, c&amp;rsquo;est pas bon.&lt;/p&gt;
&lt;p&gt;La solution la plus simple, qui n&amp;rsquo;ajoute pas de &amp;ldquo;classe css&amp;rdquo;, qui ne cherche pas à manipuler des variables rémanentes dans la fonction, etc, est de &lt;em&gt;générer la fonction qui ajoute l&amp;rsquo;image&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Voici comment je m&amp;rsquo;y prends:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;
/*CODE QUI FONCTIONNE !!!*/

document.addEventListener(&#39;load&#39;, function(){

    var head = document.querySelector(&#39;head&#39;);
    var articles = document.querySelectorAll(&#39;.article&#39;)
    var i, j, script, article, tags, fncName;

    // cette fonction génère une fonction.
    function generateAddImageFnc(elem) {
        // on retourne la fonction qui 
        // manipule un element passé en argument.
        return function(data){
            var img = document.createElemen(&#39;img&#39;);
            img.src = data;
            elem.appendChild(img);
        }
    }
    
    for (i=0; i&amp;lt;articles.length; i++) {
        article=articles[i];
        tags = article.querySelector(&#39;.tag&#39;);
        
        // le nom de la fonction à appeler
        fncName = &#39;addImage&#39; + i;
        
        // on appelle le générateur
        window[fncName] = generateAddImageFnc(article);
        
        // pour chaque tag
        for (k=0; k&amp;lt;tags.length; k++) {
            script = document.createElement(&#39;script&#39;);
            script.setAttribute(&#39;src&#39;, &#39;http://api.to.call/?&amp;amp;callback&#39;+fncName+&#39;&amp;amp;q=&#39;+tags[k]);
            head.appendChild(script); // on inject le script
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et cette fois ça fonctionne. La raison est simple: à chaque appel du générateur nous passons l&amp;rsquo;élement (article) qui est gardé en mémoire dans la closure (generateAddImageFnc). Chaque appel à cette fonction alloue de la mémoire qui reside pour la fonction retournée.&lt;/p&gt;
&lt;p&gt;Nous n&amp;rsquo;avons pas utilisé de &amp;ldquo;bind&amp;rdquo;, nous avons simplement éliminé le souci en utilisant un &lt;strong&gt;générateur&lt;/strong&gt; ou plutôt une &lt;strong&gt;closure&lt;/strong&gt;. Un générateur ayant plutôt pour vocation de rester actif (par exemple un compteur ou un itérateur infini comme je l&amp;rsquo;ai présenté dans mon &lt;a href=&#34;https://www.metal3d.org/ticket/l-interet-des-closures-en-go&#34;&gt;article pour Go&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Ainsi, quand nous appellerons &amp;ldquo;addImage2&amp;rdquo; nous serons sûrs que l&amp;rsquo;article est celui qui a été référencé dans la closure.&lt;/p&gt;
&lt;p&gt;Ça parait obscure pour certains mais c&amp;rsquo;est pourtant l&amp;rsquo;un des avantages des closures, et surtout c&amp;rsquo;est certainement la meilleure raison de les utiliser.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>L&#39;intérêt des closures en Go</title>
      <link>https://www.metal3d.org/blog/2016/lint%C3%A9r%C3%AAt-des-closures-en-go/</link>
      <pubDate>Sun, 20 Mar 2016 10:58:07 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/lint%C3%A9r%C3%AAt-des-closures-en-go/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Vous avez entendu parler des &amp;ldquo;closures&amp;rdquo; et &amp;ldquo;generators&amp;rdquo; qui sont implémentés par plusieurs langages (comme le Python). Mais je sais aussi que beaucoup n&amp;rsquo;ont pas conscience de l&amp;rsquo;intérêt particulier de ce pattern. Et comme je me suis retrouvé dans une situation où un generator m&amp;rsquo;a fait gagné beaucoup de temps, je vais vous montrer à quoi ça sert avec deux exemples: un qui explique le fonctionnement, et un autre qui peut vous rendre service, à savoir &amp;ldquo;itérer indéfiniement&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Une closure est une fonction. Si si, je vous assure ce n&amp;rsquo;est ni plus ni moins que cela. A la différence près que la closure peut garder son état en mémoire. Mais vous allez me dire que quand on a appelé une fonction et qu&amp;rsquo;on a traité la valeur la de retour alors on perd son état. Oui, effectivement. Sauf dans le cas où ce que vous retournez est une fonction.&lt;/p&gt;
&lt;p&gt;Le premier exemple est celui que je donne souvent pour montrer ce qu&amp;rsquo;est une closure de type &amp;ldquo;generator&amp;rdquo;. Elle va initialiser un compteur et retourner une fonction qui permet d&amp;rsquo;incrémenter ce dernier, et le retourner.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;// Counter retourne une fonction qui retourne un entier
func Counter() func() int {
    count := 0
    return func() int{
        count++
        return count
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, on va s&amp;rsquo;en servir de cette manière:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;c := Counter()
fmt.Println(c())
fmt.Println(c())
fmt.Println(c())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A chaque appel de &lt;code&gt;&amp;quot;c()&amp;quot;&lt;/code&gt;, la variable &amp;ldquo;counter&amp;rdquo; est incrémentée et on l&amp;rsquo;affiche. L&amp;rsquo;intérêt est aussi de pouvoir avoir plusieurs compteurs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;c1 := Counter()
c2 := Counter()
fmt.Println(c1())
fmt.Println(c2())
fmt.Println(c1())
fmt.Println(c2())
fmt.Println(c2())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela va afficher:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1
1
2
2
3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bien. C&amp;rsquo;est un générateur simple, pas super utile en soi mais qui a le mérite de vous montrer le principe. Passons à l&amp;rsquo;exemple qui, lui, va vous permettre de réalier un générateur plus intéressant.&lt;/p&gt;
&lt;p&gt;Imaginons que nous ayons un &amp;ldquo;map&amp;rdquo; dont les clefs sont des chaines. Et imaginons que nous voulions itérer sur les clefs indéfiniement. Quand nous atteignons la fin de la liste, il faut revenir au début. J&amp;rsquo;explique:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;items := map[string]int{
    &amp;quot;A&amp;quot; : 0
    &amp;quot;B&amp;quot; : 0
    &amp;quot;C&amp;quot; : 0
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je veux donc être capable d&amp;rsquo;itérer en ayant, coup par coup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A
B
C
A
B
C
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il existe plusieurs façons de faire, mais celle que j&amp;rsquo;apprécie est le generator. Je vous montre étape par étape.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;algo de rotation infini est à peu près:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;for {
    for key, _ := range items {
        // faire un truc avec key
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous avez compris, je fais une boucle &amp;ldquo;for&amp;rdquo; sans sortie et une autre qui itère sur les &amp;ldquo;items&amp;rdquo;. Ainsi, quand la boucle interne qui itère sur les clefs a terminé, et bien on recommence.&lt;/p&gt;
&lt;p&gt;Mais il faut &amp;ldquo;faire un truc&amp;rdquo; avec la clef. Si j&amp;rsquo;assigne indéfiniement, je vais faire boucler ma fonction comme un bourrin et à l&amp;rsquo;arrivée on sera pas plus avancée. L&amp;rsquo;idée pas trop débile est donc d&amp;rsquo;écrire la valeur dans un canal qui va bloquer tant qu&amp;rsquo;on l&amp;rsquo;a pas lu.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;// attendez, ça marchera pas encore

func Loop(c chan string){
    for {
        for k, _ := range items {
            c &amp;lt;- k
        }
    }
}

// et plus loin:

c := make(chan string)
go Loop(c)
fmt.Println(&amp;lt;-c)
fmt.Println(&amp;lt;-c)
fmt.Println(&amp;lt;-c)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est déjà pas mal. Mais c&amp;rsquo;est pas &amp;ldquo;élégant&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Pourquoi je trouve pas ça  élégant ? nous devons nous même créer le canal, puis lancer la goroutine (tâche en concurrence) ce qui me chagrine un peu.&lt;/p&gt;
&lt;p&gt;On va donc faire autremement. Faire en sorte d&amp;rsquo;avoir un générateur qui va lancer la goroutine, fournir les valeurs au canal et permettre d&amp;rsquo;avoir la valeur suivante à chaque appel.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;
// generateur
func Loop() func() string {
    // le canal qui prend les valeurs
    c:=make(chan string)
    
    // on lance ça en tache de fond
    go func(){
        for {
            for k, _ := range items {
                c &amp;lt;- k
            }
        }
    }()
    
    // on retourne une fonction qui retourne
    // la valeur lue depuis le canal
    return func() string {
        return &amp;lt;-c
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et on va l&amp;rsquo;utiliser de cette manière:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-{.souceCode&#34;&gt;g := Loop()
fmt.Println(g())
fmt.Println(g())
fmt.Println(g())
fmt.Println(g())
fmt.Println(g())
//...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez tester, ça fonctionne très bien. Chaque appel à &amp;ldquo;&lt;code&gt;g()&lt;/code&gt;&amp;rdquo; débloque le canal, la goroutine interne insert alors la prochaine clef du map dans le canal. Si on atteind la fin de la liste, on recommence.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;exemple se trouve ici: &lt;a href=&#34;http://play.golang.org/p/a_xC4l4NoL&#34;&gt;http://play.golang.org/p/a_xC4l4NoL&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Une question vous tourmente, je le sais. Pouquoi je retourne pas le canal ? Effectivement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;// generateur
func Loop() &amp;lt;-chan string {
    // le canal qui prend les valeurs
    c:=make(chan string)
    
    // on lance ça en tache de fond
    go func(){
        for {
            for k, v := range items {
                c &amp;lt;- k
            }
        }
    }()
    
    // on retourne le canal
    return c
}

// plus loin

c := Loop()
fmt.Println(&amp;lt;-c)
fmt.Println(&amp;lt;-c)
fmt.Println(&amp;lt;-c)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ça marche aussi très bien ! &lt;strong&gt;MAIS !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Si jamais un jour je me rend compte que j&amp;rsquo;ai besoin de changer la valeur lue, ou qu&amp;rsquo;un opération doit être effectué avant que mon programme lise effectivement la prochaine entrée, je suis coincé. Alors qu&amp;rsquo;avec un generator je sais que je peux manipuler la fonction retournée. Par exemple, si je veux écrire un log lors de la lecture:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func Loop() func() string {
    // le canal qui prend les valeurs
    c:=make(chan string)
    
    // on lance ça en tache de fond
    go func(){
        for {
            for k, v := range items {
                c &amp;lt;- k
            }
        }
    }()
    
    // on retourne une fonction qui retourne
    // la valeur lue depuis le canal
    return func() string {
        // je décide de faire un truc avant
        log.Println(&amp;quot;Je fais un truc avant&amp;quot;)
        return &amp;lt;-c
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas possible avec l&amp;rsquo;exemple qui retourne le canal. Oui, on pourrait le faire dans la boucle qui écrit la valeur dans le canal, mais dans ce cas le &amp;ldquo;truc fait avant&amp;rdquo; est effectué quand la valeur est insérée dans le canal, et non pas quand on la lit. La différence peut être très importante.&lt;/p&gt;
&lt;p&gt;Bref, je vous invite à utiliser les générateurs quand vous le pouvez. C&amp;rsquo;est un pattern puissant qui trouve sa place en Go, en Python, mais aussi en Javascript&amp;hellip;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Introduction à Angular2</title>
      <link>https://www.metal3d.org/blog/2016/introduction-%C3%A0-angular2/</link>
      <pubDate>Sun, 06 Mar 2016 15:13:12 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/introduction-%C3%A0-angular2/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Angular2 est en phase &amp;ldquo;beta&amp;rdquo; depuis maintenant quelques semaines et je pense qu&amp;rsquo;il est temps de parler de mon expérimentation sur le sujet. Je ne vais pas vous proposer une documentation mais plutôt un genre de &amp;ldquo;howto&amp;rdquo; de mon point de vue.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-af96ab9d-3975-49af-8f9f-35ad4c260b19.png&#34; alt=&#34;Angular 2 - From binding&#34;&gt;&lt;/p&gt;
&lt;p&gt;Je ne vais pas vous faire un manuel ou un &amp;ldquo;cookbook&amp;rdquo;, mais juste un retour sur mon expérience qui vous permettra de démarrer sans trop de douleurs avec Angular 2.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Attention, je vais beaucoup vulgariser, résumer et il se peut que je dérive de la réalité. Donc, amis experts de Angular 2, ne tenez pas compte de ces raccourcis, et amis débutants, ne prenez pas tout comme parole d&amp;rsquo;évangile.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Sachez avant tout que j&amp;rsquo;utilise Docker pour éviter de saloper mon système avec un paquet de dépendances node, et l&amp;rsquo;image est récupérable via:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker pull metal3d/ng
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tout est expliqué &lt;a href=&#34;https://hub.docker.com/r/metal3d/ng/&#34;&gt;sur la page de mon image docker&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;angular2&#34;&gt;Angular2&lt;/h1&gt;
&lt;p&gt;J&amp;rsquo;ai aujourd&amp;rsquo;hui pas mal d&amp;rsquo;expérience sur Angular 1. J&amp;rsquo;apprécie énormément les principes qu&amp;rsquo;a apporté ce framework. D&amp;rsquo;abord parce que ce n&amp;rsquo;est pas une &amp;ldquo;librairie&amp;rdquo; en soit, qu&amp;rsquo;il impose une certaine manière de faire et que l&amp;rsquo;injection de dépendances permet de ne pas faire du code noué.&lt;/p&gt;
&lt;p&gt;Passer à Angular2 a été douloureux pendant les premières heures. Car au lieu de faire une évolution, les développeurs de Angular ont littéralement changé le framework. Ça peut surprendre au premier regard, et le fait de passer à un autre langage (typescript) a une tendance à faire tomber dans les pâmes (&lt;a href=&#34;http://www.directmatin.fr/patrimoine/2016-02-04/pourquoi-dit-tomber-dans-les-pommes-701804&#34;&gt;et non pas dans les pommes&lt;/a&gt;) certaines personnes.&lt;/p&gt;
&lt;p&gt;Typescript n&amp;rsquo;est pas le seul langage que l&amp;rsquo;on peut utiliser. En fait, on peut toujours utiliser javascript. Mais je ne vous le conseille pas. Il existe aussi Dart, mais je n&amp;rsquo;ai pas encore assez de recul sur le langage pour en parler. On va donc parler essentiellement de Typescript.&lt;/p&gt;
&lt;h1 id=&#34;les-décorateurs&#34;&gt;Les décorateurs&lt;/h1&gt;
&lt;p&gt;C&amp;rsquo;est un concept très connu en Java, en Python, en C#, etc. Mais absolument inconnu en Javascript (bien qu&amp;rsquo;il existe des manières de faire). Un décorateur est un pattern et une notation qui permet de modifier un comportement ou des attributs d&amp;rsquo;un élément du code (classe, méthode, propriété) et ce via une fonction décoratrice.&lt;/p&gt;
&lt;p&gt;Comme dans la plupart des langages, Typescript utilise la notation &amp;ldquo;&lt;code&gt;@Foo&lt;/code&gt;&amp;rdquo; où &amp;ldquo;Foo&amp;rdquo; est le décorateur.&lt;/p&gt;
&lt;p&gt;Exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;@Foo
class Bar {

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, &amp;ldquo;Bar&amp;rdquo; est décoré par &amp;ldquo;Foo&amp;rdquo;. En fait le décorateur précède simplement l&amp;rsquo;élément à décorer.&lt;/p&gt;
&lt;p&gt;Je ne vais pas vous expliquer comment créer un décorateur, je tenais juste à préciser qu&amp;rsquo;ils existent et qu&amp;rsquo;ils sont fortement utilisés sur Angular2. Il ne faut retenir qu&amp;rsquo;une chose: un décorateur modifie un type pour lui attribuer des comportements. Et ce sera le cas pour transformer une classe en Component, en Injectable, etc.&lt;/p&gt;
&lt;p&gt;Une dernière chose, il est possible d&amp;rsquo;utiliser plusieurs décorateurs en multipliant les annotations:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;@Foo
@Bar
class Baz {
    // classs décorée par Foo et Bar

    @admin
    delete(){
        // ... méthode décorée par @admin
        // par exemple pour interdire son utilisation
        // à moins d&#39;être loggué en tant qu&#39;administrateur
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;component&#34;&gt;Component&lt;/h1&gt;
&lt;p&gt;Si vous comprenez ce qu&amp;rsquo;est un component, vous avez déjà compris 50% du fonctionnement de Angular 2. Je ne plaisante pas, c&amp;rsquo;est clairement la notion à comprendre.&lt;/p&gt;
&lt;p&gt;Pratiquement tout ce que vous allez développer en Angular2 sera des &amp;ldquo;Components&amp;rdquo;. Vous pouvez traduire ça en &amp;ldquo;Composant&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Angular2 bénéficie de la possibilité en Typescript d&amp;rsquo;utiliser des décorateurs (annotations), et c&amp;rsquo;est de cette manière qu&amp;rsquo;une classe devient un &amp;ldquo;Component&amp;rdquo;. Pour résumer, vous aurez ce genre de code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;import {Component} from &#39;angular2/core&#39;;

@Component({
    // des trucs ici
})
export class MaClasse {
    // et ici le contenu de la classe
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Car effectivement, en Typescript, on utilise des classes, des vraies.&lt;/p&gt;
&lt;p&gt;Mais que propose un Component ?&lt;/p&gt;
&lt;p&gt;Et bien sachez qu&amp;rsquo;ils existaient en Angular 1, la preuve &lt;a href=&#34;https://docs.angularjs.org/guide/component&#34;&gt;ici&lt;/a&gt; - un component est un type de directive très simple qui se configure d&amp;rsquo;une traite. En Angular2 c&amp;rsquo;est devenu le composant de base.&lt;/p&gt;
&lt;p&gt;En gros, pour résumer (c&amp;rsquo;est le but de l&amp;rsquo;article hein), un Component défini une directive, son controlleur, les styles, la vue&amp;hellip; et voilà.&lt;/p&gt;
&lt;p&gt;Pour vous donner un exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;@Component({
    selector: &#39;mycomponent&#39;,
    template: &#39;&amp;lt;div&amp;gt;...&amp;lt;/div&amp;gt;&#39;,
    // ou
    templateurl: &#39;app/components/mycomponent/view.html&#39;
})
export class AnExample{}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;les-routes&#34;&gt;Les routes&lt;/h1&gt;
&lt;p&gt;Ce qui m&amp;rsquo;a dérouté (sans jeu de mot) c&amp;rsquo;est le fonctionnement des routes. Je ne dis pas que c&amp;rsquo;est compliqué, mais je me suis fait avoir par le client angular&amp;hellip;&lt;/p&gt;
&lt;p&gt;En effet, &amp;ldquo;angular-cli&amp;rdquo; permet de créer des routes en tapant:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;ng generate route example
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or, une route, du point de vue du client angular, est une &amp;ldquo;jeu d&amp;rsquo;urls&amp;rdquo;, c&amp;rsquo;est à dire qu&amp;rsquo;il propose en gros le principe d&amp;rsquo;état de &amp;ldquo;ui.router&amp;rdquo; dans Angular 1.&lt;/p&gt;
&lt;p&gt;Et honnêtement, nettoyer le code généré par angular-cli est tout bonnement infâme. Je peux paraitre peut-être méchant là, mais je vous assure que la première fois c&amp;rsquo;est loin d&amp;rsquo;être marrant.&lt;/p&gt;
&lt;p&gt;Ce que j&amp;rsquo;ai compris c&amp;rsquo;est qu&amp;rsquo;il ne faut pas générer des routes pour tout et n&amp;rsquo;importe quoi.&lt;/p&gt;
&lt;p&gt;Pour résumer, générer une route équivaut à générer un chemin qui aura des descendants, par exemple &amp;ldquo;&lt;code&gt;/heroes&lt;/code&gt;&amp;rsquo; pour lister les héros, puis &amp;ldquo;&lt;code&gt;/heroes/:id&lt;/code&gt;&amp;rdquo; pour voir le détail d&amp;rsquo;un héro en particulier&lt;/p&gt;
&lt;p&gt;Le client génère un Component &amp;ldquo;racine&amp;rdquo; qui va permettre de router plusieurs uri vers des components. Donc effectivement, pour vous &amp;ldquo;montrer un exemple&amp;rdquo;, le client Angular va générer pas mal de choses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un Component racine (XxxRoot)&lt;/li&gt;
&lt;li&gt;un Component liste (XxxList)&lt;/li&gt;
&lt;li&gt;une classe de service pour lister des &amp;ldquo;items&amp;rdquo;&lt;/li&gt;
&lt;li&gt;deux chemins, un pour la racine, et un pour la liste (formulaire qui modifie les items du service)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, je vous préviens, c&amp;rsquo;est une liste de fichiers générés qu&amp;rsquo;il faut aller manipuler, nettoyer, etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ce qui m&amp;rsquo;a posé problème, c&amp;rsquo;est de savoir &lt;strong&gt;comment faire pour que que l&amp;rsquo;url &amp;ldquo;&lt;code&gt;/heroes&lt;/code&gt;&amp;rdquo; pointe sur le composant HeroesRoot&lt;/strong&gt;. Et bien c&amp;rsquo;est vraiment tout con.&lt;/p&gt;
&lt;p&gt;Dans le code du projet (project.ts) il faut:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;importer HeroesRoot&lt;/li&gt;
&lt;li&gt;ajouter la route dans le décorateur RouteConfig&lt;/li&gt;
&lt;li&gt;et bha c&amp;rsquo;est tout hein.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Petite subtilité, puisque que HeroesRoot est un &amp;ldquo;router&amp;rdquo; qui permet de faire descendre des sous-routes, il faut suffixer la route par &amp;ldquo;&amp;hellip;&amp;rdquo;. Pour une route vers un component simple, ce n&amp;rsquo;est pas le cas. Voyez l&amp;rsquo;exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;import {RouteConfig, ROUTER_DIRECTIVES} from &#39;angular2/router&#39;;
import {HeroesRoot} from &#39;./heroes/heroes-root.component&#39;;
import {Hello} from &#39;./components/hello/hello&#39;;
//...

@Component({//...
})
@RouteConfig([
    // Cas d&#39;une route &amp;quot;heroes&amp;quot; générée avec
    // $ ng generate route heroes
    // Le chemin fini par &amp;quot;...&amp;quot; ce qui signifie qu&#39;il y aura des &amp;quot;sous-éléments&amp;quot;
    {path: &#39;/heroes/...&#39;, component: HeroesRoot, name: &#39;Heroes&#39;, useAsDefault: true},

    // Cas d&#39;une url vers un component généré avec
    // $ ng generate component hello
    {path: &#39;/hello&#39;, component: Hello, name: &#39;Hello&#39;}
])
export class Project {//...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;form&#34;&gt;Form&lt;/h1&gt;
&lt;p&gt;Les formulaires avec Angular 2 et surtout le &amp;ldquo;binding&amp;rdquo; de model est quant à lui, pour ma part, un bonheur sans nom. Exit l&amp;rsquo;utilisation de &amp;ldquo;$scope&amp;rdquo;, tout se passe coté classe et html sans aucune peine.&lt;/p&gt;
&lt;p&gt;Prenons un exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;
@Component({
    template: `
    &amp;lt;form (submit)=&amp;quot;onSubmit()&amp;quot;&amp;gt;
        &amp;lt;input type=&amp;quot;text&amp;quot; [(ngModel)]=&amp;quot;user.login&amp;quot; /&amp;gt;
        &amp;lt;input type=&amp;quot;password&amp;quot; [(ngModel)]=&amp;quot;user.password&amp;quot; /&amp;gt;
        &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;OK&amp;quot; /&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;pre&amp;gt;
    {{ user | json }}
    &amp;lt;/pre&amp;gt;
    `
})
export class User{
    user = {
        login: &amp;quot;foo&amp;quot;,
        password: &amp;quot;bar&amp;quot;
    }

    onSubmit(){
        console.log(&amp;quot;USER:&amp;quot;, this.user);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On décrypte.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord, le template. Vous avez remarqué le &amp;ldquo;&lt;code&gt;(submit)&lt;/code&gt;&amp;rdquo; ? Angular propose une notation de template déroutante de prime abord, mais aggréable quand on la comprend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;&lt;code&gt;[X]=&amp;quot;Y&amp;quot;&lt;/code&gt;&amp;rdquo; permet de changer l&amp;rsquo;attribut &amp;ldquo;X&amp;rdquo; de l&amp;rsquo;élément avec la valeur &amp;ldquo;Y&amp;rdquo; du Component.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;&lt;code&gt;(X)=&amp;quot;Y&amp;quot;&lt;/code&gt;&amp;rdquo; permet de binder Y sur l&amp;rsquo;évènement &amp;ldquo;X&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, ici, quand le formulaire est soumis, l&amp;rsquo;évènement &amp;ldquo;submit&amp;rdquo; appelera la méthode &amp;ldquo;onSubmit&amp;rdquo; de mon Component.&lt;/p&gt;
&lt;p&gt;Mélangeons les deux bindings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;&lt;code&gt;[(X)]=Y&lt;/code&gt;&amp;rdquo; va donc naturellement faire un &amp;ldquo;two way data binding&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et on le voit bien avec &amp;ldquo;ngModel&amp;rdquo; (directive Angular 2) qui va modifier la propriété &amp;ldquo;user&amp;rdquo; de mon component, et inversement si le component modifie la propriété &amp;ldquo;user&amp;rdquo; alors la vue sera notifiée.&lt;/p&gt;
&lt;p&gt;Car, oui, Angular 2 soulage fortement la notion de &amp;ldquo;scope&amp;rdquo; en le supprimant purement et simplement. La vue est directement connecté à la classe lors de la décoration &amp;ldquo;Component&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Angular 2 me paraissait super abstrait à ce sujet (il y a encore peu d&amp;rsquo;article sur le net qui parle de Angular 2), mais à force de tester j&amp;rsquo;ai vite compris &lt;strong&gt;qu&amp;rsquo;il ne faut pas chercher midi à quatorze heure&lt;/strong&gt;. Faire au plus simple, et aggrémenter après.&lt;/p&gt;
&lt;p&gt;Reste le dernier point que je veux traiter dans cet article, le requêtage HTTP.&lt;/p&gt;
&lt;h1 id=&#34;http&#34;&gt;Http&lt;/h1&gt;
&lt;p&gt;Honnêtement, là, j&amp;rsquo;ai passé des plombes à lire et relire des didacticiels tous plus ou moins compliqué pour finalement me rendre compte que tout est super simple. Ce que je n&amp;rsquo;avais tout simplement pas assimilé à la base, c&amp;rsquo;est la notation &lt;strong&gt;de lambda&lt;/strong&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Pourtant, j&amp;rsquo;en ai bouffé de la lambda en Python. Mais &lt;em&gt;connement&lt;/em&gt; je n&amp;rsquo;ai pas pris en compte un concept important: Typescript est compatible ES6. Et bha ouais&amp;hellip; Quand on ne prend pas ça en compte, tous les exemples que vous lirez vous rendrons la vie dure. Et je n&amp;rsquo;avais pas compris que la notation que j&amp;rsquo;avais sous les yeux était une notation de lambda.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Mais c&amp;rsquo;est quoi une lambda ?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est tout simplement une &amp;ldquo;fonction sans corps&amp;rdquo;. Oui je résume. En gros, avant, au temps de Javascript ES5, vous aviez un lot de &amp;ldquo;callback&amp;rdquo; du genre:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;obj.onSubmit(function(data){
    obj.result = data.name
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En Typescript (et ES6), vous pouvez faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;obj.onSubmit( data =&amp;gt; this.result = data);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui revient exactement au même !&lt;/p&gt;
&lt;p&gt;Là où ça devient intéressant, c&amp;rsquo;est que vous pouvez désormais avoir une notation plus claire pour réagir à des évènements, et c&amp;rsquo;est le cas pour la classe Http proposé par Angular.&lt;/p&gt;
&lt;p&gt;Voici un exemple de Component qui embarque la vue et l&amp;rsquo;appel à une API -  bien que je vous conseille de séparer l&amp;rsquo;appel dans un service:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;import {Component} from &#39;angular2/core&amp;quot;,
import {Http, Response} from &#39;angular2/http&#39;;

@Component({
    template: `
        &amp;lt;form (submit)=&amp;quot;onSubmit&amp;quot;&amp;gt;
            &amp;lt;input type=&amp;quot;text&amp;quot; [(ngModel)]=&amp;quot;model.data&amp;quot; /&amp;gt;
            &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Send&amp;quot; /&amp;gt;
        &amp;lt;/form&amp;gt;
    `
})
export class Example{
    // le model dans le formulaire
    model = {data:&amp;quot;&amp;quot;}

    // resultat
    res = &amp;quot;&amp;quot;

    // constructor, ici on crée 
    // une propriété &amp;quot;this.http&amp;quot; de type Http (injectée)
    constructor(private http: Http){}

    // lors d&#39;un submit de formulaire
    onSubmit(){
        // on utilise this.http, on appelle l&#39;url &amp;quot;/api/url&amp;quot;
        this.http.post(&amp;quot;/api/url&amp;quot;, JSON.stringify(model))
            .subscribe(res =&amp;gt; this.res = res.json())
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La méthode &amp;ldquo;subscribe&amp;rdquo; attend une fonction (en premièr argument) qui prend en argument les données retournées par l&amp;rsquo;appel. Au lieu de faire une fonction, j&amp;rsquo;utilise une &amp;ldquo;lambda&amp;rdquo; qui assigne à &amp;ldquo;this.res&amp;rdquo; le résultat décodé. L&amp;rsquo;équivalent serait:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;export class Example{
    // le model dans le formulaire
    model = {data:&amp;quot;&amp;quot;}

    // resultat
    res = &amp;quot;&amp;quot;

    // constructor, ici on crée 
    // une propriété &amp;quot;this.http&amp;quot; de type Http (injectée)
    constructor(private http: Http){}

    // lors d&#39;un submit de formulaire
    onSubmit(){
        // on utilise this.http, on appelle l&#39;url &amp;quot;/api/url&amp;quot;
        this.http.post(&amp;quot;/api/url&amp;quot;, JSON.stringify(model))
            .subscribe(function(res){
                this.res = res.json();
            })
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous n&amp;rsquo;ête pas forcé d&amp;rsquo;utiliser une lambda, il se peut que justement vous ayez besoin d&amp;rsquo;une méthode pour faire effectuer plusieurs opérations. Mais dans le principe, il est souvent très pratique d&amp;rsquo;utiliser une notation lambda.&lt;/p&gt;
&lt;p&gt;Si vous ne voulez pas vous occuper de l&amp;rsquo;argument, ou simplement que la fonction attendue n&amp;rsquo;en utilise pas, vous devez utiliser &amp;ldquo;&lt;code&gt;()&lt;/code&gt;&amp;rdquo; en guise d&amp;rsquo;argument:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;obj.onFoo(() =&amp;gt; console.log(&amp;quot;bar&amp;quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Du moment que vous comprennez qu&amp;rsquo;une lambda est une forme simplifiée de noter une fonction &amp;ldquo;inline&amp;rdquo;, alors tout s&amp;rsquo;éclaire. Ainsi, une manière de faire un formulaire d&amp;rsquo;authentification pourrait utiliser ce genre de code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-typescript&#34;&gt;
export class Auth{
    constructor(private http: Http){}
    auth = {
        login: &amp;quot;&amp;quot;,
        pass : &amp;quot;&amp;quot;
    }

    onSubmit(){
        // subscribe prend en fait 3 arguments:
        // - onsucess
        // - onerror
        // - ondone
        this.http.Post(&amp;quot;/login&amp;quot;, this.auth)
            .subscibe(
                res =&amp;gt; this.onLogged(res.json()),
                err =&amp;gt; this.handleError(err),
                () =&amp;gt; console.log(&amp;quot;loggin done&amp;quot;)
            )
    }

    onLogin(res){
        // res est décodé via la lambda, j&#39;ai bien un objet
        window.localStorage.setItem(&#39;token&#39;, res.token);
        //... etc ...
    }

    handleError(err){
        console.error(err);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;screencast&#34;&gt;Screencast&lt;/h1&gt;
&lt;p&gt;J&amp;rsquo;ai fait rapidement un petit screencast pour vous montrer comment on peut rapidement faire fonctionner Angular 2. J&amp;rsquo;ai réduit le temps d&amp;rsquo;init du projet dans la vidéo dans les premières secondes, en réalité le temps que npm récupère les paquets est assez long (près de 2 minutes&amp;hellip;).&lt;/p&gt;
&lt;p&gt;Et vous verrez aussi que je me plante bien souvent sur les chemins d&amp;rsquo;import ;)&lt;/p&gt;
&lt;video src=&#34;https://www.metal3d.org//statics/captures/angular2.webm&#34; style=&#34;max-width: 100%&#34; preload controls&gt;
Désolé, votre navigateur ne gère pas les vidéo au formal WebM...
&lt;/video&gt;
&lt;h1 id=&#34;petit-bilan&#34;&gt;Petit bilan&lt;/h1&gt;
&lt;p&gt;En toute honnêteté, j&amp;rsquo;ai perdu beaucoup de temps à lire des didactitiels eronnés ou trop vieux (datant de l&amp;rsquo;époque de Angular alpha). J&amp;rsquo;avais pris Angular 2 comme quelque chose de complexe, trop strict et franchement pas adapté. Mais en me penchant un peu, en arrêtant de copier-coller du code d&amp;rsquo;articles qui m&amp;rsquo;apportaient plus de questions que de réponses, j&amp;rsquo;ai compris rapidement que tout était bien plus simple que ça n&amp;rsquo;y parait.&lt;/p&gt;
&lt;p&gt;Evitez de vous perdre dans des notions abstraites telles que les &amp;ldquo;observers&amp;rdquo; et allez-y &amp;ldquo;naturellement&amp;rdquo; pour commencer. Créez vous un composant simple, un truc qui affiche des valeurs contenues dans la classe. Ensuite, créez un formulaire et essayez de binder les valeurs, de réagir au &amp;ldquo;submit&amp;rdquo;. Rapidement les concepts s&amp;rsquo;imbriquent et la lumière apparait.&lt;/p&gt;
&lt;p&gt;Angular 2 propose quelque chose de bien plus orienté &amp;ldquo;développement&amp;rdquo; que ne le faisait sa version 1. Le fait de passer à Typescript rend le projet bien plus structuré et largement moins compliqué à partager dans une équipe.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Développer avec Angular 2, Vim et Docker</title>
      <link>https://www.metal3d.org/blog/2016/d%C3%A9velopper-avec-angular-2-vim-et-docker/</link>
      <pubDate>Tue, 01 Mar 2016 12:18:33 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/d%C3%A9velopper-avec-angular-2-vim-et-docker/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Angular 2 commence à faire des émules. C&amp;rsquo;est un fait. Mais passer de la version 1 à la version 2 est un peu déroutant. Bref, j&amp;rsquo;ai aujourd&amp;rsquo;hui quelques outils qui me permettent de bosser efficacement avec Angular 2 sur Vim avec en plus une petite image Docker qui peut vous rendre service.&lt;/p&gt;
&lt;p&gt;Passer de Angular 1 à 2, c&amp;rsquo;est comme passer de la voiture à la moto. On a bien un moteur, on roule sur des routes, on se déplace&amp;hellip; Mais la conduite est considérablement différente.&lt;/p&gt;
&lt;p&gt;Clairement, Angular 2 n&amp;rsquo;a quasiment rien à voir à avec la version 1. D&amp;rsquo;abord dans l&amp;rsquo;approche, puisqu&amp;rsquo;on a plus de contrôlleur, plus de &amp;ldquo;$scope&amp;rdquo;, etc. Mais aussi parce que le langage utilisé n&amp;rsquo;est plus Javascript. Pour être plus précis, on peut utiliser Javascript mais il semble que la majeure partie des développeurs passent à la version &amp;ldquo;TypeScript&amp;rdquo; qui propose une syntaxe plus poussée en termes de POO. Car Typescript est un langage qui propose une définition de classe avec encapsulation.&lt;/p&gt;
&lt;p&gt;Mais nous n&amp;rsquo;allons pas parler de cela aujourd&amp;rsquo;hui, je veux vous parler essentiellement des outils que j&amp;rsquo;ai installés sur mon poste pour tester Angular 2 avec Vim.&lt;/p&gt;
&lt;p&gt;On va donc passer par deux étapes, la première est la configuration de vim pour avoir l&amp;rsquo;auto-completion typescript, et la seconde vous montrera comment je démarre un projet Angular 2 (avec une image docker, et oui&amp;hellip; On se refait pas)&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-187a4d28-68d6-447c-9e27-0927e1c870c0.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;installation-des-outils&#34;&gt;Installation des outils&lt;/h1&gt;
&lt;h2 id=&#34;typescript&#34;&gt;Typescript&lt;/h2&gt;
&lt;p&gt;Malheureusement, il faut installer &lt;strong&gt;globalement&lt;/strong&gt; le transpiler &amp;ldquo;typescript&amp;rdquo;. J&amp;rsquo;ai cherché à m&amp;rsquo;abstraire de cette contrainte sans succès.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est assez facile, mais bon, ça installera le module dans votre système&amp;hellip;&lt;/p&gt;
&lt;p&gt;Rien de grave hein, c&amp;rsquo;est juste que je n&amp;rsquo;aime pas trop qu&amp;rsquo;un outil tiers manipule mon installation Linux si ce n&amp;rsquo;est pas un RPM (ou un .deb pour les debian/like)&lt;/p&gt;
&lt;p&gt;Sans paniquer, vous pouvez y aller:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo npm install -g typescript
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;était pas compliqué.&lt;/p&gt;
&lt;h2 id=&#34;plugins-vim&#34;&gt;Plugins Vim&lt;/h2&gt;
&lt;p&gt;Je travaille sur &amp;ldquo;neovim&amp;rdquo;, mais si vous avez &amp;ldquo;vim&amp;rdquo; traditionnel cela devrait passer sans souci. Personnellement j&amp;rsquo;utilise &amp;ldquo;Vundle&amp;rdquo; pour installer mes plugins vim, c&amp;rsquo;est plus rapide et plus maintenable que Pathogen selon moi.&lt;/p&gt;
&lt;p&gt;Donc, dans votre &amp;ldquo;~/.vimrc&amp;rdquo; vous allez ajouter ces lignes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-vim&#34;&gt;
&amp;quot; Mainly for typescript
Plugin &#39;Shougo/vimproc.vim&#39;
Plugin &#39;Quramy/tsuquyomi&#39;
Plugin &#39;leafgarland/typescript-vim&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sauvez votre fichier et fermez vim, puis ouvrez vim et tapez:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:PluginInstall
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quand c&amp;rsquo;est terminé, fermez vim.&lt;/p&gt;
&lt;p&gt;Si vous n&amp;rsquo;utilisez pas &amp;ldquo;Vundle&amp;rdquo;, vous devrez alors suivre les instructions des pages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Shougo/vimproc.vim&#34;&gt;https://github.com/Shougo/vimproc.vim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Quramy/tsuquyomi&#34;&gt;https://github.com/Quramy/tsuquyomi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/leafgarland/typescript-vim&#34;&gt;https://github.com/leafgarland/typescript-vim&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bref, admettons que vous ayez enfin installé les plugins.&lt;/p&gt;
&lt;p&gt;Maintenant allez dans le répertoire &amp;ldquo;&lt;code&gt;~/.vim/bundle/vimproc.vim/&lt;/code&gt;&amp;rdquo; et tapez la commande &amp;ldquo;&lt;code&gt;make&lt;/code&gt;&amp;rdquo;. Sans cela, rien ne fonctionnera.&lt;/p&gt;
&lt;p&gt;Le troisième plugin ajoute la coloration syntaxique.&lt;/p&gt;
&lt;p&gt;Voilà, on a terminé&amp;hellip; Si si je vous assure !&lt;/p&gt;
&lt;h1 id=&#34;créer-un-projet&#34;&gt;Créer un projet&lt;/h1&gt;
&lt;p&gt;Plusieurs solutions s&amp;rsquo;offrent à vous. La première est d&amp;rsquo;utiliser l&amp;rsquo;outil officiel &lt;a href=&#34;https://github.com/angular/angular-cli&#34;&gt;angular-cli&lt;/a&gt; en l&amp;rsquo;installant avec &amp;ldquo;npm&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Personnellement je n&amp;rsquo;aime pas cette méthode, pour des raisons déjà expliqué plus tôt: je n&amp;rsquo;aime pas installer &amp;ldquo;globallement&amp;rdquo; des choses sur mon système.&lt;/p&gt;
&lt;p&gt;Vous pourriez utiliser nvm aussi pour installer tout ça dans votre HOME, mais encore une fois j&amp;rsquo;ai un peu de mal avec cette méthode. Car si vous partagez votre projet avec une équipe, je suis prêt à parier qu&amp;rsquo;à un moment ou un autre vous allez oublier une dépendance dans votre &amp;ldquo;package.json&amp;rdquo; tout simplement parce que la librairie est accessible sur votre poste, et installée depuis un autre projet.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai eut le cas&amp;hellip; Donc personnellement je vais passer par une image Docker (vous vous y attendiez si vous me connaissez bien).&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;image que je vous propose est ici: &lt;a href=&#34;http://hub.docker.com/r/metal3d/ng&#34;&gt;metal3d/ng&lt;/a&gt;. Vous pouvez attraper cette image:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker pull metal3d/ng
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Créons donc un projet:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir ~/Projects/angular2-test
cd ~/Projects/angular2-test
docker run --rm -it      \
    -v $(pwd):/project   \
    -u $(id -u):$(id -g) \
    metal3d/ng new myproject
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela crée le projet &amp;ldquo;myproject&amp;rdquo; dans votre répertoire.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attention, à partir de là il va y avoir un piège !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le répertoire de &amp;ldquo;projet&amp;rdquo; est désormais &amp;ldquo;&lt;code&gt;~/Project/angular2-test/myproject&lt;/code&gt;&amp;rdquo; !&lt;/p&gt;
&lt;p&gt;Donc, maintenant, vous devez vous déplacer dans ce répertoire pour faire fonctionner votre application, ou changer le volume dans la ligne de commande.&lt;/p&gt;
&lt;p&gt;Vous avez donc deux solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vous décidez que votre projet se trouve dans le répertoire &amp;ldquo;myproject&amp;rdquo; et donc vous avez simplement à vous déplacer dans ce repertoire pour lancer les commande docker&lt;/li&gt;
&lt;li&gt;vous décidez que &amp;ldquo;myproject&amp;rdquo; est un sous-repertoire de votre projet principal, il faut adapter le répertoire dans les commandes docker en ce qui concerne le volume&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A partir de là, j&amp;rsquo;estime que vous avez choisi la première solution, à savoir que le répertoire &amp;ldquo;myproject&amp;rdquo; est le répertoire de projet. Vous pouvez donc taper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;cd myproject
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour gagner en confort, je vais créer deux fichiers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un docker-compose.yml pour lancer le projet en mode &amp;ldquo;service&amp;rdquo;&lt;/li&gt;
&lt;li&gt;un Makefile pour éviter de taper de longues commandes docker pour créer des fichiers de projet&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Allons-y&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;un-peu-de-confort&#34;&gt;Un peu de confort&lt;/h1&gt;
&lt;p&gt;Nous somme donc désormais dans le répertoire &amp;ldquo;~/Projects/angular2-test/myproject&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Mon fichier docker-compose.yml ressemble à ceci (remplacez bien 1000:1000 par vos uid et gid):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;version: &#39;2&#39;
services:
    app:
        image: metal3d/ng
        user: 1000:1000
        command: serve
        ports:
        - 4200:4200
        - 49152:49152
        volumes:
        - ./:/project
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le service s&amp;rsquo;appelle &amp;ldquo;app&amp;rdquo;, je vais l&amp;rsquo;utiliser dans un Makefile.&lt;/p&gt;
&lt;p&gt;Voilà donc le fichier &amp;ldquo;Makefile&amp;rdquo; que j&amp;rsquo;utilise:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;.PHONY: ng
DIR:=$(PWD)
APPNAME:=app
UID:=$(shell id -u)
GID:=$(shell id -g)

run:
    docker-compose up

ng:
ifndef DO 
    @echo &#39;You must provide a &amp;quot;DO&amp;quot; parameter&#39;
    @echo &#39;Example: make ng DO=&amp;quot;generate component foo&amp;quot;&#39;
    exit 1
endif
    docker-compose run --rm $(APPNAME) $(DO)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comme ça, je pourrais taper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ make ng DO=&amp;quot;g component foo&amp;quot;
installing component
  create src/app/components/foo/foo.css
  create src/app/components/foo/foo.html
  create src/app/components/foo/foo.ts
installing component-test
  create src/app/components/foo/foo.spec.ts

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et donc, en tapant &amp;ldquo;&lt;code&gt;make&lt;/code&gt;&amp;rdquo; ou en tapant &amp;ldquo;&lt;code&gt;docker-compose up&lt;/code&gt;&amp;rdquo;, j&amp;rsquo;ai bien un service angular qui fonctionne.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app_1 | Could not find watchman, falling back to NodeWatcher for file system events.
app_1 | Visit http://www.ember-cli.com/user-guide/#watchman for more info.
app_1 | Livereload server on http://localhost:49152
app_1 | Serving on http://localhost:4200/
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Je ne suis pas encore entré dans les détails de Angular 2. Je suis encore en cours d&amp;rsquo;apprentissage. Mais cette image docker, créée en quelques minutes, et les fichiers &amp;ldquo;Makefile&amp;rdquo; et &amp;ldquo;docker-compose.yml&amp;rdquo; que je vous ai montré accélèrent vraiment le boulot.&lt;/p&gt;
&lt;p&gt;Encore une fois, vous pouvez vous passer de Docker ici, mais je pense honnêtement que si vous travaillez en équipe alors l&amp;rsquo;utilisation d&amp;rsquo;une image qui propose les outils et le serveur &amp;ldquo;dev&amp;rdquo; évitera pas mal de dérapages.&lt;/p&gt;
&lt;p&gt;Vim, configuré comme je vous l&amp;rsquo;ai montré, me donne une bonne completion de code et des informations utiles ainsi qu&amp;rsquo;une coloration propre. Le confort de travail est suffisant pour moi.&lt;/p&gt;
&lt;p&gt;En espérant que tout ça vous aide.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>JWT en Go</title>
      <link>https://www.metal3d.org/blog/2016/jwt-en-go/</link>
      <pubDate>Sun, 28 Feb 2016 13:34:48 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/jwt-en-go/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Vous avez envie de gérer l&amp;rsquo;autorisation de vos API REST en Go avec JWT ? Personnellement c&amp;rsquo;est mon cas. Alors voilà comment on s&amp;rsquo;y prend.&lt;/p&gt;
&lt;p&gt;JWT (JSON Web Token) est fortement utilisé depuis plusieurs années maintenant. Il permet de signer une clef et de permettre l&amp;rsquo;autorisation d&amp;rsquo;un utilisateur dans des échanges client-serveur. Il est intégré au protocole OAuth2 et bénéficie d&amp;rsquo;une RFC.&lt;/p&gt;
&lt;p&gt;Pour faire simple, c&amp;rsquo;est un peu comme gérer des sessions&amp;hellip; Mais sans session.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hein ? Keskidi ?&lt;/p&gt;
&lt;p&gt;&amp;ndash; un lecteur bourré&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Bon pour faire simple, sans aller dans le détail de OAuth2 que nous n&amp;rsquo;allons pas utiliser, voilà en gros le déroulé:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un utilisateur se connecte sur le serveur en envoyant un login et mot de passe&lt;/li&gt;
&lt;li&gt;le serveur authentifie (ou non) l&amp;rsquo;utilisateur&lt;/li&gt;
&lt;li&gt;le serveur crée alors un &amp;ldquo;token&amp;rdquo; signé et crypté qu&amp;rsquo;il retourne à l&amp;rsquo;utilisateur&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et après cela, &lt;strong&gt;à chaque requête de l&amp;rsquo;utilisateur&lt;/strong&gt;, le client doit envoyer ce token pour être reconnu par le serveur.&lt;/p&gt;
&lt;p&gt;Mais comment le serveur sait que ce token est bien au format attendu ? C&amp;rsquo;est ultra simple ! Le token est en fait une combinaison d&amp;rsquo;information dont un JSON qui contient des informations reconnues par le serveur (et lui seul, à moins de donner la clef secrète à d&amp;rsquo;autres).&lt;/p&gt;
&lt;p&gt;Donc, quand le serveur reçoit une requête, il décrypte le token avec une clef secrète (qui a servi à encrypter le token) et il vérifie des informations. Dans ces informations il y a généralement au moins une date d&amp;rsquo;expiration. Si cette date est dépassée, le client va devoir redemander un token.&lt;/p&gt;
&lt;p&gt;Bref, c&amp;rsquo;est assez bien fichu. C&amp;rsquo;est pas non plus infaillible (il parait), mais ça a le mérite de faire le job attendu, et ça permet surtout d&amp;rsquo;éviter au serveur de retenir des sessions. Du coup, on peut utiliser le même token sur une application &amp;ldquo;scallée&amp;rdquo;, à condition de partager la clef avec tous les noeuds afin qu&amp;rsquo;ils puissent tous authentifier l&amp;rsquo;utilisateur.&lt;/p&gt;
&lt;p&gt;En visitant la page &lt;a href=&#34;https://jwt.io/&#34;&gt;https://jwt.io/&lt;/a&gt; j&amp;rsquo;ai découvert que le package &amp;ldquo;jwt-go&amp;rdquo; proposé est celui-ci: &lt;a href=&#34;https://github.com/dgrijalva/jwt-go&#34;&gt;https://github.com/dgrijalva/jwt-go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Voyons donc comment il marche.&lt;/p&gt;
&lt;h1 id=&#34;exemple-simple-en-go&#34;&gt;Exemple Simple en Go&lt;/h1&gt;
&lt;p&gt;On va juste faire un petit test, on commence par installer le package:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go get -u github.com/dgrijalva/jwt-go
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et on se fait un petit programme pour voir. On crée un répetoire &amp;ldquo;&lt;code&gt;~/jwt-test&lt;/code&gt;&amp;rdquo; et on crée le fichier &amp;ldquo;&lt;code&gt;~/jwt-test/main.go&lt;/code&gt;&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
    &amp;quot;log&amp;quot;
    &amp;quot;time&amp;quot;
    &amp;quot;github.com/dgrijalva/jwt-go&amp;quot;
)

var SECRET = []byte(&amp;quot;Une phrase longue pour encrypter/décrypter le token&amp;quot;)

func main(){
    token := jwt.New(jwt.SigningMethodHS256)
    // dans Claims, on peut mettre ce qu&#39;on veut
    token.Claims[&amp;quot;id&amp;quot;] = 123456

    // la clef &amp;quot;exp&amp;quot; est là par défaut, on peut la modifier
    // c&#39;est la date d&#39;expiration du token
    token.Claims[&amp;quot;exp&amp;quot;] = time.Now().Add(time.Hour * 24)

    // on récupère la chaine
    jwtstring, err := token.SignedString(SECRET)
    if err != nil {
        log.Fatal(err)
    }
    // voilà la clef
    log.Println(jwtstring)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette clef peut donc être retournée au client. Il va la garder dans un coin et l&amp;rsquo;envoyer dans une entête ou en argument pour faire des requêtes. Le serveur doit donc maintenant être capable de décrypter le token. Voilà comment faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;// je vous laisse faire la fonction &amp;quot;getHeader&amp;quot;
jwtstring := getHeader(&amp;quot;Authorization&amp;quot;)

// on décrypte
// je vais revenir sur la fonction utilisée pour récupérer le secret
token, err := jwt.Parse(jwtstring, func(token *jwt.Token)(interface{}, error){
    return SECRET, nil
})

if !token.Valid {
    log.Fatal(&amp;quot;Et bha non...&amp;quot;, err)
}

log.Println(&amp;quot;id utilisateur: &amp;quot;, token.Claims[&amp;quot;id&amp;quot;])

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A quoi sert la fonction utilisée dans &amp;ldquo;Parse()&amp;rdquo; ? C&amp;rsquo;est assez simple, vous pouvez créer une fonction qui, selon les entêtes, la requête, le type d&amp;rsquo;utilisateur, retournera une clef spécifique. C&amp;rsquo;est loin d&amp;rsquo;être idiot. Mais dans notre cas nous n&amp;rsquo;allons pas faire compliqué et retourner la variable &amp;ldquo;SECRET&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Le miracle opère, vous avez l&amp;rsquo;id utilisateur. Puisque le token est valide, c&amp;rsquo;est à dire que le serveur a réussi à le décrypter et que la date &amp;ldquo;exp&amp;rdquo; n&amp;rsquo;est pas dépassée, et bien vous pouvez être certain que l&amp;rsquo;utilisateur est le bon.&lt;/p&gt;
&lt;p&gt;À vous de choisir quoi mettre dans le token, ici nous avons intégré l&amp;rsquo;identifiant du compte, mais vous pouvez ajouter son email, son login, sa pointure de chaussure&amp;hellip; Bref, ce qui vous intéresse. Certains s&amp;rsquo;amusent à foutre tout l&amp;rsquo;objet utilisateur à l&amp;rsquo;intérieur. C&amp;rsquo;est bourri hein&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;jaime-les-auteurs-de-package-&#34;&gt;J&amp;rsquo;aime les auteurs de package !&lt;/h1&gt;
&lt;p&gt;Franchement, en Go, je ne sais pas pourquoi, mais je trouve que les auteurs de package s&amp;rsquo;arrachent vraiment pour rendre la vie facile aux développeurs. Là, par exemple, l&amp;rsquo;auteur du package jwt-go a pensé à mettre une méthode &amp;ldquo;&lt;a href=&#34;https://godoc.org/github.com/dgrijalva/jwt-go#ParseFromRequest&#34;&gt;ParseFromRequest&lt;/a&gt;&amp;rdquo; qui fonctionne super bien.&lt;/p&gt;
&lt;p&gt;Plutôt que de chercher vous-même l&amp;rsquo;entête ou la variable postée par le client, la fonction le fait pour vous:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;// une méthode qui demande une authentification
func GetSomething(w http.ResponseWriter, req *http.Request){

    // on décrypte
    token, err := jwt.ParseFromRequest(req, func(token *jwt.Token)(interface{}, error){
        return SECRET, nil
    })

    if !token.Valid {
        http.Error(w, &amp;quot;Problème avec ton token&amp;quot;, http.StatusUnauthorized);
        return
    }

    w.Write([]byte(&amp;quot;Yes !&amp;quot;))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour faire simple, la méthode cherche l&amp;rsquo;entête &amp;ldquo;Authorization&amp;rdquo; ou la valeur POST &amp;ldquo;&lt;code&gt;access_token&lt;/code&gt;&amp;rdquo;. Vous voyez, c&amp;rsquo;est pas compliqué.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;JWT est une notion essentielle de nos jours, il faut dire que les API REST sont à la mode et que la scallabilité n&amp;rsquo;aide pas la gestion de session. Donc, JWT est une bonne réponse à cette problématique et elle a le mérite d&amp;rsquo;être assez sûre.&lt;/p&gt;
&lt;p&gt;En Go, c&amp;rsquo;est finalement pas si compliqué.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Ubuntu et Debian, pourquoi je les évite</title>
      <link>https://www.metal3d.org/blog/2016/ubuntu-et-debian-pourquoi-je-les-%C3%A9vite/</link>
      <pubDate>Tue, 23 Feb 2016 12:47:14 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/ubuntu-et-debian-pourquoi-je-les-%C3%A9vite/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Debian et Ubuntu sont aujourd&amp;rsquo;hui des distributions extrêmement bien positionnées et disons-le, incontournables. Mais personnellement, je rechigne depuis des années à utiliser ces deux distributions pour des raisons que je vais vous exposer.&lt;/p&gt;
&lt;p&gt;Oui je sais, ça peut ressembler à un troll vu de loin. Mais je vous demande de ranger les armes et de me lire dans une optique de réflexion et non d&amp;rsquo;une agression. Car le fond de ma pensée n&amp;rsquo;est pas si négative qu&amp;rsquo;elle peut paraitre, je vais donc préciser deux choses:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Debian est une excellente distribution basée sur un modèle de communauté que j&amp;rsquo;apprécie énormément.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et pour la seconde:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Je ne remets pas du tout en question que Ubuntu ait permis une forte augmentation de l&amp;rsquo;utilisation de Linux par d&amp;rsquo;autres personnes que des professionnels.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ce serait malhonnête de ma part de dire le contraire. Sauf que depuis tant d&amp;rsquo;années que je travaille dans le développement, et ayant utilisé quotidiennement ces distributions (que ce soit en poste de travail ou en serveur), j&amp;rsquo;ai un avis assez tranché sur la question.&lt;/p&gt;
&lt;p&gt;On va commencer par Debian et finir par Ubuntu.&lt;/p&gt;
&lt;h1 id=&#34;debian&#34;&gt;Debian&lt;/h1&gt;
&lt;p&gt;Debian est une distribution gérée par une communauté de passionnés bénévoles. Sur ce point, elle se démarque de Ubuntu ou RedHat. Par contre, elle utilise le même principe que CentOS ou Fedora.&lt;/p&gt;
&lt;p&gt;En réalité, c&amp;rsquo;est CentOS qui est véritablement sur la même ligne de front. Car je fais partie de ceux qui pensent que Debian n&amp;rsquo;est pas une distribution adéquate pour le Desktop, tout comme CentOS. Ce sont des distributions que je trouve franchement plus portées &amp;ldquo;serveur&amp;rdquo;. Avis personnel, oui.&lt;/p&gt;
&lt;p&gt;Debian utilise un logiciel de gestion de paquets nommé &amp;ldquo;apt&amp;rdquo;, dont un outil plus facile et bénéficiant d&amp;rsquo;une interface utilisateur en console nommé &amp;ldquo;aptitude&amp;rdquo; peut vraiment soulager le non spécialiste.&lt;/p&gt;
&lt;p&gt;En bref, Debian est une distribution très complète et ce n&amp;rsquo;est pas par hasard si elle est fortement utilisée coté serveur.&lt;/p&gt;
&lt;p&gt;Alors pourquoi je ne l&amp;rsquo;aime pas ? C&amp;rsquo;est pas que je ne l&amp;rsquo;aime pas, c&amp;rsquo;est quelle me pose des soucis tant philosophiques que techniques. Je n&amp;rsquo;ai rien contre elle, croyez-moi. Mais laissez-moi exposer ce qui m&amp;rsquo;amène à l&amp;rsquo;éviter.&lt;/p&gt;
&lt;p&gt;Pour certain cela va paraitre dérisoire, mais j&amp;rsquo;estime que cela a un impact très important sur le confort de travail. Debian ne respecte pas certains standards et ne fait pas grand-chose, à mes yeux, pour corriger le tir.&lt;/p&gt;
&lt;p&gt;Je vais parler ici d&amp;rsquo;une tendance à vouloir &amp;ldquo;trop en faire&amp;rdquo;. Parlons des paquets Nginx et Httpd.&lt;/p&gt;
&lt;h2 id=&#34;a-trop-vouloir-en-faire&#34;&gt;A trop vouloir en faire&amp;hellip;&lt;/h2&gt;
&lt;p&gt;En premier lieu, Debian a voulu &amp;ldquo;simplifier le nommage&amp;rdquo; (c&amp;rsquo;est une hypothèse de ma part) et à décidé de renommer Httpd (le service http de apache) en &amp;ldquo;apache&amp;rdquo;. Or&amp;hellip; Apache est une fondation qui propose d&amp;rsquo;autres produits, par exemple Tomcat.&lt;/p&gt;
&lt;p&gt;Apache est donc, si on reste dans une optique de service, un utilisateur qui fournit des services. C&amp;rsquo;est d&amp;rsquo;ailleurs de cette manière que l&amp;rsquo;ensemble des distributions non basées sur une Debian ont défini &amp;ldquo;apache&amp;rdquo;. Il y a un utilisateur nommé comme tel (apache) et des services nommés comme le proposent &amp;ldquo;apache&amp;rdquo;, c&amp;rsquo;est à dire &amp;ldquo;httpd&amp;rdquo; et &amp;ldquo;tomcat&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Debian a décidé d&amp;rsquo;avoir un utilisateur &amp;ldquo;www-data&amp;rdquo; (avec un tiret dans le nom &amp;hellip;) qui en plus de ne pas être clair (data signifiant &amp;ldquo;données&amp;rdquo;, nous avons là un utilisateur qui s&amp;rsquo;appelle &amp;ldquo;données www&amp;rdquo;) est utilisé pour tous les services &amp;ldquo;internet&amp;rdquo;. Sur ce point à la limite, je peux me raviser. Car effectivement un utilisateur standard pour le web n&amp;rsquo;est pas une mauvaise idée en soi. Ce sera le même utilisateur qui pourra toucher les fichiers nginx et apache. Donc en soit, c&amp;rsquo;est une demi mauvaise idée (et donc une demi bonne idée).&lt;/p&gt;
&lt;p&gt;Et le service nommé &amp;ldquo;httpd&amp;rdquo; sur tous les systèmes Unix non Debian devient alors &amp;ldquo;apache&amp;rdquo;. Ce qui fait que nous avons un mot, &amp;ldquo;apache&amp;rdquo; qui désigne un utilisateur sur toutes les distributions sauf Debien, et qui désigne un &amp;ldquo;service&amp;rdquo; sur les distributions Debian&amp;hellip; Et là par contre c&amp;rsquo;est une mauvaise idée de la part de Debian, car il répand un amalagame sur ce qu&amp;rsquo;est &amp;ldquo;apache&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Et ça ne s&amp;rsquo;arrête pas là.&lt;/p&gt;
&lt;p&gt;Debian propose des outils que je trouve, sans ironie, très intelligents. C&amp;rsquo;est &amp;ldquo;a2ensite, a2dissite&amp;rdquo; ou encore &amp;ldquo;ngensite et ngdissite&amp;rdquo;. Ces deux commandes vont activer ou désactiver des &amp;ldquo;sites&amp;rdquo; sur apache2 et nginx.&lt;/p&gt;
&lt;p&gt;En soit l&amp;rsquo;idée est très bonne, je le répète. Cela permet d&amp;rsquo;avoir une série de configurations de site que l&amp;rsquo;on peut activer ou désactiver en une commande.&lt;/p&gt;
&lt;p&gt;Sauf que Debian modifie fortement l&amp;rsquo;arborescence standard des fichiers de configuration (celle proposé par les auteurs des logiciels Httpd et Nginx pour ne citer qu&amp;rsquo;eux). Pour réussir ce tour, Debian supprime le répertoire &amp;ldquo;conf.d&amp;rdquo; où toutes les autres distributions permettent l&amp;rsquo;écriture de configuration personnelle, et crée deux autres répertoires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sites-available où se trouve les sites &amp;ldquo;disponibles&amp;rdquo;&lt;/li&gt;
&lt;li&gt;sites-enabled où un lien symbolique est créé depuis les fichiers du répertoire précédent pour activer un site&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Encore une fois, c&amp;rsquo;est une excellente idée, je ne dis pas le contraire&amp;hellip; Dans le fond. Mais dans la forme cette manipulation de répertoire casse le &amp;ldquo;standard&amp;rdquo;. Et cela induit des erreurs.&lt;/p&gt;
&lt;p&gt;Par exemple, l&amp;rsquo;image Docker officielle &amp;ldquo;nginx&amp;rdquo; s&amp;rsquo;est vu avoir un fonctionnement ambigue. &lt;a href=&#34;https://github.com/nginxinc/docker-nginx/issues/48&#34;&gt;J&amp;rsquo;ai d&amp;rsquo;ailleurs participé au bug report&lt;/a&gt; qui est l&amp;rsquo;exemple type de l&amp;rsquo;ambigüité induite par l&amp;rsquo;utilisation de Debian en &amp;ldquo;base d&amp;rsquo;image&amp;rdquo;. Ici, l&amp;rsquo;auteur utilisant Debian mais installant une version &amp;ldquo;standard&amp;rdquo; de nginx à travers les dépôts des auteurs de Nginx s&amp;rsquo;est retrouvé dans une confusion, et donc des soucis aux utilisateurs qui ont suivi la documentation.&lt;/p&gt;
&lt;p&gt;Cela est bien plus grave que ça n&amp;rsquo;y parait. Docker en soit propose des images pour des services qui, théoriquement, sont &amp;ldquo;distribution agnostic&amp;rdquo; (ce n&amp;rsquo;est pas une règle en soit, mais la logique d&amp;rsquo;utilisation nous permet de le percevoir ainsi). Si les images suivent les principes de Debian, alors le coté agnostique disparait, car les chemins de configuration seraient typique Debian.&lt;/p&gt;
&lt;p&gt;Sauf que Debian n&amp;rsquo;est pas la seule image de base utilisée par les créateurs d&amp;rsquo;image Docker, et par conséquent cela complexifie l&amp;rsquo;utilisation d&amp;rsquo;une image pour y injecter de la configuration.&lt;/p&gt;
&lt;p&gt;En clair, Debian pose un souci.&lt;/p&gt;
&lt;p&gt;Ce qui est étonnant c&amp;rsquo;est que Debian aurait pu trouver une solution qui ne brise pas le standard. Simplement en utilisant deux répertoires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sites-available, ça, ça ne change pas&lt;/li&gt;
&lt;li&gt;conf.d où les liens sont écrits par a2ensite ou ngensite.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ou encore, il leur suffirait de faire un lien symbolique de &amp;ldquo;site-enabled&amp;rdquo; vers &amp;ldquo;conf.d&amp;rdquo;&amp;hellip; Par contre, ce serait un peu plus compliqué pour les images Docker dont les liens symboliques peuvent engendrer des incohérences ou des complexités. Mais en soit, il y avait des moyens de faire en sorte de rester &amp;ldquo;standard&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;De cette manières, que l&amp;rsquo;on écrive une configuration de manière &amp;ldquo;standard&amp;rdquo;, ou en passant par &amp;ldquo;sites-available&amp;rdquo; et un outil de lien automatique, tout fonctionnerait de la même manière.&lt;/p&gt;
&lt;p&gt;Et pour finir, ce qui me choque un peu, c&amp;rsquo;est que seuls les services Web ont cette méthode d&amp;rsquo;écriture de configuration. Les autres services tels que Fail2Ban utilisent, pour le coup, des répertoires &amp;ldquo;conf.d&amp;rdquo; comme toutes les autres distributions. C&amp;rsquo;est comme si Debian imagine que les ingénieurs ou développeurs de site web sont un peu plus bête que les autres et qu&amp;rsquo;ils ont besoin d&amp;rsquo;avoir des répertoires &amp;ldquo;clairement identifiés par leur nom&amp;rdquo; (je suis un peu méchant sur les intentions de Debian, je vous l&amp;rsquo;accorde)&lt;/p&gt;
&lt;p&gt;Mais les outils et l&amp;rsquo;arborescence ne sont pas les seuls problèmes qui me gênent.&lt;/p&gt;
&lt;h2 id=&#34;qualité-et-respect&#34;&gt;Qualité et respect&lt;/h2&gt;
&lt;p&gt;Là je sais que je vais énerver pas mal de fans de Debian. Mais il faut clairement comprendre que si j&amp;rsquo;en suis venu à cette conclusion, c&amp;rsquo;est parce que j&amp;rsquo;ai longuement testé et utilisé Debian&lt;/p&gt;
&lt;p&gt;Notamment, je l&amp;rsquo;utilise encore sur mon serveur (celui-ci, qui propose mon blog) et je vais rapidement devoir réinstaller cette machine pour revenir à ma CentOS que j&amp;rsquo;avais quitté pour une raison peu évidente à la base.&lt;/p&gt;
&lt;p&gt;Mes services que je propose ici (il n&amp;rsquo;y a pas que ce blog), tournent pour 99% d&amp;rsquo;entre eux sur des conteneurs Docker. Un jour j&amp;rsquo;ai décidé de supprimer mes paquets pour réinstaller proprement le service Docker.&lt;/p&gt;
&lt;p&gt;Et j&amp;rsquo;ai été vraiment surpris, et franchement dans la panade, quand je me suis rendu compte que les packageurs Debian avait décidé de supprimer des dépots les paquets Docker. Voir &lt;a href=&#34;https://packages.qa.debian.org/d/docker.io/news/20150331T163915Z.html&#34;&gt;https://packages.qa.debian.org/d/docker.io/news/20150331T163915Z.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mon serveur s&amp;rsquo;est donc retrouvé dans une situation compliquée&amp;hellip; à cause de mainteneur qui, du jour au lendemain, supprime les paquets des dépôts officiels.&lt;/p&gt;
&lt;p&gt;Heureusement que docker.io propose des dépôts et paquets&amp;hellip;&lt;/p&gt;
&lt;p&gt;Bref, ce genre de comportement m&amp;rsquo;exaspère. Et pour le coup l&amp;rsquo;estime que j&amp;rsquo;avais pour Debian est descendu encore d&amp;rsquo;un cran.&lt;/p&gt;
&lt;p&gt;Pour le coup, ma colère va parler là, je trouve ceci complètement irrespectueux pour les utilisateurs de Debian, et j&amp;rsquo;imagine la tête des administrateurs systèmes qui utilisaient Docker à ce moment là lors des mises à jour et/ou d&amp;rsquo;une réinstallation (comme moi). C&amp;rsquo;est vraiment pas pro de la part des packageurs.&lt;/p&gt;
&lt;p&gt;On va maintenant passer à Ubuntu.&lt;/p&gt;
&lt;h1 id=&#34;ubuntu-le-coup-de-grâce&#34;&gt;Ubuntu, le coup de grâce&lt;/h1&gt;
&lt;p&gt;Ubuntu est basée sur Debian, ce qui me pose donc déjà un souci en soit pour les raisons expliquées ci-dessus. Mais Ubuntu est aussi une distribution qui est développée par une entreprise nommée &amp;ldquo;Canonical&amp;rdquo; dont les intentions ne sont pas très claires&amp;hellip; ou plutôt, elles le sont, et elles ne me plaisent pas du tout. Pour résumer, l&amp;rsquo;idée est clairement de &amp;ldquo;devenir une référence en faisant en sorte que les autres distributions ne soient pas compatible avec ce qu&amp;rsquo;elle propose&amp;rdquo; (ça vous rapelle rien ?)&lt;/p&gt;
&lt;p&gt;Donc, amis de Debian, je vous le dis en toute franchise, personnellement je n&amp;rsquo;apprécie pas le coup que vous fait Canonical. Car se baser sur votre boulot et s&amp;rsquo;attirer les éloges en massacrant dans le même temps tout le travail que vous faites, moi je trouve ça limite.&lt;/p&gt;
&lt;p&gt;Bon je l&amp;rsquo;admets, c&amp;rsquo;est le militant pour le logiciel libre qui va parler là.&lt;/p&gt;
&lt;p&gt;En gros, elle se base sur Debian qui lui fourni une base sans dépenser un sou, et elle retourne la communauté en lui faisant croire que seule Ubuntu est super géniale, rend fertile et riche, alors que les autres sont des trucs de vieux geek boutonneux qui recompilent leur kernel au dessert.&lt;/p&gt;
&lt;p&gt;Car oui, quand je lis des avis sur le net, ou que j&amp;rsquo;écoute certaines personnes, la première phrase qui ressort est &amp;ldquo;bha ouais mais Ubuntu c&amp;rsquo;est facile, c&amp;rsquo;est pas comme les autres où tu dois installer à la main en tapant des commandes&amp;rdquo;. Je vous assure que ce que je vous dis est vrai. L&amp;rsquo;image de Ubuntu est &amp;ldquo;Linux pour les humains&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Ouais parce que moi je suis sur Fedora, donc je ne suis pas humain&amp;hellip; Ou alors faut un doctorat pour installer Fedora hein&amp;hellip;&lt;/p&gt;
&lt;p&gt;Mais si ce n&amp;rsquo;était que ça&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ubuntu propose des outils propriétaires par défaut, dépose un &amp;ldquo;market&amp;rdquo; Amazon, et fourni de base des pilotes propriétaires.&lt;/p&gt;
&lt;p&gt;Là déjà je ne suis pas franchement à l&amp;rsquo;aise avec cette politique. D&amp;rsquo;abords pour des raisons clairement techniques: on ne voit pas le contenu des sources des logiciels qui sont &amp;ldquo;imposés&amp;rdquo; et cela va à l&amp;rsquo;encontre de mes convictions. Mais en plus de cela, l&amp;rsquo;image de &amp;ldquo;Linux&amp;rdquo; (intentionnellement, je ne mets pas de GNU devant) proposé à une classe d&amp;rsquo;utilisateurs généralement débutante est tronquée.&lt;/p&gt;
&lt;p&gt;Aux yeux de Canonical, Linux est une base de vente, un peu comme le fait Google avec Android (basé sur Linux). Sauf que Google ne cherche pas à tuer GNU&amp;hellip; Canonical montre tous les signes qu&amp;rsquo;elle cherche clairement à imposer Ubuntu au détriment du travail effectuer par ceux qui lui permettent de se faire de l&amp;rsquo;argent. Je trouve ça malhonnête.&lt;/p&gt;
&lt;p&gt;Il y a peu, un ami, non-informaticien, m&amp;rsquo;a sorti &amp;ldquo;dis donc toi, tu m&amp;rsquo;as dit que Linux c&amp;rsquo;est libre et tout, j&amp;rsquo;ai installé Ubuntu et bha il me demande de payer des logiciels et j&amp;rsquo;ai vu que c&amp;rsquo;était pas du tout libre comme tu me l&amp;rsquo;avais dit&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Effectivement, il est passé par l&amp;rsquo;outil (dont j&amp;rsquo;ai perdu le nom) qui propose des logiciels payants, et dont les licences ne sont pas libres&amp;hellip;&lt;/p&gt;
&lt;p&gt;Pour ceux qui défendent le fait que la connaissance doit être offerte à tous, que le logiciel doit être accessible sans restriction&amp;hellip; voir une boite proposer une distribution qui trahi ces objectifs me fait mal au cœur.&lt;/p&gt;
&lt;p&gt;Sans compter que tous les développeurs qui bossent avec moi et qui ont une Ubuntu dans leurs mains ont généralement des soucis. La faute étant la qualité des paquets proposés par Ubuntu.&lt;/p&gt;
&lt;p&gt;Parfois le binaire est inexistant, parfois le paquet est vide&amp;hellip; Ou encore une erreur de dépendance&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et je ne parle pas des &amp;ldquo;PPA&amp;rdquo;, ces dépôts créés par des utilisateurs, dont la qualité est parfois très limite.&lt;/p&gt;
&lt;p&gt;En gros, cette distribution bénéficie d&amp;rsquo;une communauté très vaste de par son image &amp;ldquo;facile&amp;rdquo;. Mais la qualité n&amp;rsquo;est pas là&amp;hellip; Et je pense franchement que ces utilisateurs &amp;ldquo;Ubuntu&amp;rdquo; devraient tenter de passer à une autre distribution avant de  juger hâtivement de la simplicité.&lt;/p&gt;
&lt;h2 id=&#34;lts---le-piège&#34;&gt;LTS - le piège&lt;/h2&gt;
&lt;p&gt;Le LTS me fait aussi doucement rigoler. Je ne connais pas beaucoup de personnes qui utilise une distribution qui a plus de 2 ans. La plupart (et le mot est faible) essait plutôt de se mettre à jour.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est même un piège ! Car du coup, vous vous trainez une distribution qui n&amp;rsquo;est pas à jour et qui ne reçoit que des patchs de sécurité pendant 5 ans&amp;hellip; Je le répète, &lt;strong&gt;vous n&amp;rsquo;êtes donc pas à jour&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Le LTS est intéressant sur un serveur, et encore&amp;hellip; Tout dépend du serveur. Sur un Desktop, avec des utilisateurs Ubuntu qui aiment en plus jouer, rester sur une version 14.04 est tout simplement étrange.&lt;/p&gt;
&lt;p&gt;Bref, LTS n&amp;rsquo;est, pour moi, et pour beaucoup, absolument pas un argument, et n&amp;rsquo;a rien à voir avec la qualité ! Car on me l&amp;rsquo;a sorti ça&amp;hellip; &amp;ldquo;ouais mais Ubuntu elle est LTS, c&amp;rsquo;est vachement mieux pour la stabilité&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Pardon ???&lt;/p&gt;
&lt;h2 id=&#34;séparation&#34;&gt;Séparation&lt;/h2&gt;
&lt;p&gt;Et dernièrement Ubuntu a clairement décidé de se séparer de la communauté, celle-là même qui lui a permis de vendre et de se répandre. Là où un consensus se porte sur Gnome (pour des raisons qui peuvent ne pas vous plaire) et que l&amp;rsquo;effort de compatibilité est fait pour que KDE ou Xfce reste de bonnes alternatives, Ubuntu nous pond &amp;ldquo;Unity&amp;rdquo;. Déjà le nom est un joli doublon avec le moteur 3D du même nom, mais en plus il a été fortement critiqué pour ses soucis de stabilité et la perte de place qu&amp;rsquo;engendre l&amp;rsquo;installation de base sur le coté de l&amp;rsquo;écran.&lt;/p&gt;
&lt;p&gt;Et pour bien, disons le mot, &amp;ldquo;emmerder les autres&amp;rdquo;, et bien là où enfin on a une prévision de refondre le serveur graphique et que Wayland est franchement une optique intéressante, et bien Ubuntu décide de développer dans son coin un autre serveur nommé &amp;ldquo;Mir&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Le risque est sévère en ce qui concerne la compatibilité. Si en plus vous regardez à quel point les &amp;ldquo;industriels&amp;rdquo; ne se penche que sur cette distribution, malgré toutes les mises en gardes faites (y compris par les développeurs Debian), on peut très vite imager à quel point les communautés Linux vont se séparer. Car si ça se passe mal, Steam ne proposera pas de compatibilité avec les distributions autres que Ubuntu&amp;hellip;&lt;/p&gt;
&lt;p&gt;Pour une distribution qui vient d&amp;rsquo;Afrique du Sud, parler de &amp;ldquo;séparation&amp;rdquo; est limite un &amp;ldquo;point Godwin&amp;rdquo; à atteindre si je me laissais aller&amp;hellip; (pour les personnes intéressées, allez voir la page Wikipedia &lt;a href=&#34;https://fr.wikipedia.org/wiki/Apartheid&#34;&gt;Apartheid&lt;/a&gt; ainsi que la signification de ce mot)&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est déjà compliqué d&amp;rsquo;installer Steam sur autre chose que Ubuntu, je n&amp;rsquo;ose pas imaginer l&amp;rsquo;avenir avec Mir et Wayland qui vont s&amp;rsquo;opposer.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;ailleurs, à ce propos&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;petite-parenthèse&#34;&gt;Petite parenthèse&lt;/h2&gt;
&lt;p&gt;Je commence à en avoir un peu marre de voir des éditeurs de logiciels comme &lt;a href=&#34;https://www.genymotion.com/&#34;&gt;Genymotion&lt;/a&gt; ne proposer que des &amp;ldquo;.deb&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ça leur prendrait à peine plus de temps de proposer un tarball et/ou un RPM, surtout que des outils automatiques permettent de le faire rapidement, comme &lt;a href=&#34;https://github.com/jordansissel/fpm/wiki&#34;&gt;FPM&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Je suis pas fan de Debian et Ubuntu, et pourtant je me débrouille pour proposer des .deb en plus de tarball et RPM, et ça ne m&amp;rsquo;a pas pris plus de 20 minutes pour y arriver. Sérieusement, ça fatigue.&lt;/p&gt;
&lt;p&gt;Donc, moi qui développe sur Fedora, je n&amp;rsquo;ai pas droit à Genymotion. Je les ai contacté, je n&amp;rsquo;ai pas eut de réponse&amp;hellip;&lt;/p&gt;
&lt;p&gt;Pour Steam, il existe des dépôts où des packageurs se sont cassé la tête pour réussir une installation sur autre chose que Ubuntu. Mais c&amp;rsquo;est assez dingue que les &amp;ldquo;.deb&amp;rdquo; soient si répandu, surtout quand on connait les défauts de ce type de paquet en ce qui concerne la construction (sifflotements&amp;hellip;)&lt;/p&gt;
&lt;h2 id=&#34;docker-encore&#34;&gt;Docker, encore&lt;/h2&gt;
&lt;p&gt;Ce qui m&amp;rsquo;a choqué&amp;hellip; C&amp;rsquo;est que des images &lt;strong&gt;officielles&lt;/strong&gt; de services sous Docker se basent sur &amp;ldquo;Ubuntu&amp;rdquo; !&lt;/p&gt;
&lt;p&gt;Déjà, je me suis exprimé sur le fait d&amp;rsquo;utiliser une distribution &amp;ldquo;desktop&amp;rdquo; ou &amp;ldquo;serveur&amp;rdquo; pour créer une image Docker, alors que des distributions bien plus légères sont bien plus adaptées pour cet effet.&lt;/p&gt;
&lt;p&gt;Si encore on utilise CentoOS ou Debian, je peux comprendre. Si l&amp;rsquo;utilisation d&amp;rsquo;une distribution &amp;ldquo;desktop&amp;rdquo; est nécessaire pour des cas particuliers (j&amp;rsquo;ai une image qui utilise Fedora pour une raison particulière) alors ok&amp;hellip;&lt;/p&gt;
&lt;p&gt;Mais la plupart des images que je voyais utilisaient Ubuntu sans aucune raison valable&amp;hellip; Juste un &amp;ldquo;c&amp;rsquo;est une distribution répandue&amp;rdquo; en guise de défense.&lt;/p&gt;
&lt;p&gt;Effectivement, vous devriez écouter du Justin Bieber et apprécier, puisqu&amp;rsquo;il vend des millions de disques&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Honnêtement, je vais me répéter mais&amp;hellip; Debian est une très bonne distribution. Je suis simplement gêné par l&amp;rsquo;irrespect de certains standards qui, je le conçois, ne sont pas des &amp;ldquo;lois&amp;rdquo;&amp;hellip; Mais par souci d&amp;rsquo;homogénéité j&amp;rsquo;aimerai que les packageurs et autres décisionnaires de cette distribution se penchent un peu plus sur ce qui les entoure. Un peu plus d&amp;rsquo;échanges, un peu plus de recherche de respect de standard et que nous soyons plus homogènes.&lt;/p&gt;
&lt;p&gt;Je me fiche de l&amp;rsquo;utilisation ou non de systemd, j&amp;rsquo;attends simplement que les hiérarchies de répertoires et les noms de fichier, de services et d&amp;rsquo;utilisateurs soit basé sur les noms conventionnels. Ceux-là même qui sont définis par les &amp;ldquo;développeur du service&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Par contre, Ubuntu est selon moi une distribution à éviter.&lt;/p&gt;
&lt;p&gt;Une politique que je déteste, des paquets de piètre qualité, un bureau par défaut infâme et une tendance à briser le travail d&amp;rsquo;ouverture et de standardisation avec les acteurs du libre, tout ça me choque. Si Ubuntu fonctionne, c&amp;rsquo;est avant tout grâce à Debian et toutes les autres distributions qui ont essuyé les plâtres, et qui aujourd&amp;rsquo;hui encore travaille sur la qualité d&amp;rsquo;un système d&amp;rsquo;exploitation fiable. Et au lieu de l&amp;rsquo;aider, elle &amp;ldquo;vole&amp;rdquo; la partie intéressante pour se l&amp;rsquo;accaparer et enfin développer des systèmes qui ne seront plus compatibles avec le reste de l&amp;rsquo;univers GNU.&lt;/p&gt;
&lt;p&gt;Il existe tellement d&amp;rsquo;autres distributions plus fiables, plus respectueuses et pourtant &lt;strong&gt;pas plus compliquées à installer&lt;/strong&gt;, que je vous invite à y réfléchir à deux fois avant de faire une bêtise.&lt;/p&gt;
&lt;p&gt;Donc, oui, j&amp;rsquo;apprécie Debian, je suis juste un peu déçu, mais &lt;strong&gt;non, je n&amp;rsquo;aime pas Ubuntu et je la déconseille&lt;/strong&gt; et ce tant qu&amp;rsquo;elle ne sera pas plus qualitative.&lt;/p&gt;
&lt;p&gt;Bon, ça va troller&amp;hellip;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Alpine ou comment faire une image Docker très légère</title>
      <link>https://www.metal3d.org/blog/2016/alpine-ou-comment-faire-une-image-docker-tr%C3%A8s-l%C3%A9g%C3%A8re/</link>
      <pubDate>Mon, 01 Feb 2016 12:25:20 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2016/alpine-ou-comment-faire-une-image-docker-tr%C3%A8s-l%C3%A9g%C3%A8re/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Puisque ces temps-ci je met à profit mes compétences CoreOS/Docker de manière plus soutenue, je me suis dit que quelques articles sur mon blog pour partager quelques astuces pourrait être de bon allois (t&amp;rsquo;as vu !). Alors aujourd&amp;rsquo;hui, je vais parler de &lt;a href=&#34;https://hub.docker.com/_/alpine/&#34;&gt;Alpine&lt;/a&gt;, une distribution pour conteneur très légère et pourtant très fournie.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;un des soucis majeurs avec Docker c&amp;rsquo;est bien l&amp;rsquo;espace nécessaire pour une image. Certes, le fait de partir d&amp;rsquo;une même image de base (debian, fedora, centos&amp;hellip;) ne va pas dupliquer l&amp;rsquo;espace (parce que Docker utilise un système d&amp;rsquo;union fs) mais tout de même&amp;hellip; Quand on se retrouve à vouloir fournir une image qui contient un seul binaire de 1 ou 2 Mo, on a une image de 100Mo voir 200Mo à fournir. Et j&amp;rsquo;exagère à peine.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée est donc de réduire un maximum l&amp;rsquo;espace nécessaire. Il y a, en premier lieu, des astuces simples à connaitre:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tenter de faire un maximum de chose en une seule commande &amp;ldquo;RUN&amp;rdquo;&lt;/li&gt;
&lt;li&gt;supprimer les fichiers inutiles (cache apt, yum&amp;hellip;) très rapidement et surtout &lt;strong&gt;dans la commande qui génère ces fichiers&lt;/strong&gt;; par exemple, supprimer le cache et faire un &amp;ldquo;autoremove&amp;rdquo; juste après un &amp;ldquo;apt-get install&amp;rdquo; ou un &amp;ldquo;dnf install&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mais quoiqu&amp;rsquo;il arrive, une distribution &amp;ldquo;commune&amp;rdquo; telle que debian, fedora, centos, etc. ont la facheuse tendance à installer, de base, pas mal d&amp;rsquo;outils inutiles dans une image Docker. Ce n&amp;rsquo;est pas de leur faute, loin de là, elle ne sont juste pas optimisée pour ça. Ces fichiers sont présents parce que ces distributions sont faite pour &amp;ldquo;booter&amp;rdquo; sur une machine (virtuelle ou physique).&lt;/p&gt;
&lt;h1 id=&#34;busybox---la-base&#34;&gt;Busybox - la base&lt;/h1&gt;
&lt;p&gt;Il existe deux distributions (si on peut les appeler comme ça) faites pour de l&amp;rsquo;embarqué et les images Docker. La première est très légère: &lt;a href=&#34;https://hub.docker.com/_/busybox/&#34;&gt;busybox&lt;/a&gt;. Cette distribution embarque réellement le strict minimum et, à moins que vous ne vouliez embarquer que des binaire statiquement compilés ou ne l&amp;rsquo;utiliser que pour faire un conteneur &amp;ldquo;data only&amp;rdquo;, elle ne vous sera pas très utile. Personnellement je m&amp;rsquo;en sers pour des outils développés en Go qui n&amp;rsquo;ont pas besoin d&amp;rsquo;enregistrement libc particulier, donc sitôt que je fais un binaire qui utilise du réseau, je me retrouve avec un binaire qui ne veut pas s&amp;rsquo;exécuter sur busybox.&lt;/p&gt;
&lt;p&gt;Heureusement pour moi, une autre distribution embarquée existe, basée sur busybox.&lt;/p&gt;
&lt;h1 id=&#34;alpine---la-solution&#34;&gt;Alpine - la solution&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://hub.docker.com/_/alpine/&#34;&gt;Alpine&lt;/a&gt; est une distribution qui n&amp;rsquo;occupe que quelques megaoctets (4 Mo). La différence entre busybox et celle-ci est que Alpine propose un gestionnaire de paquet qui, en plus, propose pas mal d&amp;rsquo;outils paquagés, allant de Mysql et Mongo à Nginx en passant par Java, Go et Python.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;outil se nomme &amp;ldquo;apk&amp;rdquo; et il est assez simple à utiliser. Tentons l&amp;rsquo;expérience.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker run --rm -it alpine sh

/ # python
sh: python: not found

/ # apk add --update python
... 
OK: 51 MiB in 21 packages

/ # python
Python 2.7.11 (default, Jan 23 2016, 12:34:14) 
[GCC 5.3.0] on linux2
Type &amp;quot;help&amp;quot;, &amp;quot;copyright&amp;quot;, &amp;quot;credits&amp;quot; or &amp;quot;license&amp;quot; for more information.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&amp;rsquo;option &amp;ldquo;&amp;ndash;update&amp;rdquo; permet de ne pas passer par deux commandes &amp;ldquo;&lt;code&gt;alpine update; alpine add python&lt;/code&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Donc, dans un Dockerfile, on aura le loisir d&amp;rsquo;installer des outils et de les supprimer si nécessaire.&lt;/p&gt;
&lt;p&gt;Par exemple, si je veux compiler un  binaire de mon projet Go, je n&amp;rsquo;ai qu&amp;rsquo;à faire:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;deposer mes sources dans l&amp;rsquo;image&lt;/li&gt;
&lt;li&gt;installer go&lt;/li&gt;
&lt;li&gt;compiler mes sources&lt;/li&gt;
&lt;li&gt;déplaccer le ou les binaires dans /usr/bin/&lt;/li&gt;
&lt;li&gt;supprimer go (puisque j&amp;rsquo;ai compilé mes binaires)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voilà un exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM alpine:latest
MAINTAINER Patrice FERLET &amp;lt;metal3d@gmail.com&amp;gt;

ADD src/ /opt/src

RUN set -xe;                \
    apk add --update go     \
    cd /opt/src;            \
    go build;               \
    mv macommande /usr/bin; \
    cd /; rm -rf /opt/src;  \
    apk del --purge go;     \
    rm -rf /var/cache/apk/* 

CMD macommande
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour résumer, après compilation, je supprime mes sources (inutile en fait puisque le layer précédent utilis ADD et que les sources sont donc préservées) puis je supprime &amp;ldquo;go&amp;rdquo; (en purgant si nécessaire) et je vire le cache &amp;ldquo;apk&amp;rdquo; qui n&amp;rsquo;a plus d&amp;rsquo;utilité.&lt;/p&gt;
&lt;p&gt;Et le tout se fait en une commande RUN afin de ne pas avoir un layer chargé de fichiers qui seraient préservés. Donc, le principe est finalement le même qu&amp;rsquo;avec n&amp;rsquo;importe quel autre gestionnaire de paquet.&lt;/p&gt;
&lt;p&gt;Par contre, la taille de mon image passe de 120Mo avec une Debian, à 10Mo avec Alpine.&lt;/p&gt;
&lt;h1 id=&#34;bilan&#34;&gt;Bilan&lt;/h1&gt;
&lt;p&gt;Voilà pour ce premier petit article parlant de Alpine. Le prochain va vous présenter CoreOS et quelques méthodes de balancing automatique. Pour l&amp;rsquo;heure, pensez à Alpine si vous voulez créer des images Docker rapidement, allégée, et ce pour des service NodeJS, Go, Java, PHP, Ruby&amp;hellip; car le dépot apk est bien fourni&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Fish shell - adopté</title>
      <link>https://www.metal3d.org/blog/2015/fish-shell---adopt%C3%A9/</link>
      <pubDate>Tue, 08 Dec 2015 12:35:55 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/fish-shell---adopt%C3%A9/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Je pense que beaucoup connaissent déjà le &amp;ldquo;SHELL&amp;rdquo; nommé &lt;a href=&#34;http://fishshell.com/&#34;&gt;Fish&lt;/a&gt;. Je l&amp;rsquo;ai adopté depuis un certain temps et je vais vous donner mes impressions ainsi que ma configuration.&lt;/p&gt;
&lt;p&gt;En premier lieu:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Attention, ne confondez pas un &lt;em&gt;terminal&lt;/em&gt; et un &lt;em&gt;shell&lt;/em&gt;.
Le terminal &lt;em&gt;affiche&lt;/em&gt; le shell.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;http://fishshell.com/&#34;&gt;Fish&lt;/a&gt; est un &amp;ldquo;SHELL&amp;rdquo;, à l&amp;rsquo;instar de bash, sh, ou encore zsh. Ce shell a un certain avantage: il est moderne. Vous allez me dire qu&amp;rsquo;il y a une antiphrase en parlant de &amp;ldquo;shell&amp;rdquo; et de &amp;ldquo;modernité&amp;rdquo; mais trolleurs et mauvaises langues n&amp;rsquo;ont plus beaucoup d&amp;rsquo;impacts sur moi. Travailler sur un terminal est efficace, fiable, rapide et clair, on est dans la modernité. Sauf que, malgré tout l&amp;rsquo;amour que j&amp;rsquo;ai pour bash ou zsh, l&amp;rsquo;interfacage et l&amp;rsquo;interaction n&amp;rsquo;était pas ce que l&amp;rsquo;on peut appeler un modèle du genre. Ce n&amp;rsquo;est pas une critique acerbe, mais je trouve que depuis tant d&amp;rsquo;années ces shells n&amp;rsquo;ont pas franchement bougé en terme d&amp;rsquo;expérience utilisateur.&lt;/p&gt;
&lt;p&gt;Fish est un shell un peu à part, il propose une interaction poussée et un visuel attrayant.&lt;/p&gt;
&lt;p&gt;Il existe depuis plus de 10 ans mais, je ne sais pas pourquoi, il est de plus en plus répandu ces derniers temps. Beaucoup de projets proposent des bindings fish, par exemple Docker qui fourni depuis longtemps les fonctionnalités d&amp;rsquo;auto-completion pour fish.&lt;/p&gt;
&lt;p&gt;Il est donc, à coup sûr, dans les dépots de votre distribution. Donc, un coup de &amp;ldquo;&lt;code&gt;dnf install fish&lt;/code&gt;&amp;rdquo; ou &amp;ldquo;&lt;code&gt;apt-get install fish&lt;/code&gt;&amp;rdquo; doit théoriquement fonctionner. Reste maintenant à le paramétrer.&lt;/p&gt;
&lt;h1 id=&#34;fish-par-défaut&#34;&gt;Fish par défaut&lt;/h1&gt;
&lt;p&gt;Puisqu&amp;rsquo;on est pas des froussards, on va passer utiliser Fish à la place de Bash pour notre utilisateur. C&amp;rsquo;est simple comme chou:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo chsh -s /usr/bin/fish $USER
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez maintenant relancer une session utilisateur:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;su - $USER
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et hop, vous voilà sur fish !&lt;/p&gt;
&lt;p&gt;En général, il faudra redémarrer Gnome pour que cette modification soit complètement prise en compte.&lt;/p&gt;
&lt;p&gt;Si vraiment Fish ne vous plait plus:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo chsh -s /bin/bash $USER
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Allez on passe au paramétrage.&lt;/p&gt;
&lt;h1 id=&#34;première-impression&#34;&gt;Première impression&lt;/h1&gt;
&lt;p&gt;Je vous conseille vivement de taper &amp;ldquo;help&amp;rdquo; dans le terminal. Cette commande va ouvrir un navigateur avec la documentation. Cette documentation est &lt;strong&gt;primoriale&lt;/strong&gt; pour ne pas pêter un câble. Vous y trouverez la liste des commandes spécifiques, des informations importantes, et tout ce dont vous aurez besoin pour faire passer vos anciennes commandes bash en fish.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord le prompt est pas mal, il est simple, ça me va bien. En tapant des commandes je remarque que le shell auto-complète en fonction de mon historique.&lt;/p&gt;
&lt;p&gt;Autre chose interessante, la manière dont Fish complète les options, il suffit de taper &amp;ldquo;&amp;ndash;&amp;rdquo; puis ma touche &amp;ldquo;tabulation&amp;rdquo; et on peut naviguer dans les options. De plus, il donne de la documentation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/e77f5b5e-c282-478a-a1af-c443b26f6ea7-fish-help.png&#34; alt=&#34;Fish, complétion des options et doc&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et ça marche avec les page de &amp;ldquo;man&amp;rdquo;, &amp;ldquo;git&amp;rdquo;, etc&amp;hellip; donc en tapant &amp;ldquo;git&amp;rdquo; suivit de &amp;ldquo;tabulation&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/812f3f51-1065-442a-ae76-127a0fe27bbc-fish-git-comp.png&#34; alt=&#34;Fish, complétion de commande&#34;&gt;&lt;/p&gt;
&lt;p&gt;En bref, je sentais bien, quand j&amp;rsquo;ai commencé à tester ce shell, que ça allait me plaire.&lt;/p&gt;
&lt;h1 id=&#34;oh-my-fish&#34;&gt;Oh My Fish&lt;/h1&gt;
&lt;p&gt;Tout comme zsh, il existe un outil &amp;ldquo;oh my &amp;hellip;&amp;rdquo; qui va mettre un coup de boost à votre shell pour lui fournir des options sympathiques tels que des thèmes. On commence donc par cela.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;curl -L https://github.com/oh-my-fish/oh-my-fish/raw/master/bin/install | fish
omf update
omf install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si tout se passe bien, vous avez maintenant la commande &amp;ldquo;omf&amp;rdquo; et vous allez pouvoir changer de thème, installer des plugins, etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Pour ma part, j&amp;rsquo;utilise le thème &amp;ldquo;cmorrell&amp;rdquo; qui m&amp;rsquo;affiche l&amp;rsquo;état de mes projets git sur la droite, ainsi que le nombre de fichiers modifiés dans le projet. Bien entendu, si je ne suis pas dans un dossier de projet, rien ne s&amp;rsquo;affiche. Donc voilà comment faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# on regarde la liste des thèmes existants
omf theme
# installons cmorrell
omf install cmorrell
# on utilise le thème
omf theme cmorrell
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est tout !&lt;/p&gt;
&lt;h1 id=&#34;configurons&#34;&gt;Configurons&lt;/h1&gt;
&lt;p&gt;Maintenant, on va configurer un peu le prompt (couleur, texte&amp;hellip;) et c&amp;rsquo;est là que j&amp;rsquo;adore fish. Dans le terminal, tapez :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;fish_config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Un navigateur va apparaitre, et vous allez pouvoir changer le thème de couleur, le type de prompt, etc.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;utilise pour ma part le thème de couleurs &amp;ldquo;Lava&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/88702446-dcc0-4b89-8b9f-0615d69a4487-fish1.png&#34; alt=&#34;Thème Lava sur fish&#34;&gt;&lt;/p&gt;
&lt;p&gt;En ce qui concerne le &amp;ldquo;prompt&amp;rdquo;, je passe en mode &amp;ldquo;Minimalist&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/cd0d77cf-b718-4191-898c-956db32cafde-fish2.png&#34; alt=&#34;Prompt minimalist fish&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et voici ce qu&amp;rsquo;il se passe dans un répertoire qui contient un projet Git:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/8733ffbf-75d8-4e50-a977-ca6b328943cb-fish-git.png&#34; alt=&#34;Fish dans un dossier Git&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;encore-un-peu-de-tweak&#34;&gt;Encore un peu de &amp;ldquo;tweak&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;Il nous reste quelques modifications pour aller un peu plus loin. Dans le fichier &amp;ldquo;&lt;code&gt;~/.config/fish/fish.config&lt;/code&gt;&amp;rdquo; vous allez pouvoir modifier vos chemins par défaut, créer des fonctions, des alias, etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Voici ce que je mets:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Path to Oh My Fish install.
set -gx OMF_PATH /home/patachou/.local/share/omf

# Customize Oh My Fish configuration path.
#set -gx OMF_CONFIG /home/patachou/.config/omf

# Load oh-my-fish configuration.
source $OMF_PATH/init.fish

set -x GOROOT ~/go
set -x GOPATH ~/goprojects
set -x PATH $GOROOT/bin $GOPATH/bin/ ~/.local/bin/  $PATH
set -x EDITOR vim

# function to have gnome notification
# FEDORA ONLY !
if test &amp;quot;$VTE_VERSION&amp;quot; -ge 3405
    switch &amp;quot;$TERM&amp;quot;
        case &#39;vte*&#39; &#39;xterm*&#39;
            function __notify_vte_command_completed --on-event fish_postexec --description &#39;Notify VTE of command completion&#39;
                printf &#39;\e]777;notify;Command completed;%s\a&#39; (echo &amp;quot;$argv&amp;quot; | cat --show-nonprinting | tr --delete \;)
            end
    end
end

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[EDIT]: la fonction de notification ne fonctionne que sur Fedora 22 minimum. Désolé.&lt;/p&gt;
&lt;p&gt;Comme vous l&amp;rsquo;avez remarqué, fish n&amp;rsquo;utilise pas la syntaxe Bash, par exemple pour exporter une variable, il faut utiliser &amp;ldquo;set -x&amp;rdquo; et non &amp;ldquo;export&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;La fonction que je place en fin de fichier est expliquée dans mon billet &lt;a href=&#34;https://metal3d.org/ticket/fish-et-les-notifications-de-gnome-terminal&#34;&gt;Fish et les notications Gnome&lt;/a&gt; et ne fonctionne que sur Fedora &amp;gt;= 22.&lt;/p&gt;
&lt;p&gt;À vous d&amp;rsquo;adapter tout ça pour votre propre configuration.&lt;/p&gt;
&lt;h1 id=&#34;les-soucis&#34;&gt;Les soucis&lt;/h1&gt;
&lt;p&gt;Utiliser fish par défaut va vous entrainer quelques soucis mais il n&amp;rsquo;y a rien d&amp;rsquo;insurmontable.&lt;/p&gt;
&lt;h2 id=&#34;mon-ctrlr-&#34;&gt;Mon CTRL+R !!!&lt;/h2&gt;
&lt;p&gt;Ha zut, plus de recherche dans le terminal&amp;hellip; Effectivement en Bash on pouvait utiliser &amp;ldquo;CTRL+R&amp;rdquo; pour rechercher des commandes dans l&amp;rsquo;historique. Fish ne fourni pas ce genre d&amp;rsquo;option, mais &lt;strong&gt;c&amp;rsquo;est normal !!! puisque l&amp;rsquo;autocompletion est native&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Or, moi j&amp;rsquo;aime bien taper le terme à chercher et utiliser CTRL+R. Donc on va utiliser un super outil codé en Go, nommé &lt;a href=&#34;https://github.com/junegunn/fzf&#34;&gt;FZF&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On l&amp;rsquo;installe:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà, terminé ! &lt;strong&gt;Relancez une session fish&lt;/strong&gt; avant de tester, sinon ça ne marchera pas.&lt;/p&gt;
&lt;p&gt;On tente ? Je tape un début de commande et CTRL+R:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/6edba434-2767-4474-bfd3-188fded46690-fzf2.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Je trouve même que cet outil est &lt;em&gt;meilleur&lt;/em&gt; que le CTRL+R de bash&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;syntaxe-modifiée&#34;&gt;Syntaxe modifiée&lt;/h2&gt;
&lt;p&gt;Pas mal de choses changent en Fish:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Ne pas utiliser &amp;ldquo;&amp;amp;&amp;amp;&amp;rdquo; mais &amp;ldquo;; and&amp;rdquo;. C&amp;rsquo;est un peu énervant, je sais, je suis d&amp;rsquo;accord, mais on s&amp;rsquo;y fait vite et c&amp;rsquo;est finalement assez lisible.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;grep &amp;quot;foo&amp;quot; /bar; and echo &amp;quot;Cool&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Les redirections sur STDERR &lt;em&gt;peuvent&lt;/em&gt; être remplacées par un accent circonflexe &amp;ldquo;^&amp;rdquo;. Par conséquent:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;commande &amp;gt;/tmp/out 2&amp;gt;/tmp/err
# équivalent
commande &amp;gt;/tmp/out ^/tmp/err

commande 2&amp;gt;&amp;gt;/tmp/err
# équivalent
commande ^^/tmp/err
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On peut &lt;strong&gt;enfin revenenir à la ligne en tapant une boucle&lt;/strong&gt;, et en plus le shell indente tout seul. Autre chose intéressante, les flèches permettent de se déplacer dans la boucle&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/7bf97682-6110-4547-b501-39bb3eef2664-fish-loop.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et j&amp;rsquo;en passe&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;vim&#34;&gt;Vim&lt;/h2&gt;
&lt;p&gt;Si vous utilisez certains plugins vim, tels que Vundle, les auteurs n&amp;rsquo;ont pas pensé à &amp;ldquo;forcer&amp;rdquo; un shell par défaut. Pour corriger le souci, ouvrez votre &amp;ldquo;vimrc&amp;rdquo; et ajouter, au plus haut possible:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set shell=sh
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;virtualenv&#34;&gt;VirtualEnv&lt;/h2&gt;
&lt;p&gt;Suivez simplement les indications du projet &lt;a href=&#34;https://github.com/adambrenecki/virtualfish&#34;&gt;VirtualFish&lt;/a&gt; et utilisez la commande &amp;ldquo;vf&amp;rdquo; pour créer, activer et désactiver les environnements virtuels python.&lt;/p&gt;
&lt;h1 id=&#34;bilan&#34;&gt;Bilan&lt;/h1&gt;
&lt;p&gt;Que ce soit en termes visuels ou en termes d&amp;rsquo;options et y compris l&amp;rsquo;affichage, les raccourcis claviers etc&amp;hellip; Fish m&amp;rsquo;a franchement convaincu. Certes il faut un temps d&amp;rsquo;adaptation, mais l&amp;rsquo;aide est très bien fichue, l&amp;rsquo;auteur répond rapidement sur GitHub et la communauté s&amp;rsquo;active.&lt;/p&gt;
&lt;p&gt;Difficile pour moi de revenir sur Bash pour mon travail quotidien. J&amp;rsquo;ai adopté le poisson :)&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Let&#39;s encrypt nginx</title>
      <link>https://www.metal3d.org/blog/2015/lets-encrypt-nginx/</link>
      <pubDate>Thu, 03 Dec 2015 21:44:20 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/lets-encrypt-nginx/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;C&amp;rsquo;est enfin le grand jour, &lt;a href=&#34;https://letsencrypt.org/&#34;&gt;Let&amp;rsquo;s encrypt&lt;/a&gt; passe enfin en version &amp;ldquo;beta publique&amp;rdquo;, ce qui signifie qu&amp;rsquo;il n&amp;rsquo;est plus  nécessaire de passer par une phase de demande pour tester ce nouveau service gratuit de certification SSL/TLS. Le petit bémole, c&amp;rsquo;est que pour nginx, le mode est expérimental. Alors voici comment procéder pour avoir un certificat SSL gratuit avec ce superbe outil.&lt;/p&gt;
&lt;h1 id=&#34;tldr&#34;&gt;TL;DR&lt;/h1&gt;
&lt;p&gt;Notez bien: dans cette solution il faut couper le serveur web le temps de lancer le mode &amp;ldquo;standalone&amp;rdquo;, mais il existe d&amp;rsquo;autres méthodes dont une &amp;ldquo;expérimentale&amp;rdquo; pour nginx qui permet de se passer de cette coupure. Je ne l&amp;rsquo;ai pas testé. Il existe aussi une manière de faire en mode &amp;ldquo;manuel&amp;rdquo; où le client vous donne des instructions pour déposer un fichier texte contenant un code qui doit être accessible par les serveurs letsencrypt. Pour ma part, la coupure n&amp;rsquo;était pas un souci majeur, mais pour ceux qui ont la contrainte de ne surtout pas couper le serveur web, je vous invite à &lt;a href=&#34;https://letsencrypt.readthedocs.org/en/latest/index.html&#34;&gt;lire la documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Sur votre serveur Web:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
# cela va paramétrer virtualenv 
# et récuperer les dépendances
./letsencrypt-auto --help

# coupez nginx, c&#39;est nécessaire, désolé
systemctl stop nginx
# ou 
service nginx stop

# puis lancez la procédure de certification
./letstencrypt-auto certonly --standalone \
    --agree-tos \
    --email VOTREMAIL -d VOTREDOMAINE.TLD \
    -d AUTRE...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Les certificats sont maintenant dans &lt;code&gt;/etc/letsencrypt/live/VOTREDOMAINE/...&lt;/code&gt;, vous n&amp;rsquo;avez plus qu&amp;rsquo;à utiliser les certificats &lt;code&gt;fullchain.pem&lt;/code&gt; et &lt;code&gt;privkey1.pem&lt;/code&gt; dans votre configuration nginx.&lt;/p&gt;
&lt;p&gt;EDIT, virer le SSLv3 en activant seulement les protocols TLS, merci à Matthieu&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-nginx&#34;&gt;server {
    //...
    listen 443 ssl;
	ssl on;
	server_name VOTREDOMAINE;
	ssl_certificate /etc/letsencrypt/live/VOTREDOMAINE/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/VOTREDOMAINE/privkey1.pem;
	# stop sslV3 and poodle attack
	# see https://www.linode.com/docs/security/security-patches/disabling-sslv3-for-poodle
	# thanks to Matthieu L.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puis redémarrer nginx:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;service nginx start
# ou pour systemctl
systemctl start nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;explications&#34;&gt;Explications&lt;/h1&gt;
&lt;h2 id=&#34;le-client&#34;&gt;Le client&lt;/h2&gt;
&lt;p&gt;Le client letsencrypt ne gérant pas (pour le moment) la configuration automatique de nginx, à moins d&amp;rsquo;activer le mode expériemental, le mode &amp;ldquo;standalone&amp;rdquo; va lui même faire office de service Web le temps de la certification. C&amp;rsquo;est pour cela qu&amp;rsquo;il faut couper le service nginx le temps de faire la manipulation.&lt;/p&gt;
&lt;p&gt;Donc en appelant &lt;code&gt;letsencrypt-auto --help&lt;/code&gt; &lt;strong&gt;avant&lt;/strong&gt; de couper nginx, je m&amp;rsquo;assure de ne pas couper trop longtemps le serveur avant de lancer la véritable phase de certification. Car le client va installer un certain nombre de dépendances et initialiser un &lt;em&gt;virtualenv&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ensuite, il suffit de couper le serveur web, et appeler la commande:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;letsencrypt-auto certonly --standalone ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si vous ne mettez pas les options &amp;ldquo;&amp;ndash;email&amp;rdquo; et &amp;ldquo;&amp;ndash;agree-tos&amp;rdquo;, alors le client va vous demander d&amp;rsquo;entrer un email, et d&amp;rsquo;accèpter les &amp;ldquo;terms of service&amp;rdquo;. Or, il va s&amp;rsquo;avérer très intéressant d&amp;rsquo;utiliser les options &lt;code&gt;--email&lt;/code&gt; et &lt;code&gt;--agree-tos&lt;/code&gt; pour vous permettre de faire le renouvellement dans 90 jours de manuère automatique (un petit script bash).&lt;/p&gt;
&lt;p&gt;Ensuite suivent les options &amp;ldquo;&lt;code&gt;-d&lt;/code&gt;&amp;rdquo; à la chaine pour chaque domaines et sous domaines. Dans mon cas, j&amp;rsquo;ai certifié:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;metal3d.org&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.metal3d.org&#34;&gt;www.metal3d.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce qui donne:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./letsencrypt-auto --email secret@...com --agree-tos \
    -d www.metal3d.org  \
    -d metal3d.org
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En à peine une seconde la séquence a été effectuée. Les certificats sont générés dans le répertoire &lt;code&gt;/etc/letsencrypt/live/metal3d.org&lt;/code&gt; et je n&amp;rsquo;ai eut qu&amp;rsquo;à modifier la configuration de nginx.&lt;/p&gt;
&lt;h1 id=&#34;et-ça-marche-&#34;&gt;Et ça marche ?&lt;/h1&gt;
&lt;p&gt;Oui ! Regardez par vous-même le &amp;ldquo;https&amp;rdquo; en vert dans la barre d&amp;rsquo;url de mon site.&lt;/p&gt;
&lt;h1 id=&#34;mais-comment-ils-font-ça-&#34;&gt;Mais comment ils font ça ?&lt;/h1&gt;
&lt;p&gt;Let&amp;rsquo;s encrypt est un projet lancé par la &amp;ldquo;linux fundation&amp;rdquo; il y a plus d&amp;rsquo;un an afin de permettre de faire &amp;ldquo;passer le web en mode chiffré pour tous&amp;rdquo; et ce gratuitement. Je dois bien avouer que l&amp;rsquo;offre alléchante m&amp;rsquo;avait donné une impréssion de &amp;ldquo;on va se faire avoir quelque part&amp;rdquo; et pourtant&amp;hellip; il y a 1 mois, j&amp;rsquo;ai accédé à une beta privée qui a fonctionné sur mon service gitlab.&lt;/p&gt;
&lt;p&gt;Comment ils font pour certifier comme ça plus de &lt;strong&gt;11.000 domaines&lt;/strong&gt; lors de la beta privée sans demander un sou ? Et bien tout se passe coté &amp;ldquo;sponsors&amp;rdquo;. Sur la page &lt;a href=&#34;https://letsencrypt.org/sponsors/&#34;&gt;des sponsors&lt;/a&gt; apparaissent Mozilla, Cisco, Akamai et EFF, mais on compte aussi Faceboock, ou encore IdentTrust qui est justement l&amp;rsquo;autorité de confiance qui a permis de faire fonctionner le projet.&lt;/p&gt;
&lt;p&gt;Et aujourd&amp;rsquo;hui, on y est ! Vous allez enfin pouvoir certifier vos serveurs de manière simple, rapide et &lt;strong&gt;gratuite&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Mais où est le vice caché ? et bien il n&amp;rsquo;y en a pas.&lt;/p&gt;
&lt;p&gt;La seule chose que vous devez comprendre c&amp;rsquo;est que &amp;ldquo;let&amp;rsquo;s encrypt&amp;rdquo; permet de certifier votre connexion SSL/TLS et &lt;em&gt;rien de plus&lt;/em&gt;. Enfin &amp;ldquo;&lt;em&gt;rien de plus&lt;/em&gt;&amp;rdquo; est peu dire, car c&amp;rsquo;est déjà &lt;strong&gt;énorme&lt;/strong&gt;, c&amp;rsquo;est ce dont 90% des services ont besoin.&lt;/p&gt;
&lt;p&gt;Seuls les systèmes bancaires ou les services dont les données qui transitent entre clients et serveur sont très sensibles ne vont *&lt;em&gt;certainement pas utiliser letsencrypt&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Donc ne vous y trompez pas, &amp;ldquo;let&amp;rsquo;s encrypt&amp;rdquo; &lt;strong&gt;est un formidable outil&lt;/strong&gt; pour avoir accès à des connexions client/serveur sécurisées (tels qu&amp;rsquo;un site web, un registre docker privé ou des clients particuliers) mais il n&amp;rsquo;est pas conseillé, à mon humble avis, de l&amp;rsquo;utiliser pour sécuriser un service de paiement en ligne.&lt;/p&gt;
&lt;p&gt;Petit edit de Matthieu L., un ex-collègue, dont le commentaire arrive à générer une erreur 500 (?!), le voici:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Après avoir fait tout cela il est préférable d&amp;rsquo;avoir une config SSL correct de son serveur. Pour cela on peu se rendre sur un site de Mozilla :
&lt;a href=&#34;https://mozilla.github.io/server-side-tls/ssl-config-generator/&#34;&gt;https://mozilla.github.io/server-side-tls/ssl-config-generator/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Et après aller sur un autre site pour vérifier que tout est en ordre :
&lt;a href=&#34;https://www.ssllabs.com/ssltest&#34;&gt;https://www.ssllabs.com/ssltest&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Par exemple tu as une note «C» seulement &lt;a href=&#34;https://www.ssllabs.com/ssltest/analyze.html?d=metal3d.org&#34;&gt;https://www.ssllabs.com/ssltest/analyze.html?d=metal3d.org&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Effectivement j&amp;rsquo;ai une note &amp;ldquo;C&amp;rdquo; à cause d&amp;rsquo;une vulnérabilité SSL v3. Je vais mettre en place la configuration générée par l&amp;rsquo;outil Mozilla proposé par Matthieu et voir si ça bouge :) Merci à Matthieu pour ce commentaire !&lt;/p&gt;
&lt;p&gt;Et me voilà en grade B, en virant le sslv3, voir la conf nginx modifiée plus haut.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Atom.io, l&#39;IDE qui me donne presque envie de me passer de vim</title>
      <link>https://www.metal3d.org/blog/2015/atom.io-lide-qui-me-donne-presque-envie-de-me-passer-de-vim/</link>
      <pubDate>Fri, 13 Nov 2015 00:32:28 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/atom.io-lide-qui-me-donne-presque-envie-de-me-passer-de-vim/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Atom.io est un IDE multiplateforme, libre, pratique et modulaire. Bourré de plugins, ergonomique, il entre peu à peu dans mon univers de logiciels à utiliser quotidiennement. À tel point, que je pense à me passer de Vim pour certains projets et utiliser ce dernier. Pouquoi ? bha je vais vous expliquer.&lt;/p&gt;
&lt;p&gt;Depuis 2001 que je fais ce métier, j&amp;rsquo;ai utilisé un bon paquet d&amp;rsquo;IDE (Integrated Development Environment) tels que Eclipse, PyCharm, Netbeans, et autres Anjuta, etc. Chacun de ces IDE m&amp;rsquo;ont laissé sur ma fin, certains m&amp;rsquo;ont énervé, d&amp;rsquo;autres m&amp;rsquo;ont juste déçu, et dans tous les cas je suis revenu à Vim. Vim est certes un &amp;ldquo;editeur&amp;rdquo; d&amp;rsquo;un certain point de vue, archaïque pour certains, mais tellement puissant. Vim me permet de développer efficacement avec des plugins qui enlèvent littéralement la complexité d&amp;rsquo;un tel environnement.&lt;/p&gt;
&lt;p&gt;Auto-completion, gestion de Git, terminal intégré (surtout avec neovim), coloration propre, rapide, et utilisable dans mon terminal. Vous me direz &amp;ldquo;oui mais bon, c&amp;rsquo;est pas à la portée de tous&amp;rdquo;, j&amp;rsquo;en conviens. Cela-dit, en quelques heures, voir quelques jours, Vim s&amp;rsquo;adopte pour la plupart des gens qui s&amp;rsquo;y penchent. En gros, quand on y goûte, on a du mal à passer à un autre outil.&lt;/p&gt;
&lt;p&gt;Sauf que là&amp;hellip; je tombe sur &lt;a href=&#34;http://atom.io&#34;&gt;Atom.io&lt;/a&gt;, et je doute&amp;hellip; oui mesdames et messieurs, je doute !&lt;/p&gt;
&lt;h1 id=&#34;à-première-vue&#34;&gt;À première vue&lt;/h1&gt;
&lt;p&gt;Atom n&amp;rsquo;est pas l&amp;rsquo;unique projet à chercher à convaincre. LightTable et Brackets sont deux exemples très proches de SublimeText qui visent à rendre l&amp;rsquo;environnement de développement &amp;ldquo;agréable&amp;rdquo;. Je les ai testé, sans conviction, et je suis revenu (pour les 3) à Vim après quelques heures. Alors pourquoi Atom.io me convainc un peu plus ?&lt;/p&gt;
&lt;p&gt;Pourtant, Atom ne paye pas de mine. A première vue on dirait un éditeur &amp;ldquo;décoré&amp;rdquo; pour faire &amp;ldquo;pro&amp;rdquo;, qui va nous claquer dans les doigts quand on va chercher à faire de l&amp;rsquo;auto-completion complexe. Par exemple, avec Go, faut utiliser un binaire nommé &amp;ldquo;gocode&amp;rdquo; qui permet d&amp;rsquo;auto-compléter le code. Et je me dis que je vais me marrer quand je vais tenter le coup.&lt;/p&gt;
&lt;p&gt;Mais bon, je suis bon joueur, je suis pas du tout fermé et j&amp;rsquo;aime tester. Donc j&amp;rsquo;installe &amp;ldquo;Atom.io&amp;rdquo; via le RPM fourni, et je lance la bête.&lt;/p&gt;
&lt;h1 id=&#34;test-1-on-lance-allume-le-jouet&#34;&gt;Test 1, on lance allume le jouet&lt;/h1&gt;
&lt;p&gt;Bon sang&amp;hellip; il se lance vite le bougre ! Sérieusement je m&amp;rsquo;attendais à un chargement de plusieurs secondes, il n&amp;rsquo;en est rien. Atom a démarré en 1 seconde, la page de &amp;ldquo;welcome&amp;rdquo; est pas mal, elle explique en quelques lignes que je vais pouvoir m&amp;rsquo;amuser à y placer des plugins, des thèmes, etc.&lt;/p&gt;
&lt;p&gt;Ça me plait bien ça, je peux directement ajouter des &amp;ldquo;snippets&amp;rdquo;, apprendre les racourcis clavier, et l&amp;rsquo;interface est propre, sans trop de fioritures, en bref c&amp;rsquo;est simple et beau.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/8c0e0d0a-22a3-444d-90bb-dff635fac270-atom1.png&#34; alt=&#34;Atom welcom&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;test-2-on-code-&#34;&gt;Test 2, on code !&lt;/h1&gt;
&lt;p&gt;Bon allez j&amp;rsquo;ai du boulot moi! J&amp;rsquo;ouvre un projet Go et je me lance dans le code. Hop un fichier ouvert et&amp;hellip; arrrggg pas de completion, pas de formatage auto de mon code&amp;hellip; Zut et zut.&lt;/p&gt;
&lt;p&gt;Ok, j&amp;rsquo;avoues, pour Vim j&amp;rsquo;ai du installer un plugin &amp;ldquo;vim-go&amp;rdquo;. Donc dans Atom, je vais certainement faire pareil. Allez, sans conviction, je tente. Direction des settings, &amp;ldquo;install&amp;rdquo; et je cherche &amp;ldquo;golang&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Etonnement ! Des plugins existent. Je vérifie les deux qui me paraissent intéressant: &lt;a href=&#34;https://atom.io/packages/go-plus&#34;&gt;go-plus&lt;/a&gt; et &lt;a href=&#34;https://atom.io/packages/go-runtime&#34;&gt;go-runtime&lt;/a&gt;. Mais en lisant la doc du plugin, je découvre un truc: on a deux façons d&amp;rsquo;installer un plugin.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Soit en cliquant sur le bouton &amp;ldquo;installer&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Soit en utilisant la ligne de commande &amp;ldquo;apm&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Allez, je me fais un kiff, je tente les deux :)&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord, &amp;ldquo;go-runtime&amp;rdquo; en cliquant sur &amp;ldquo;install&amp;rdquo;: ça marche.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ouvre un terminal et je tape:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apm install go-plus
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et bha ça marche aussi. Et cerise sur le pompon: pas la peine de redémarrer Atom.&lt;/p&gt;
&lt;p&gt;Voyons voir mon code, j&amp;rsquo;édite mon fichier, je tape le nom d&amp;rsquo;un objet suivi d&amp;rsquo;un point et hop, la completion s&amp;rsquo;active.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/f14eec5f-598d-414e-abf7-fab3f0a34a2e-atom-completion.png&#34; alt=&#34;Atom autocomplete golang&#34;&gt;&lt;/p&gt;
&lt;p&gt;Bien, sérieusement bien !&lt;/p&gt;
&lt;h1 id=&#34;le-racourci-qui-sauve&#34;&gt;Le racourci qui sauve&lt;/h1&gt;
&lt;p&gt;Ce qui parrait toujours compliqué sur Vim, c&amp;rsquo;est l&amp;rsquo;apprentissage des racourics. J&amp;rsquo;ai beau adoré mon vieux copain Vim, je ne peux pas mentir en disant que c&amp;rsquo;est parfois un peu&amp;hellip; allez, disons-le, &amp;ldquo;tordu&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Mais une chose absolument géniale à laquelle ont pensé les auteurs de Atom, c&amp;rsquo;est que quand vous démarrez l&amp;rsquo;IDE &amp;ldquo;à vide&amp;rdquo;, c&amp;rsquo;est-à-dire sans fichier ouvert (ou si vous fermez tous les fichiers), des messages d&amp;rsquo;astuces défilent sur l&amp;rsquo;écran. Et je découvre le racourci &amp;ldquo;CTRL+Maj+P&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est tout bonnement &amp;ldquo;LE&amp;rdquo; racourci à connaitre. Il permet de trouver les fonctionnalités classés par plugin.&lt;/p&gt;
&lt;h1 id=&#34;les-plugins-que-jai-adoré&#34;&gt;Les plugins que j&amp;rsquo;ai adoré&lt;/h1&gt;
&lt;p&gt;Le premier, vous allez dire que je suis franchement intoxiqué, c&amp;rsquo;est &lt;a href=&#34;https://atom.io/packages/vim-mode-plus&#34;&gt;&amp;ldquo;vim-mode-plus&amp;rdquo;&lt;/a&gt; qui me permet d&amp;rsquo;avoir les &amp;ldquo;racourcis&amp;rdquo; de vim. Je ne voyais pas comment me passer du racourcis &amp;ldquo;cit&amp;rdquo; pour changer le texte &amp;ldquo;entre tag&amp;rdquo; d&amp;rsquo;un fichier HTML. Et je préfère me passer des &amp;ldquo;CTRL+C CTRL+V&amp;rdquo;, trouvant &amp;ldquo;dd&amp;rdquo;, &amp;ldquo;p&amp;rdquo; et &amp;ldquo;yy&amp;rdquo; plus accessibles sur mon clavier.&lt;/p&gt;
&lt;p&gt;Oui, je sais, il existe plein de plugins pour simuler Vim dans plein d&amp;rsquo;IDE. Je ne dis pas le contraire, et c&amp;rsquo;est pas ce qui a fait la différence. Le fait est que sans ce plugin, j&amp;rsquo;aurais peut-être fermé Atom après quelques heures. Ce plugin a le mérite d&amp;rsquo;exister.&lt;/p&gt;
&lt;p&gt;Sinon, les plugins git:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://atom.io/packages/git-plus&#34;&gt;git-plus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://atom.io/packages/git-control&#34;&gt;git-control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://atom.io/packages/git-log&#34;&gt;git-log&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://atom.io/packages/merge-conflicts&#34;&gt;merge-conflicts&lt;/a&gt; tout simplement impréssionnant, allez voir le gif animé sur la page pour comprendre pourquoi j&amp;rsquo;adore ce plugin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le plugin pour avoir un terminal de commande:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://atom.io/packages/terminal-plus&#34;&gt;terminal-plus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/d3b1c0bd-0c22-4289-a5d0-51252112bb7b-atom-term.png&#34; alt=&#34;Atom terminal plus&#34;&gt;&lt;/p&gt;
&lt;p&gt;Et comme je vous l&amp;rsquo;ai dit, pour Go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://atom.io/packages/go-runtime&#34;&gt;go-runtime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://atom.io/packages/go-plus&#34;&gt;go-plus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et pour pas péter mon cable, j&amp;rsquo;ai installé&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://atom.io/packages/vim-mode-plus&#34;&gt;vim-mode-plus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il existe énormément de plugins pour vos projets, autocompletion pour Angular, projet Python, etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;La seule chose qui me manque, mais je suis certain qu&amp;rsquo;un jour il va apparaitre, c&amp;rsquo;est le fait de lancer un debugger (Gdb et Pdb). Il existe un plugin pour Gdb mais apparement il n&amp;rsquo;y a pas de &amp;ldquo;watcher&amp;rdquo; (pour voir le contenu de la variable). Donc pour debugger mes programmes en Go, c&amp;rsquo;est pas gagné à ce jour.&lt;/p&gt;
&lt;p&gt;Alors, oui, je l&amp;rsquo;admet sans problème, Atom me donne presque envie de me passer de vim&amp;hellip; Je me tate les amis, je me tate&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;je-me-tate-mais&#34;&gt;Je me tate mais&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Mais voilà&amp;hellip; Je me force à utiliser Atom pour lui donner sa chance, pour par mourir bête et parce que je trouve que les développeurs de cet IDE méritent le respect. Cela-dit, j&amp;rsquo;ai toujours un terminal ouvert dans un coin pour plein de trucs et j&amp;rsquo;ai le réflexe de taper &amp;ldquo;vim&amp;rdquo; pour éditer un code source rapidement.&lt;/p&gt;
&lt;p&gt;Impossible à ce jour de me résoudre à passer &amp;ldquo;tout le temps&amp;rdquo; par Atom. Vim est ancré dans mes habitudes, je connais tous les racourcis utiles à mon travail&amp;hellip;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est d&amp;rsquo;ailleurs un truc qui me travaille sur tous les IDE: les racourcis étranges. Expliquez moi pourquoi je dois faire &amp;ldquo;CTRL+Shift+X&amp;rdquo; pour commiter un fichier ? Quelle est la logique derrière le &amp;ldquo;X&amp;rdquo; ? Vim a pour lui le fait d&amp;rsquo;être plus clair à condition de lire une fois la doc. Exemple, je veux &amp;ldquo;indenter dans le bloc&amp;rdquo;, je fais &amp;ldquo;&amp;gt;iB&amp;rdquo; pour &amp;ldquo;indent Inside Block&amp;rdquo;. Quand on comprend la logique de Vim, ça se retient en un rien de temps. Par contre, dans Atom, sans le racourci &amp;ldquo;CTRL+Shift+P&amp;rdquo;, pas moyen de retenir la séquence de touches proposée qui n&amp;rsquo;a pas de valeur sémantique.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est bête, mais c&amp;rsquo;est une des principales raisons qui gâche le plaisir de passer sur un autre IDE. Vous me direz, je pourrais modifier les bindings, mais y&amp;rsquo;en a tellement.&lt;/p&gt;
&lt;p&gt;Mais je vous le dis, sans honte, Atom me donne bien envie de pousser l&amp;rsquo;expérience plus loin.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Rooter votre Galaxy S6</title>
      <link>https://www.metal3d.org/blog/2015/rooter-votre-galaxy-s6/</link>
      <pubDate>Thu, 12 Nov 2015 11:33:56 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/rooter-votre-galaxy-s6/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Après quelques mois d&amp;rsquo;utilisation de mon Galaxy S6, je me suis retrouvé, comme toujours, dans le besoin de &amp;ldquo;rooter&amp;rdquo; mon téléphone pour utiliser &lt;a href=&#34;https://play.google.com/store/apps/details?id=org.proxydroid&amp;amp;hl=fr&#34;&gt;ProxyDroid&lt;/a&gt; ou avoir des optimisations &lt;a href=&#34;https://play.google.com/store/apps/details?id=eu.thedarken.sdm&amp;amp;hl=fr&#34;&gt;SD Maid&lt;/a&gt;. Jusque-ici, impossible de trouver une méthode qui fonctionne&amp;hellip; mais j&amp;rsquo;ai trouvé! Voici donc comment rooter en quelques minutes (sans exagération) votre Galaxy S6. Cela fonctionne sur un Linux récent, avec le logiciel heimdall.&lt;/p&gt;
&lt;p&gt;Avant toutes choses, le traditionnel message qui va me protéger de tout procès:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Je ne peux être tenu responsable de dégradations subies par votre téléphone en suivant les explications de cet article. En suivant ces explications, vous accéptez de ne pas me tenir responsable des problèmes que vous pourriez rencontrer.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Voilà qui est dit.&lt;/p&gt;
&lt;p&gt;Rooter son téléphone Galaxy S6 se fait en 4 étapes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;activation du mode développeur&lt;/li&gt;
&lt;li&gt;activation du debug USB&lt;/li&gt;
&lt;li&gt;démarrage en mode &amp;ldquo;download&#39;&lt;/li&gt;
&lt;li&gt;injection du &amp;ldquo;boot.img&amp;rdquo; (root)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Toutes les infos ont été trouvées sur &lt;a href=&#34;http://galaxys6root.highonandroid.com/&#34;&gt;ces pages de highonandroid&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;prérequis-importants&#34;&gt;Prérequis importants&lt;/h1&gt;
&lt;p&gt;Cela fonctionne pour les téléphones Galaxy S6 ayant une version de Android 5.1.1 &lt;strong&gt;seulement&lt;/strong&gt;. Si vous avez une autre version de Android, allez voir &lt;a href=&#34;http://galaxys6root.highonandroid.com/galaxy-s6-root/how-to-root-galaxy-s6-or-s6-edge/&#34;&gt;cette page&lt;/a&gt; qui vous indiquera la marche à suivre et ne suivez pas mon article !&lt;/p&gt;
&lt;p&gt;Il faut que vous récupériez le numéro de version de votre téléphone, c&amp;rsquo;est &lt;strong&gt;très important&lt;/strong&gt;. Dans &amp;ldquo;Paramètres&amp;rdquo; » &amp;ldquo;À propos de l&amp;rsquo;appareil&amp;rdquo; vous devez trouver la section &amp;ldquo;Numéros du modèle&amp;rdquo;. Notez le !&lt;/p&gt;
&lt;p&gt;Allez ensuite sur la page &lt;a href=&#34;http://galaxys6root.highonandroid.com/root-kernels/&#34;&gt;ROOT KERNELS [ANDROID 5.1.1]&lt;/a&gt; et téléchargez le fichier qui correspond à votre téléphone.&lt;/p&gt;
&lt;p&gt;Vous allez récupérer un fichier &amp;ldquo;tar&amp;rdquo; qui contient un fichier &amp;ldquo;boot.img&amp;rdquo;, il suffit d&amp;rsquo;extraire ce fichier dans &amp;ldquo;&lt;code&gt;/tmp&lt;/code&gt;&amp;rdquo;. Donc à partir de là, je pars du principe que vous avez un fichier &amp;ldquo;&lt;code&gt;/tmp/boot.img&lt;/code&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Enfin, il vous faut le logiciel &amp;ldquo;heimdall&amp;rdquo;, sur Fedora 22:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo dnf install heimdall
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, tout est prêt. On peut commencer !&lt;/p&gt;
&lt;h1 id=&#34;activer-le-mode-développeur&#34;&gt;Activer le mode &amp;ldquo;développeur&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;Il vous faut ce mode activé pour permettre le &amp;ldquo;debug USB&amp;rdquo;. Sans cela, impossible de pousser l&amp;rsquo;image de boot sur votre téléphone. Ce mode s&amp;rsquo;active simplement.&lt;/p&gt;
&lt;p&gt;Allez dans les paramètres de votre téléphone » &amp;ldquo;À propos du téléphone&amp;rdquo; (en bas)&lt;/p&gt;
&lt;p&gt;Pressez 5 fois sur l&amp;rsquo;entrée &amp;ldquo;Numéro de version&amp;rdquo;, un message va vous indiquer que vous avez activé  le mode &amp;ldquo;développeur&amp;rdquo;.&lt;/p&gt;
&lt;style&gt;
@media all and (min-width: 640px) {
    .mini img {
        max-width: 25% !important;
    }
    .mini.col-50 {
        display: flex;
        flex-direction: row;
        align-items: flex-start;
    }
    .mini.col-50 img {
        flex: 1 1;
    }
}
&lt;/style&gt;
&lt;div class=&#34;mini&#34;&gt;
&lt;img src=&#34;https://www.metal3d.org//static/upload/9528adec-665e-4052-8ae2-8e1bc3710952-settings-red.png&#34; /&gt;
&lt;/div&gt;
&lt;h1 id=&#34;activer-le-mode-usb-debug&#34;&gt;Activer le mode &amp;ldquo;usb debug&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;Sans cette étape, le download de boot.img ne fonctionnera pas, vous aurez un message &amp;ldquo;&lt;code&gt;Protocol initialisation failed&lt;/code&gt;&amp;rdquo;. Si vous l&amp;rsquo;oubliez, rien de grave, il suffira de redémarrer le téléphone et faire la maniuplation suivante, puis revenir en &amp;ldquo;démarrage en mode download&amp;rdquo; expliqué plus bas.&lt;/p&gt;
&lt;p&gt;Dans les paramètres » mode développeur, activez &amp;ldquo;debug usb&amp;rdquo;&lt;/p&gt;
&lt;div class=&#34;mini col-50&#34;&gt;
&lt;img src=&#34;https://www.metal3d.org//static/upload/e4e2bd80-3fd2-4dcc-9123-28dfcc5f3b29-open-dev.png&#34; /&gt;
&lt;img src=&#34;https://www.metal3d.org//static/upload/6602e709-eb1c-42a5-b60a-1bba976a3f60-debug-red.png&#34; /&gt;
&lt;/div&gt;
&lt;h1 id=&#34;démarrage-en-mode-download&#34;&gt;Démarrage en mode &amp;ldquo;download&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;Maintenant, éteignez le téléphone. Quand il est éteind, appuyez, sans relacher les boutons, sur:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;volume bas&lt;/li&gt;
&lt;li&gt;home (le bouton principal du téléphone en dessous de l&amp;rsquo;écran)&lt;/li&gt;
&lt;li&gt;bouton d&amp;rsquo;allumage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le tout, pendant environ 5 secondes.&lt;/p&gt;
&lt;p&gt;Le téléphone se retrouve en mode &amp;ldquo;boot download&amp;rdquo;, le message vous préviens que vous allez modifier l&amp;rsquo;OS, c&amp;rsquo;est ce qu&amp;rsquo;on veut, donc pressez &amp;ldquo;volume haut&amp;rdquo;. Maintenant le téléphone est fin prêt à reçevoir notre nouveau boot root.&lt;/p&gt;
&lt;p&gt;Vous pouvez, si besoin, redémarrer le téléphone en pressant de nouveau les 3 boutons &amp;ldquo;volume bas, home, power&amp;rdquo; si vous avez un souci (par exemple, si vous avez oublié d&amp;rsquo;activer le mode debug usb)&lt;/p&gt;
&lt;h1 id=&#34;injecter-le-bootimg&#34;&gt;Injecter le boot.img&lt;/h1&gt;
&lt;p&gt;Branchez votre téléphone avec un cable USB.&lt;/p&gt;
&lt;p&gt;Dans un terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ heimdall detect
Device detected
$ heimdall flash --BOOT /tmp/boot.img
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le téléphone va alors récupérer le fichier &amp;ldquo;boot.img&amp;rdquo; et redémarrer tout seul. C&amp;rsquo;est normal ! Attendez qu&amp;rsquo;il soit bien reparti et tapez votre code PIN. On passe à la vérification.&lt;/p&gt;
&lt;h1 id=&#34;vérification&#34;&gt;Vérification&lt;/h1&gt;
&lt;p&gt;Normalement, vous devez avoir un nouvelle application dans le menu: &amp;ldquo;SuperSU&amp;rdquo;. L&amp;rsquo;icone doit ressembler à un gros dièse.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/79165077-f4cb-4aca-91d0-8a1b850c7ddf-supersu.png&#34; alt=&#34;Icone de l&amp;rsquo;application SuperSU&#34;&gt;&lt;/p&gt;
&lt;p&gt;Voilà, c&amp;rsquo;est bon, vous avez rooté votre téléphone Galaxy S6.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Optimisons un peu notre Linux en limitant les accès disques</title>
      <link>https://www.metal3d.org/blog/2015/optimisons-un-peu-notre-linux-en-limitant-les-acc%C3%A8s-disques/</link>
      <pubDate>Sun, 01 Nov 2015 17:16:27 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/optimisons-un-peu-notre-linux-en-limitant-les-acc%C3%A8s-disques/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Que vous ayez un SSD ou non, je pense que ce qui va suivre va vous intéresser. Ceux qui ont un SSD vont apprécier le fait de ne pas écrire constamment sur le disque et de tuer son espérance de vie. Et les autres vont aimer le fait d&amp;rsquo;accélerer l&amp;rsquo;utilisation de caches. Rien de révolutionnaire ici, nous allons utiliser des RAMDisks et soulager, si vous en avez, nos SSD. Je vous précise que vous n&amp;rsquo;aurez aucune surprise ici, je vais parler de méthodes vieilles de plusieurs siècles (ok&amp;hellip; de plusieurs années) et qui ne sont absolument pas des hacks ou des nouveautés. Rien que du bon sens !&lt;/p&gt;
&lt;h1 id=&#34;tldr&#34;&gt;TL;DR&lt;/h1&gt;
&lt;p&gt;[EDIT]  03/11/2015 - Une modification importante a été ajoutée suite à différents commentaires sur Google+. Ainsi, je vous propose une partie supplémentaire de &amp;ldquo;sauvegarde de cache&amp;rdquo;&lt;/p&gt;
&lt;p&gt;[EDIT] 03/11/2015 - Bonne remarque dans les commentaires à propos de l&amp;rsquo;utilisation de &amp;ldquo;&lt;code&gt;rsync&lt;/code&gt;&amp;rdquo; dans le cas où vous souhaitez sauvegarder vos caches. L&amp;rsquo;idée est de ne plus supprimer la sauvegarde de cache et de la synchroniser lors de l&amp;rsquo;extinction/démarrage avec le cache. Voir le point 5 et les explication à propos des caches utilisateurs dans l&amp;rsquo;article. Merci à &lt;a href=&#34;http://penthium2.org/&#34;&gt;Frédéric Thouin&lt;/a&gt; auteur de la distribution &lt;a href=&#34;http://viperr.org&#34;&gt;Viperr&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Je vous demande de prendre tout ce qui suit, y compris les explications, avec des pincettes !&lt;/strong&gt; Lisez et comprennez avant de lancer les commandes. Si vous vous loupez, vous pouvez faire planter le redémarrage. &lt;strong&gt;Rien n&amp;rsquo;est irreversible, vous pouvez réparer sans souci&lt;/strong&gt; mais évitez-vous une crise d&amp;rsquo;arrachage de cheveux et ne faites rien sans comprendre les commandes et éditions de fichiers qui suivent.&lt;/p&gt;
&lt;p&gt;Pour résumer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;utilisez des RAMDisk pour vos caches (cache utilisateur, cache dnf, cache packagekit&amp;hellip;) et adaptez la taille max selon votre RAM&lt;/li&gt;
&lt;li&gt;utilisez des services pour mettre en place les caches&lt;/li&gt;
&lt;li&gt;faites un TRIM régulier sur les disques SSD (si vous en avez un)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc rien de spécialement &amp;ldquo;génial&amp;rdquo; mais un minimum qui va soulager votre SSD et utiliser votre RAM pour le cache.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Utiliser un RAMDisk pour /tmp même si les gens de chez Debian sont pas d&amp;rsquo;accord. C&amp;rsquo;est déjà le cas sur Fedora, sinon:&lt;/p&gt;
&lt;p&gt;Vérifiez que tmp n&amp;rsquo;est pas déjà monté en ramdisk:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ mount | grep /tmp
tmpfs on /tmp type tmpfs (rw,seclabel)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notez bien que &amp;ldquo;seclabel&amp;rdquo; est ajouté par SELinux, ne le mettez pas vous-même. Il sera ajouté par SELinux.&lt;/p&gt;
&lt;p&gt;Si le répertoire &amp;ldquo;/tmp&amp;rdquo; nest pas monté, éditez &lt;code&gt;/etc/fstab&lt;/code&gt; et ajoutez la ligne:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;tmpfs       /tmp    tmpfs   rw     0   0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pensez à supprimer le contenu de /tmp AVANT de monter /tmp,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Montez les caches utilisateurs dans un ramdisk&lt;/p&gt;
&lt;p&gt;Fermez toutes les sessions, allez ensuite dans un tty (CTRL+ALT+F1) et connectez-vous en root:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;for c in /home/*; do
    mv $c/.cache $c/oldcache
    echo -e &amp;quot;tmpfs\t$c/.cache\ttmpfs\tnoatime,nodev,nosuid,size=500M\t0\t0&amp;quot; &amp;gt;&amp;gt; /etc/fstab
    mount $c/.cache
    mv $c/oldcache/* $c/.cache
    rm -rf $c/oldcache
done
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Placez les caches &lt;code&gt;dnf&lt;/code&gt; et &lt;code&gt;PackageKit&lt;/code&gt; dans un ramdisk&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl stop PackageKit
rm -rf /var/cache/PackageKit/*
rm -rf /var/cache/dnf/*
echo -e &amp;quot;tmpfs\t/var/cache/PackageKit\ttmpfs\tdefaults,noatime\t0\t0&amp;quot; &amp;gt;&amp;gt; /etc/fstab
echo -e &amp;quot;tmpfs\t/var/cache/dnf\ttmpfs\tdefaults,noatime\t0\t0&amp;quot; &amp;gt;&amp;gt; /etc/fstab
mount /var/cache/PackageKit
mount /var/cache/dnf
systemctl start PackageKit
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Créez un service pour charger le cache dnf quand le réseau est prêt&lt;/p&gt;
&lt;p&gt;Créez le fichier &lt;code&gt;/etc/systemd/system/mydnfcache.service&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;[Unit]
Description=DNF cache creation 
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/dnf makecache

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activez le service pour qu&amp;rsquo;il démarre au prochain redémarrage, et lancez le service:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl enable mydnfcache
systemctl start mydnfcache
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Si vous souhaitez sauver vos caches, par exemple pour PackageKit qui, lors d&amp;rsquo;une mise à jour, va redémarrer et en plus retrouver le cache, vous pouvez faire ceci:&lt;/p&gt;
&lt;p&gt;Créez un script de sauvegarde de cache &lt;code&gt;/usr/local/bin/savecaches.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash

PKGKIT=/var/cache/PackageKit
DNF=/var/cache/dnf

# create backup for PackageKit and dnf
[[ -d  ${PKGKIT}.bkp ]] || mkdir -p ${PKGKIT}.bkp
[[ -d  ${DNF}.bkp ]]    || mkdir -p ${DNF}.bkp

case $1 in

	restore)
		# move backup to real cache
		for c in $PKGKIT $DNF; do 
			rsync -ra --delete-after ${c}.bkp/ ${c}/
		done
		;;

	save)
        # restore backup cache
        for c in $PKGKIT $DNF; do 
            rsync -ra --delete-after ${c}/ ${c}.bkp/
        done
        ;;

esac
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Créez un &amp;ldquo;unit&amp;rdquo; systemd qui démarrera &lt;strong&gt;avant&lt;/strong&gt; PackageKit, &lt;code&gt;/etc/systemd/system/cachesave.service &lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;[Unit]
Description= Save caches at shutdown - reset cache at startup
Before=packagekit.service packagekit-offline-update.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStop=/bin/bash /usr/local/bin/savecaches.sh save
ExecStart=/bin/bash /usr/local/bin/savecaches.sh restore

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puis activez le service&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo systemctl enable cachesave
sudo systemctl start cachesave
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez aussi créer un &amp;ldquo;unit&amp;rdquo; pour vos caches utilisateurs en suivant le même modèle. Voir l&amp;rsquo;article&amp;hellip;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Si vous avez un SSD, vérifiez qu&amp;rsquo;il accèpte la commande &amp;ldquo;TRIM&amp;rdquo; - si oui:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl start fstrim.timer
systemctl enable fstrim.timer
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Voilà, un conseil maintenant: redémarrez votre machine. Tout devrait bien se passer.&lt;/p&gt;
&lt;h1 id=&#34;ok-tu-mexpliques-&#34;&gt;Ok, tu m&amp;rsquo;expliques ?&lt;/h1&gt;
&lt;p&gt;Le TL;DR est conséquent mais je pense que quelques explications peuvent vous aider à comprendre.&lt;/p&gt;
&lt;p&gt;Le but de toutes ces opérations est simple: utiliser un peu plus la RAM que ce qui est prévu par défaut. En effet, Linux (du moins les distributions Linux) a l&amp;rsquo;intérêt de pouvoir tourner sur des petits systèmes avec très peu de RAM. Mais dans la plupart des cas, les postes utilisateurs sont plutôt caustauds.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;entend par là que vous avez certainement pas mal de mémoire RAM (plus de 4Go, certains se balladent avec plus de 32Go de RAM&amp;hellip;) et que celle-ci est finalement pas exploitée complètement. Or, la RAM est très rapide, bien plus que votre HDD traditionnel, et plus rapide que les SSD actuels.&lt;/p&gt;
&lt;p&gt;Donc, si vous avez un poste assez récent, vous pouvez utiliser la RAM comme &amp;ldquo;disque dur temporaire&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est justement ce que nous appelons un RAMDisk.&lt;/p&gt;
&lt;p&gt;Mais utiliser un RAMDisk a une conséquence non négligeable&amp;hellip; il se vide quand la machine est éteinte. Et par conséquent, il va falloir faire quelques réglages.&lt;/p&gt;
&lt;p&gt;Ensuite, les gens ayant un SSD tremblent face à leur durée de vie dite &amp;ldquo;limitée&amp;rdquo;&amp;hellip; utiliser quelques RAMDisk va calmer un peu vos frayeurs.&lt;/p&gt;
&lt;h1 id=&#34;les-ramdisk&#34;&gt;Les RAMDisk&lt;/h1&gt;
&lt;p&gt;Sous Windows, c&amp;rsquo;est pas drôle à faire. Enfin j&amp;rsquo;en sais rien mais de ce que j&amp;rsquo;en ai lu sur le net, il faut installer un logiciel, paramétrer des trucs à la souris&amp;hellip; franchement j&amp;rsquo;ai remercié le ciel d&amp;rsquo;être sous un Unix Like (et pourtant je ne suis pas croyant)&lt;/p&gt;
&lt;p&gt;Un RAMDisk n&amp;rsquo;est pas un disque dur à brancher sur votre système. C&amp;rsquo;est &lt;em&gt;bêtement&lt;/em&gt; un disque temporaire accessible comme un répertoire (comme tout périphérique sous Linux) dont le contenu est écrit en RAM.&lt;/p&gt;
&lt;p&gt;Il y a déjà pas mal de RAMDisk préconfigurés sur votre distribution. Depuis quelques années, le répertoire &lt;code&gt;/dev/shm&lt;/code&gt; (shared memory - mémoire partagée) est justement un répertoire donc le contenu est en RAM. Certains programmes écrivent dedans pour partager des informations sans avoir à écrire sur le disque. C&amp;rsquo;est effectivement très simple d&amp;rsquo;écrire un fichier contenant des informations.&lt;/p&gt;
&lt;p&gt;Le système de montage est &amp;ldquo;tmpfs&amp;rdquo; ou &amp;ldquo;ramfs&amp;rdquo;. La différence entre les deux est simple: si la RAM est saturée, un point de montage TMPFS va &amp;ldquo;swapper&amp;rdquo;, c&amp;rsquo;est-à-dire utiliser votre disque dur temporairement, le temps que de la place se libère. Alors que le RAMFS va misérablement bloquer. C&amp;rsquo;est débile ? &lt;strong&gt;non&lt;/strong&gt;, c&amp;rsquo;est voulu ! RAMFS est surtout utilisé pour des programmes qui ne veulent surtout pas toucher au disque.&lt;/p&gt;
&lt;p&gt;Pour nous, humbles utilisateurs, TMPFS est bien plus sécurisé.&lt;/p&gt;
&lt;p&gt;Si vous voulez tester, créer un répertoire &lt;code&gt;/mnt/testramdisk&lt;/code&gt; et montez un espace tmpfs dessus:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir /mnt/testramdisk
mount -t tmpfs tmpfs /mnt/testramdisk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, si vous écrivez dans ce répertoire, et bien le fichier sera dans la RAM, et il ne sera pas écrit sur votre disque dur. Y accéder est très rapide ! Bien plus que depuis votre disque dur.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Alors attention&lt;/strong&gt;, écrire un fichier de 4Go dans le répertoire, alors que vous avez 2Go de RAM va effectivement déborder ! C&amp;rsquo;est pourquoi on doit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;limiter la taille du ramdisk&lt;/li&gt;
&lt;li&gt;éviter de les utiliser pour tout et n&amp;rsquo;importe quoi si on a peu de RAM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Une option intéressante au point de montage est justement &amp;ldquo;size&amp;rdquo;. Testons:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;umount /mnt/testramdisk
mount -t tmpfs tmpfs /mnt/testramdisk -o defaults,size=1Mo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le disque ne fait que 1Mo, essayez de déposer un fichier de plus de 1Mo et le message est sans appel &amp;ldquo;no space left on device&amp;rdquo;. C&amp;rsquo;est une sécurité nécessaire, croyez-moi.&lt;/p&gt;
&lt;p&gt;Allez, on nettoie et on passe à la suite:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;umount /mnt/testramdisk
rm -rf /mnt/testramdisk
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;montage-des-caches&#34;&gt;Montage des caches&lt;/h1&gt;
&lt;p&gt;Il existe un paquet de &amp;ldquo;caches&amp;rdquo; sur votre installation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les répertoires utilisateurs &amp;ldquo;home&amp;rdquo; ont un répertoire &amp;ldquo;.cache&amp;rdquo; dans lequel écrivent Google Chrome, Firefox, Gnome, votre client mail&amp;hellip;&lt;/li&gt;
&lt;li&gt;des répertoires systèmes pour dnf, packagekit, et j&amp;rsquo;en passe&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Un cache est un espace qui &lt;strong&gt;peut être effacé sans affecter vos données&lt;/strong&gt;. J&amp;rsquo;insiste, si un programme plante quand le cache est vidé, alors ce programme est mal foutu. Personnellement je n&amp;rsquo;en connais pas&amp;hellip; Soit dit en passant, il convient de ne pas vider le cache d&amp;rsquo;un programme qui est en train de l&amp;rsquo;utiliser. Non pas que ce soit grave, mais il sera peut-être nécessaire de le redémarrer pour qu&amp;rsquo;il réinitialise le cache.&lt;/p&gt;
&lt;p&gt;Bref, on a de la RAM, on va faire en sorte que notre utilisateur arrête d&amp;rsquo;écrire sur le disque le cache. J&amp;rsquo;ai vérifié ce que mon utilisateur en cours utilise dans &lt;code&gt;$HOME/.cache&lt;/code&gt;. Je ne dépasse pas les 200Mo par session. Je me prends un marge et j&amp;rsquo;estime que sur mes 8Go de RAM, je peux utiliser 500Mo pour mon utilisateur.&lt;/p&gt;
&lt;p&gt;Donc, j&amp;rsquo;édite &lt;code&gt;/etc/fstab&lt;/code&gt; et je mets:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tmpfs   /home/metal3d/.cache    tmpfs   noatime,nodev,nosuid,size=500M      0       0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Évidemment, vous remplacerez &amp;ldquo;metal3d&amp;rdquo; par votre nom d&amp;rsquo;utilisateur&amp;hellip;&lt;/p&gt;
&lt;p&gt;Les options que j&amp;rsquo;ai utilisé:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;noatime parce que je n&amp;rsquo;utilise pas de logiciel qui se basent sur la date d&amp;rsquo;accès au fichier&lt;/li&gt;
&lt;li&gt;nodev hors de question de mettre des périphériques dans le cache&lt;/li&gt;
&lt;li&gt;nosuid pour ne pas les bits suid - inutile dans le cache&lt;/li&gt;
&lt;li&gt;size=500Mo pour autoriser 500Mo maximum dans le cache&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Ne montez pas le disque de suite&lt;/strong&gt;, il faut vider le répertoire avant de le monter. Car si vous montez le disque de suite, le contenu sera toujours présent sur le disque mais inaccessible - à moins de démonter le cache. Donc au lieu de perdre bêtement cet espace, on va couper la session Gnome (ou KDE, ou XFCE&amp;hellip;) et allez dans un TTY (CTRL+ALT+F1) pour s&amp;rsquo;y connecter en &amp;ldquo;root&amp;rdquo;. Puis on supprime ce cache, et on le monte:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;rm -rf /home/metal3d/.cache/*
mount /home/metal3d/.cache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Évidemment, vous remplacerez &amp;ldquo;metal3d&amp;rdquo; par votre nom d&amp;rsquo;utilisateur&amp;hellip;&lt;/p&gt;
&lt;p&gt;Voilà, vous pouvez vous reloguer sur Gnome&amp;hellip;&lt;/p&gt;
&lt;p&gt;On va en faire autant pour les cache dnf et PackageKit&lt;/p&gt;
&lt;p&gt;Dans &lt;code&gt;/etc/fstab&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tmpfs   /var/cache/dnf              tmpfs   defaults,noatime        0   0
tmpfs   /var/cache/PackageKit       tmpfs   defaults,noatime        0   0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On fait pareil que pour les users, on vide le répertoire et on le monte. Par contre, on coupe le service PackageKit avant, histoire de ne pas lui couper la chique en plein milieu d&amp;rsquo;une opération.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl stop PackageKit
rm -rf /var/cache/PackageKit/*
rm -rf /var/cache/dnf/*
mount /var/cache/dnf
mount /var/cache/PackageKit
systemctl start PackageKit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà qui est fait !&lt;/p&gt;
&lt;h1 id=&#34;réparer-packagekit&#34;&gt;Réparer PackageKit&lt;/h1&gt;
&lt;p&gt;[EDIT] partie ajoutée le 03/11/2015&lt;/p&gt;
&lt;p&gt;Un souci va apparaitre, si vous décidez d&amp;rsquo;utiliser PackageKit pour vos mises à jour, il va y avoir un effet de bord très important: ça ne marchera plus.&lt;/p&gt;
&lt;p&gt;La raison est la suivante: l&amp;rsquo;interface vous propose de redémarrer. Or, en redémarrant, le RAMDisk est supprimé et les RPMs qui ont été téléchargés sont perdus. Vous avez alors trois possibilités:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vous ne mettez pas /var/cache/PackageKit en RAMDisk&lt;/li&gt;
&lt;li&gt;vous utilisez &amp;ldquo;&lt;code&gt;dnf update&lt;/code&gt;&amp;rdquo; au lieu de rebooter&lt;/li&gt;
&lt;li&gt;vous sauvegardez le cache lors de la fermeture, et vous le récupérer en RAMDisk au démarrage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour le troisième point, il suffit de faire un script et un fichier &amp;ldquo;unit&amp;rdquo;. Le script est simple et à placer, par exemple dans &amp;ldquo;&lt;code&gt;/usr/local/bin/savecaches.sh&lt;/code&gt;&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;[EDIT] Utilisation de rsync - plus de suppression du backup en dur. La synchro est plus rapide et limite l&amp;rsquo;accès disque (ainsi que les écritures).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash

PKGKIT=/var/cache/PackageKit
DNF=/var/cache/dnf

# create backup for PackageKit and dnf
[[ -d  ${PKGKIT}.bkp ]] || mkdir -p ${PKGKIT}.bkp
[[ -d  ${DNF}.bkp ]]    || mkdir -p ${DNF}.bkp

case $1 in

	restore)
		# move backup to real cache
		for c in $PKGKIT $DNF; do 
			rsync -ra --delete-after ${c}.bkp/ ${c}/
		done
		;;

	save)
        # restore backup cache
        for c in $PKGKIT $DNF; do 
            rsync -ra --delete-after ${c}/ ${c}.bkp/
        done
        ;;

esac
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Basiquement, appeler ce script avec un argument &amp;ldquo;save&amp;rdquo; va sauver le cache sur le disque dur, et l&amp;rsquo;argument &amp;ldquo;restore&amp;rdquo; va simplement le remettre en RAM (et supprimer le répertoire du disque dur).&lt;/p&gt;
&lt;p&gt;Maintenant, on va créer un fichier &amp;ldquo;unit&amp;rdquo;, je vais expliquer après à quoi servent les directives intéressantes. Le fichier à créer est &amp;ldquo;&lt;code&gt;/etc/systemd/system/cachesave.unit&lt;/code&gt;&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;[Unit]
Description= Save caches at shutdown - reset cache at startup
Before=packagekit.service packagekit-offline-update.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStop=/bin/bash /usr/local/bin/savecaches.sh save
ExecStart=/bin/bash /usr/local/bin/savecaches.sh restore

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est assez simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RemainAfterExit=yes signifie que quand la commande ExecStart a fonctionné, on garde l&amp;rsquo;état &amp;ldquo;actif&amp;rdquo; afin de ne pas le redémarrer en cas de dépendance.&lt;/li&gt;
&lt;li&gt;ExecStart=&amp;hellip; lance la commande au démarrage du service, donc dans notre cas ce sera quand packagekit ou packagekit-offline-updown aura démarré (et pas avant)&lt;/li&gt;
&lt;li&gt;ExecStop=&amp;hellip; lance cette commande quand le service est éteint, donc quand la machine redémarre ou s&amp;rsquo;éteind&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On perd un peu de l&amp;rsquo;optimisation d&amp;rsquo;écriture pour les SSD, car pour le coup nous allons écrire réellement des fichiers sur le disque. Mais en l&amp;rsquo;occurrence, vous aurez quand même une belle optimisation de lecture des paquets lors du dépaquetage à la mise à jour.&lt;/p&gt;
&lt;p&gt;Si en plus, comme moi, vous éteignez rarement votre PC, la sauvegarde du cache sera rare.&lt;/p&gt;
&lt;h1 id=&#34;et-pour-mes-caches-utilisateurs-&#34;&gt;Et pour mes caches utilisateurs ?&lt;/h1&gt;
&lt;p&gt;[EDIT] partie ajoutée le 03/11/2015&lt;/p&gt;
&lt;p&gt;[EDIT] utilisation de rsync pour limiter les écritures/accès disque et optimiser la vitesse de synchro - plus de suppression de backup en dur.&lt;/p&gt;
&lt;p&gt;Effectivement vous pouvez avoir envie de faire pareil pour vos caches utilisateurs. Le principe reste le même: vous créer un script qui sauve tous les répertoires &amp;ldquo;&lt;code&gt;/home/*/.cache&lt;/code&gt;&amp;rdquo; dans &amp;ldquo;&lt;code&gt;/home/*/.cache.bkp&lt;/code&gt;&amp;rdquo; et inversement. Par exemple dans &amp;ldquo;&lt;code&gt;/usr/local/bin/savehomecaches.sh&lt;/code&gt;&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;CACHE=.cache
BACKUP=.cache.bkp

# commands
case $1 in
    save)
        for c in /home/*; do
            # create backup cache directory if not exists an
            # ensure that owner is correct
            if [[ ! -d ${c}/${BACKUP} ]]; then
                mkdir -p ${c}/${BACKUP} 
                chown -R $(stat -c &amp;quot;%U:%G&amp;quot; $c) ${c}/${BACKUP}
            fi

            # synchronize cache to backup
            rsync -ra --delete-after ${c}/${CACHE}/ ${c}/${BACKUP}/
        done
        ;;

    restore)
        for c in /home/*; do
            # synchronize backup to cache
            rsync -ra --delete-after ${c}/${BACKUP}/ ${c}/${CACHE}/
        done
        ;;
esac
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et pour l&amp;rsquo;unit:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;[Unit]
Description=Home cache backup/restore

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStop=/bin/bash /usr/local/bin/savehomecaches.sh save
ExecStart=/bin/bash /usr/local/bin/savehomecaches.sh restore

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, plus besoin d&amp;rsquo;attendre les services PackageKit. Donc on ne met pas de &amp;ldquo;Before&amp;rdquo; dans l&amp;rsquo;unit. La synchronisation va se faire, bha, quand ça peut se faire&amp;hellip; Mais rapidement, bien avant que vous ne vous loguiez à Gnome.&lt;/p&gt;
&lt;p&gt;Bref, encore une fois, vous allez écrire sur votre SSD, ce qui induit une usure (voir la suite de l&amp;rsquo;article). C&amp;rsquo;est donc une opération propre à de l&amp;rsquo;optimisation lors de l&amp;rsquo;utilisation de votre machine, mais pas pour la réduction d&amp;rsquo;usure du SSD. Dans le même temps, il est clair que cette opération de sauvegarde de cache sur le SSD n&amp;rsquo;est pas pire que si vous ne mettiez pas de RAMDisk.&lt;/p&gt;
&lt;p&gt;Donc, pas de panique.&lt;/p&gt;
&lt;p&gt;Et puisque j&amp;rsquo;ai modifié l&amp;rsquo;article en fonction des commentaires, et que j&amp;rsquo;utilise rsync, et bien le ressenti sur le disque est encore adouci. Une nouvelle fois, merci à &lt;a href=&#34;http://penthium2.org/&#34;&gt;Frédéric Thouin&lt;/a&gt; pour cette idée !&lt;/p&gt;
&lt;h1 id=&#34;recharger-le-cache-au-démarrage-pour-dnf&#34;&gt;Recharger le cache au démarrage pour DNF&lt;/h1&gt;
&lt;p&gt;La commande &amp;ldquo;dnf&amp;rdquo; se met à jour si le cache est vide ou s&amp;rsquo;il est trop vieux. Le souci c&amp;rsquo;est que nous le vidons à chaque redémarrage, donc la prochaine fois que vous allez demander une mise à jour ou une installation, et bien &amp;ldquo;dnf&amp;rdquo; va télécharger les informations de dépôts.&lt;/p&gt;
&lt;p&gt;PackageKit le fait avec un service&amp;hellip; on va en faire autant.&lt;/p&gt;
&lt;p&gt;À tous ceux qui ont bavé sur systemd, je vous mets au défi de faire ce que je vais faire en si peu de caractères. C&amp;rsquo;est-à-dire de faire en sorte que le cache se charge quand on a accès au net, au démarrage du système.&lt;/p&gt;
&lt;p&gt;Créez un fichier &lt;code&gt;/etc/systemd/system/mydnfcache.service&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;[Unit] 
Description=DNF cache creation 
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/dnf makecache

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et activez-le pour le démarrage:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl enable mydnfcache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour le lancer de suite:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl start mydnfcache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà&amp;hellip; c&amp;rsquo;est fini.&lt;/p&gt;
&lt;h1 id=&#34;tu-chipotes-pas-un-peu-&#34;&gt;Tu chipotes pas un peu ?&lt;/h1&gt;
&lt;p&gt;Certains me disent à propos de mes montages en RAMDisk:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;À comparer du nombre de trucs écrits par ton OS par rapport à tes écritures, tu te fais suer pour rien.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Ma réponse est la suivante:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Non, je ne chipote pas. D&amp;rsquo;une part monter des RAMDisk est super simple et mon paramétrage ne bouge pas. J&amp;rsquo;ai passé 5 minutes à toucher mon fstab, ça vaut le coup.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;D&amp;rsquo;autre part, le nombre de fichiers que j&amp;rsquo;écris, même involontairement, est très conséquent. On parle de plusieurs gigaoctets par jour. Entre le cache navigateur, le cache de mails, les pages web et mes développements, l&amp;rsquo;intérêt des caches montés dans un tmpfs (ramdisk) est franchement loin d&amp;rsquo;être insignifiant.&lt;/p&gt;
&lt;p&gt;À vous de voir&amp;hellip; Si vous pensez que j&amp;rsquo;ai tort, ne faites rien de ce que je dis, après tout ce n&amp;rsquo;est que mon avis.&lt;/p&gt;
&lt;h1 id=&#34;et-au-fait-mon-ssd-&#34;&gt;Et au fait, mon SSD ?&lt;/h1&gt;
&lt;p&gt;Un SSD ça fait peur quand on l&amp;rsquo;achète. Oui c&amp;rsquo;est super rapide, ça soulage vraiment, et le confort de travail est réellement augmenté. Honnêtement, démarrer un PC en moins de 3 secondes, charger des programmes conséquents en un instant et voir enfin son CPU ne plus être bridé par les lectures disque, ça change la vie. Et je pèse mes mots.&lt;/p&gt;
&lt;p&gt;Mais tous les articles du net que j&amp;rsquo;ai lu me sortaient que&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la durée de vie allait décroitre à mesure que j&amp;rsquo;allais écrire sur mon disque&lt;/li&gt;
&lt;li&gt;ça allait être de plus en plus lent&lt;/li&gt;
&lt;li&gt;j&amp;rsquo;allais devenir stérile&lt;/li&gt;
&lt;li&gt;mes ongles allaient prendre feu&lt;/li&gt;
&lt;li&gt;je serai voué à écouter du Céline Dion jusqu&amp;rsquo;à la fin des temps&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et vraiment, Céline Dion, je peux pas&amp;hellip;&lt;/p&gt;
&lt;p&gt;Un SSD n&amp;rsquo;a pas de mécanique comme sur les HDD traditionnel (on dit &amp;ldquo;mécanique&amp;rdquo;). Un HDD a une série de disque magnétiques en rotation. La tête de lecture lit les &amp;ldquo;blocs&amp;rdquo; quand ils passent au dessous. Donc il faut un temps pour que le disque se trouve sous la tête afin qu&amp;rsquo;un bloc soit lut. Et pour reconstituer un fichier, il faut lire plusieurs centaines, voir plusieurs miliers de blocs. C&amp;rsquo;est lent&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cdn.morguefile.com/imageData/public/files/m/mike73/preview/fldr_2011_01_02/file4591293994998.jpg&#34; alt=&#34;Disque dur mécanique, la tête de lecture se balade sur les disques magnétiques, eux-mêmes en rotation&#34;&gt;&lt;/p&gt;
&lt;p&gt;Un SSD par contre n&amp;rsquo;a pas ces soucis. Il ne contient que de l&amp;rsquo;électronique. C&amp;rsquo;est comme si vous aviez un bloc de RAM dont la mémoire ne s&amp;rsquo;efface pas quand on coupe le courant. Sans la mécanique, les temps de lecture/écriture sont très rapide. On parle de plusieurs centaines de fois plus rapide ! Vous imaginez bien (si vous n&amp;rsquo;avez pas de SSD) le gain de performance, les temps de démarrages ou encore le gain significatif de batterie pour les portables puisque nous n&amp;rsquo;avons pas de moteur à faire tourner.&lt;/p&gt;
&lt;p&gt;Pourquoi cette frayeur répandue sur ces articles ? Aurai-je eu tort d&amp;rsquo;acheter mon SSD ? Je vous le dis de suite, faut arrêter d&amp;rsquo;avoir peur. D&amp;rsquo;une part parce que votre SSD va tenir bon pendant quelques années, et surtout parce qu&amp;rsquo;on peut réduire les risques. D&amp;rsquo;abord avec les RAMDisk (puisqu&amp;rsquo;on ne va pas écrire sur le SSD) et ensuite en suivant quelques réglages que je vais vous donner. Bon ça sera pas le sauvetage du siècle, mais ça peut franchement augmenter la durée de vie selon votre utilisation. Mais d&amp;rsquo;abord, un peu de théorie.&lt;/p&gt;
&lt;h2 id=&#34;1-ko-à-pied-ça-use-ça-use&#34;&gt;1 ko à pied, ça use, ça use&amp;hellip;&lt;/h2&gt;
&lt;p&gt;En gros, le souci du SSD, c&amp;rsquo;est que quand vous écrivez sur un block, ce dernier s&amp;rsquo;abîme&amp;hellip; et après 100.000 écritures environ, ce block de 4ko sera inutilisable.&lt;/p&gt;
&lt;p&gt;Vous pouvez d&amp;rsquo;ailleurs connaitre l&amp;rsquo;état d&amp;rsquo;usure (wearout) de votre disque, simplement avec la commande &amp;ldquo;smartctl&amp;rdquo; fourni dans le paquet &amp;ldquo;smartmontools&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo smartctl -a /dev/sda
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cherchez la ligne &amp;ldquo;Wear_Leveling_Count&amp;rdquo; ou contenant &amp;ldquo;Wear&amp;rdquo; (parfois ça change d&amp;rsquo;une verison à l&amp;rsquo;autre de smartctl). Vous verrez alors plusieurs colones. La colonne &amp;ldquo;VALUE&amp;rdquo; indique son état de vie. Donc une valeur de &amp;ldquo;100&amp;rdquo; indique que vous avez &amp;ldquo;100%&amp;rdquo; de la capacité de vie.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;À partir de 20%&amp;hellip; je vous conseille fortement de sauvegarder vos données&lt;/strong&gt; et de rapidment aller acheter un autre disque dur.&lt;/p&gt;
&lt;p&gt;Continuons&amp;hellip;&lt;/p&gt;
&lt;p&gt;Pour éviter d&amp;rsquo;abïmer rapidement les blocks en effacant les valeurs qui s&amp;rsquo;y trouvent, les constructeurs ont eut une idée fabuleuse: ne pas écrire dedans quans on a des blocs propre plus loin. Par con hein&amp;hellip;&lt;/p&gt;
&lt;p&gt;Attention je &lt;strong&gt;vulgarise&lt;/strong&gt; (et ça va rester un peu confus pourtant):&lt;/p&gt;
&lt;p&gt;Quand on efface un fichier, on indique simplement que, sur le disque, les blocs utilisés pour ce fichier sont &amp;ldquo;libres&amp;rdquo;. On efface pas vraiment le contenu, c&amp;rsquo;est la table des matières qui n&amp;rsquo;a plus l&amp;rsquo;index pour ce contenu.&lt;/p&gt;
&lt;p&gt;Si vous voulez maintenant écrire un nouveau fichier, on va aller sur le bloc libre et écrire une donnée. Or, à force de faire cela, on va user les premiers blocs plus vite que les derniers. Donc pour éviter cette usure, le contrôleur de disque va chercher des blocs qui ne contiennent pas de valeur (des blocs avec aucune charge). Et pour ce faire, il va tester les blocs qu&amp;rsquo;il trouve en chemin. Si le bloc n&amp;rsquo;est pas utilisé par une resource et qu&amp;rsquo;il a une valeur nulle, alors il peut l&amp;rsquo;utiliser. Sinon, il en cherche un autre, et un autre, etc&amp;hellip; car il ne veut pas abîmer les blocs en écrivant &amp;ldquo;null&amp;rdquo; (vidage de charge) et en chargeant le bloc ensuite. C&amp;rsquo;est pas idiot, croyez moi, c&amp;rsquo;est même très intelligent.&lt;/p&gt;
&lt;p&gt;Mais cela induit un effet de bord: le contrôleur va devoir tester chaque bloc pour vérifier s&amp;rsquo;il est vide dans les zones où la donnée est considéré comme &amp;ldquo;effacée&amp;rdquo;. Et ça, ça ralenti le système. Plus vous créez et supprimez des fichiers, plus ça ralenti les process. Donc, il faut de temps en temps faire en sorte que le contrôleur ne se prennent pas la tête à vérifier tous les blocs.&lt;/p&gt;
&lt;p&gt;Fin de la théorie, on passe à l&amp;rsquo;action.&lt;/p&gt;
&lt;h2 id=&#34;trim&#34;&gt;TRIM&lt;/h2&gt;
&lt;p&gt;L&amp;rsquo;idée est donc de soulager ces tests de temps en temps en mettant vraiment un zéro (en vidant la charge) sur les blocs qui ont reçu un jour une donnée et dont on a supprimé le contenu. C&amp;rsquo;est ce que nous appelons un &amp;ldquo;TRIM&amp;rdquo;. Pour simplifier, on va chercher tous les blocs du disque, vérifier s&amp;rsquo;ils sont utilisés par une ressource (fichier) et mettre &amp;ldquo;null&amp;rdquo; dedans (vider la charge) si ce n&amp;rsquo;est pas le cas. Par conséquent, le contrôleur va pouvoir écrire dedans sans avoir à l&amp;rsquo;éffacer, et il ne va pas chercher d&amp;rsquo;autres blocs libres.&lt;/p&gt;
&lt;p&gt;Pour vérifier si vous pouver &amp;ldquo;trimer&amp;rdquo; votre disque:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ sudo hdparm -I /dev/sda | grep -i trim 
 *  Data Set Management TRIM supported (limit 8 blocks)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si vous n&amp;rsquo;avez pas de ligne qui apparait&amp;hellip; bha désolé pour vous.&lt;/p&gt;
&lt;p&gt;Sur Fedora, et certainement d&amp;rsquo;autres distributions, il existe un service systemd qui va le faire pour vous:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;systemctl enable fstrim.timer
systemctl start fstrim.timer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sinon, &lt;strong&gt;si vous n&amp;rsquo;avez pas ce service&lt;/strong&gt;, vous pouvez le faire vous-même.&lt;/p&gt;
&lt;p&gt;Sous linux, la commande &amp;ldquo;fstrim&amp;rdquo; va donc faire le &amp;ldquo;TRIM&amp;rdquo;. D&amp;rsquo;ailleurs le service systemd &amp;ldquo;fstrim&amp;rdquo; ne fait ni plus ni moins qu&amp;rsquo;un &amp;ldquo;&lt;code&gt;fstrim -a&lt;/code&gt;&amp;rdquo; toutes les semaines.&lt;/p&gt;
&lt;p&gt;Donc, si vous n&amp;rsquo;avez pas ce service sur votre système, vous pouvez créer une tâche &amp;ldquo;cron&amp;rdquo; qui s&amp;rsquo;en occupera.&lt;/p&gt;
&lt;p&gt;Selon le nombre d&amp;rsquo;écritures sur le disque, vous pouvez placer le script suivant dans &lt;code&gt;/etc/cron.weekly&lt;/code&gt; ou &lt;code&gt;/etc/cron.daily&lt;/code&gt;. &lt;strong&gt;La recommandation est &amp;ldquo;weekly&amp;rdquo; à moins que vous ayez vraiment un nombre très conséquent d&amp;rsquo;écritures disques&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le script &lt;code&gt;/etc/cron.weekly/fstrim&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/sh
LOG=/var/log/fstrim.log
echo &amp;quot;===&amp;quot; $(date -R) &amp;quot;===&amp;quot; &amp;gt;&amp;gt; $LOG
fstrim -a 2&amp;gt;&amp;amp;1 &amp;gt;&amp;gt; $LOG
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rendez-le exécutable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;chmod +x /etc/cron.weekly/fstrim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tous les dimanches à minuit, ou au prochain démarrage si cette date est passée et que la machine n&amp;rsquo;était pas active, un &amp;ldquo;TRIM&amp;rdquo; est fait sur le disque.&lt;/p&gt;
&lt;h1 id=&#34;quelques-autres-idées&#34;&gt;Quelques autres idées&lt;/h1&gt;
&lt;p&gt;Les RAMDisk sont faciles à créer. Personnellement quand je bosse sur un développement qui génère des fichiers temporaires, je me débrouille pour écire dans &amp;ldquo;/tmp&amp;rdquo; ou mon &amp;ldquo;.cache&amp;rdquo; personnel. Mais parfois ce n&amp;rsquo;est pas facile de changer le comportement de l&amp;rsquo;outil de génération.&lt;/p&gt;
&lt;p&gt;Si, et &lt;strong&gt;seulement si&lt;/strong&gt; les fichiers générés sont jetables, vous pouvez repérer simplement le répertoire de sortie de ces fichiers et monter un ramdisk.&lt;/p&gt;
&lt;p&gt;Je prends un exemple&amp;hellip; on bosse avec gulp qui génère des fichiers temporaires dans un répertoire &amp;ldquo;.tmp&amp;rdquo;, lui-même contenu dans le répertoire du projet. Comme on est 5 à bosser dessus, et qu&amp;rsquo;on est pas tous sur le même OS, on impose pas l&amp;rsquo;écriture dans &amp;ldquo;/tmp&amp;rdquo;. Du coup, je monte un disque:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;rm .tmp/*
sudo mount -t tmpfs tmpfs $(pwd)/.tmp 
gulp serve
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le résultat est que je n&amp;rsquo;écris pas ce cache sur le disque. Je n&amp;rsquo;impacte pas mon SSD et en plus l&amp;rsquo;accès est bien plus rapide.&lt;/p&gt;
&lt;p&gt;Idem avec les &amp;ldquo;builds&amp;rdquo;, les fichiers n&amp;rsquo;ont pas vocation de rester sur mon disque dur. Je prend l&amp;rsquo;exemple de mon blog (développé en Go). J&amp;rsquo;ai un paquet de css à minifier dans un fichier. Et bien je monter un disque tmpfs dans un coin et j&amp;rsquo;écris dedans. Quand je teste mon application, l&amp;rsquo;accès à ce fichier est rapide et je n&amp;rsquo;impacte pas mon SSD&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;bilan&#34;&gt;Bilan&lt;/h1&gt;
&lt;p&gt;Personnellement j&amp;rsquo;ai ressentis les effets des caches utilisateurs avec l&amp;rsquo;utilisation d&amp;rsquo;un HDD traditionnel, moins avec un SSD. Quand je dis &amp;ldquo;moins&amp;rdquo;, je ne veux pas dire &amp;ldquo;pas du tout&amp;rdquo;. Simplement qu&amp;rsquo;en toute logique, le ressenti est plus léger car les accès disques SSD sont très rapides.&lt;/p&gt;
&lt;p&gt;Donc, pour commencer, &lt;strong&gt;que vous soyez avec un SSD ou non, de toutes manières je vous recommande de monter des RAMDisk pour les données temporaires&lt;/strong&gt; - la vitesse de lecture en RAM va relativement soulager votre travail. Pensez à utiliser &amp;ldquo;&lt;code&gt;/tmp&lt;/code&gt;&amp;rdquo; et (si vous avez suivi mon article) votre répertoire &amp;ldquo;&lt;code&gt;~/.cache&lt;/code&gt;&amp;rdquo;. Montez à la volée des RAMDisks.&lt;/p&gt;
&lt;p&gt;Niveau mémoire, l&amp;rsquo;ensemble de mes RAMDIsks prennent 1Go réel sur mes 8Go de RAM. Htop ne me montre pas de niveau alarmiste, bien au contraire - et j&amp;rsquo;en suis toujours étonné.&lt;/p&gt;
&lt;p&gt;Le fait d&amp;rsquo;avoir mis un cache sur PackageKit et dnf a largement accéleré les grosses mises à jour, par contre il faut avouer que pendant la mise à jour l&amp;rsquo;utilisation de la RAM grimpe en flèche. Logique, l&amp;rsquo;ensemble des RPM se trouve dans le cache pendant l&amp;rsquo;installation, et par conséquent dans la RAM puisque le répertoire est un RAMDisk. Donc clairement, il vous faut de la RAM, un minimum de 4Go pour ne voir le pc ralentir pendant une mise à jour, et je pense que 8Go est le minimum recommandé pour ne pas avoir trop de ressenti. Au prix de la RAM, ne nous privons pas. Cela soulage le SSD, ça soulage les nerfs des propriétaires de HDD et c&amp;rsquo;est franchement facile de configurer des RAMDisk sous Linux du moment où on comprend comment les monter.&lt;/p&gt;
&lt;p&gt;Personnellement, je réfléchis à mettre encore d&amp;rsquo;autres répertoires en RAM, et à me payer 16Go, voir 32Go de RAM&amp;hellip;&lt;/p&gt;
&lt;p&gt;Quant au TRIM, et bien je ne peux pas trop vous dire ce qu&amp;rsquo;il en est. J&amp;rsquo;ai suivi des recommandations, je n&amp;rsquo;ai pas de points de comparaisons sous la main. Si vous en avez, contactez moi, commentez l&amp;rsquo;article, bref, tenez-moi au courant.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Un exemple Golang de résolution de tâche parallèle</title>
      <link>https://www.metal3d.org/blog/2015/un-exemple-golang-de-r%C3%A9solution-de-t%C3%A2che-parall%C3%A8le/</link>
      <pubDate>Fri, 30 Oct 2015 01:41:33 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/un-exemple-golang-de-r%C3%A9solution-de-t%C3%A2che-parall%C3%A8le/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;J&amp;rsquo;ai participé aux BlendWebMix 2015 en tant que &amp;ldquo;speaker&amp;rdquo; avec un grand plaisirs. J&amp;rsquo;y ai présenté &amp;ldquo;&lt;a href=&#34;http://go-talks.appspot.com/github.com/metal3d/blendwebmix2015/blendwebmix2015.slide#1&#34;&gt;Golang pour le web&lt;/a&gt;&amp;rdquo; afin d&amp;rsquo;expliquer comment ce langage est en train d&amp;rsquo;entrer dans les moeurs et va permettre de développer des applications Web performantes. Je vais vous montrer un exemple que j&amp;rsquo;ai traité lors d&amp;rsquo;une démo improvisée le lendemain sur un coin de bureau. Le fait est qu&amp;rsquo;on m&amp;rsquo;a beaucoup parlé après la conférence au stand &amp;ldquo;Smile&amp;rdquo; et qu&amp;rsquo;une question récurrente m&amp;rsquo;était posé: &amp;ldquo;As-tu un exemple concret que je ne peux pas résoudre en PHP par exemple, dans mon application/site Web ?&amp;rdquo;. J&amp;rsquo;en avais quelques-uns, et j&amp;rsquo;ai décidé de montrer le suivant: des miliers de personnes veulent remplir un formulaire de contact en simultané, comment je vais gérer tout ça en parallèle sans planter mon serveur de mail SMTP ? Voilà la démo, revue et corrigée.&lt;/p&gt;
&lt;p&gt;Pour remettre les choses dans leur contexte, la conférence présentait le développement d&amp;rsquo;un prototype de site avec &lt;a href=&#34;http://martini.codegangsta.io/&#34;&gt;Martini&lt;/a&gt;. Mais j&amp;rsquo;ai laissé entendre que Go pouvait permettre la résolution de tâche concurrente de manière simple. Bien entendu les connaisseurs du langage savent très bien que toute la saveur de Go se trouve dans ce principe de gestion et de synchronisation de &amp;ldquo;&lt;em&gt;goroutines&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/215ac61a-411a-4d39-9679-2d58cda15007-blendconf.jpg&#34; alt=&#34;Conférence Golang - BlendWebMix 2015&#34;&gt;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc improvisé une démo hors conférence le lendemain en 10 minutes, entre deux pilones, sur un coin de bureau. Et comme je suis un éternel insatisfait (laissant une coquille en fin de démo pour avoir le temps de conclure), je ne peux pas aller dormir sans mettre au clair mon explication. La voici.&lt;/p&gt;
&lt;h1 id=&#34;mettre-le-contact&#34;&gt;Mettre le contact&lt;/h1&gt;
&lt;p&gt;Prenons un exemple de page de &amp;ldquo;contact&amp;rdquo; qui permet d&amp;rsquo;envoyer un mail à un destinataire. La fonction qui va récupérer le formulaire ressemblerait à cela (avec mon framework &lt;a href=&#34;http://gopkg.in/kwiscale/framework.v1&#34;&gt;Kwiscale&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;// je passe le détail du handler, 
// on traite ici la méthode &amp;quot;POST&amp;quot; 
func (handler *ContactHandler) Post() {
    from := handler.GetPost(&amp;quot;from&amp;quot;)
    message := handler.GetPost(&amp;quot;message&amp;quot;)
    
    // on envoie le message
    sendMail(from, message)
    
    //on signale que c&#39;est ok
    handler.Redirect(&amp;quot;/contact/ok&amp;quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour clarifier, bien que ce soit peu utile à mon avis, on récupère les champs de formulaire &amp;ldquo;from&amp;rdquo; et &amp;ldquo;message&amp;rdquo; et on appelle la fonction &amp;ldquo;&lt;code&gt;sendMail()&lt;/code&gt;&amp;rdquo; (que nous allons coder plus bas), puis on signale à l&amp;rsquo;utilisateur que le message a été envoyé.&lt;/p&gt;
&lt;p&gt;La fonction &amp;ldquo;&lt;code&gt;sendMail()&lt;/code&gt;&amp;rdquo; peut être simulée par une tâche longue, par exemple ici on va faire en sorte qu&amp;rsquo;elle prenne 3 secondes à s&amp;rsquo;exécuter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func sendMail(from, message string) {
    log.Println(&amp;quot;Envoi du message...&amp;quot;)
    time.Sleep(3 * time.Second)
    log.Println(&amp;quot;Message envoyé !&amp;quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Que va-t-il se passer ?&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;utilisateur va remplir son formulaire, cliquer sur &amp;ldquo;Envoyer&amp;rdquo; et attendre sagement 3 secondes que le mail parte pour enfin voir un message lui signifiant que le message est envoyé.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ce fonctionnement est répandu&amp;hellip; et complètement idiot&amp;hellip;&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;attendre-est-inutile&#34;&gt;Attendre est inutile&lt;/h1&gt;
&lt;p&gt;Qu&amp;rsquo;on soit clair. En PHP (pour ne citer que lui) vous déroulez le code en partant du haut vers le bas sans vous poser de question. C&amp;rsquo;est simple, clair mais pas efficace. Si la connexion au serveur SMTP est contrainte, lente, foireuse, ou je ne sais quel adjectif employer, vous allez faire poireauter l&amp;rsquo;utilisateur pendant des plombes. Et pendant tout ce temps vous avez votre handler (ou votre controlleur) qui traine des pieds dans la mémoire. C&amp;rsquo;est encombrant et &lt;strong&gt;ça sert strictement à rien&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;La plus efficace des solutions consiste à envoyer le mail dans &lt;strong&gt;une tâche concurrente&lt;/strong&gt;, ce que nous appelons une &amp;ldquo;&lt;em&gt;goroutine&lt;/em&gt;&amp;rdquo; en Go, et que PHP (je suis désolé de lui casser du sucre sur le dos) ne sait (à ce jour) pas faire &amp;ldquo;facilement&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;En go, c&amp;rsquo;est simple: on ajoute le fabuleux mot clef &amp;ldquo;go&amp;rdquo; devant l&amp;rsquo;appel:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;// je passe le détail du handler, 
// on traite ici la méthode &amp;quot;POST&amp;quot; 
func (handler *ContactHandler) Post() {
    from := handler.GetPost(&amp;quot;from&amp;quot;)
    message := handler.GetPost(&amp;quot;message&amp;quot;)
    
    // on envoie le message *en parallèle*
    go sendMail(from, message)
    
    //on signale que c&#39;est ok
    handler.Redirect(&amp;quot;/contact/ok&amp;quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette fois, la redirection est faite alors que le mail est en cours de traitement. Que l&amp;rsquo;envoi foire ou pas !&lt;/p&gt;
&lt;p&gt;Question très pertinente d&amp;rsquo;un de mes auditeurs &amp;ldquo;&lt;em&gt;oui mais si le mail ne part pas, on ne peut pas prévenir l&amp;rsquo;utilisateur et gérer le problème dans notre éxécution principale&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Je suis d&amp;rsquo;accord avec le principe mais pas sur l&amp;rsquo;approche, ma réponse est la suivante:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;L&amp;rsquo;utilisateur ne saurait quoi faire de l&amp;rsquo;erreur. &lt;strong&gt;La routine peut gérer l&amp;rsquo;erreur dans son coin en sauvegardant le message quelque part, en déportant une nouvelle tentative d&amp;rsquo;envoi plus tard, en loguant l&amp;rsquo;erreur pour les admins. Mais en aucun cas l&amp;rsquo;utilisateur n&amp;rsquo;a besoin de savoir, ici dans notre cas, que le mail est bien parti, il s&amp;rsquo;en moque je dirai, pire ça le génerai&lt;/strong&gt;. Je suis conscient que parfois vous aurez besoin de traiter l&amp;rsquo;erreur et de faire un retour visuel à l&amp;rsquo;utilisateur. Mais dans bien des cas, et c&amp;rsquo;est notre cas, ce n&amp;rsquo;est absolument pas pertinent puisque nous sommes en mesure de gérer ces plantages sans embêter l&amp;rsquo;utilisateur.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;ça-suffit-&#34;&gt;Ça suffit ?&lt;/h1&gt;
&lt;p&gt;Dans 90% des cas, cette simple modification suffit.&lt;/p&gt;
&lt;p&gt;Mais imaginons maintenant que nous ayons une contrainte de volume. Par exemple, nous savons que nous allons avoir des centaines d&amp;rsquo;utilisateurs qui vont se connecter en même temps et tenter d&amp;rsquo;envoyer un mail avec notre formulaire de contact.&lt;/p&gt;
&lt;p&gt;Ces centaines de mail vont être traités en même temps, dans autant de goroutines que de message à envoyer. Et ça, c&amp;rsquo;est pas bon pour les nerfs de notre serveur SMTP.&lt;/p&gt;
&lt;p&gt;Il va falloir &amp;ldquo;mettre en queue&amp;rdquo; les messages et envoyer un nombre limité de mails en simultané.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai déjà tenté de résoudre ce souci en Python, ça a été épique mais pas insurmontable. En PHP je n&amp;rsquo;ai pas de solution simple qui me vienne à l&amp;rsquo;esprit. En Go par contre, c&amp;rsquo;est &lt;em&gt;limpide&lt;/em&gt;.&lt;/p&gt;
&lt;h1 id=&#34;faites-la-queue-&#34;&gt;Faites la queue !&lt;/h1&gt;
&lt;p&gt;Le principe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la fonction &amp;ldquo;&lt;code&gt;sendMail()&lt;/code&gt;&amp;rdquo; ne va plus envoyer directement le message mais déposer le message dans un canal (&amp;quot;&lt;code&gt;chan&lt;/code&gt;&amp;quot;)&lt;/li&gt;
&lt;li&gt;on va lancer une goroutine qui va lire le canal en continu et envoyer les mails au fur et à mesure&lt;/li&gt;
&lt;li&gt;voilà&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc on commence par notre &amp;ldquo;consomateur de message&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;
// une structure pour mieux empaqueter le message.
type Mail struct {
    From string
    Message string
}

// on crée un canal de mails.
// Pour des questions de perd, on va utiliser l&#39;adresse de la structure
// et non pas la valeur. Donc &amp;quot;*Mail&amp;quot; =&amp;gt; pointeur.
var mailsQueue = make(chan *Mail)

// la fonction qui va traiter les messages.
func MailSender(){
    // lit continellement le canal et traite le message
    for mail := range mailsQueue {
        time.Sleep(3 * time.Second)
        log.Println(&amp;quot;Message envoyé&amp;quot;, mail.From, mail.Message)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A noter que j&amp;rsquo;aurai put écrire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;// la fonction qui va traiter les messages
func MailSender(){
    // lit continellement le canal et traite le message
    for  {
        mail &amp;lt;- mailsQueue
        time.Sleep(3 * time.Second)
        log.Println(&amp;quot;Message envoyé&amp;quot;, mail.From, mail.Message)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On modifie maintenant &amp;ldquo;&lt;code&gt;sendMail&lt;/code&gt;&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func sendMail(from, message string) {
    log.Println(&amp;quot;Envoi du message...&amp;quot;)
    mailsQueue &amp;lt;- &amp;amp;Message{from, message}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On envoie donc notre pointeur sur &amp;ldquo;Message&amp;rdquo; (l&amp;rsquo;adresse&amp;hellip;) dans la queue, et on attend que le canal soit lut pour quitter la fonction.&lt;/p&gt;
&lt;p&gt;Rappelez vous que dans notre handler nous avons lancé &amp;ldquo;sendMail()&amp;rdquo; en tant que &amp;ldquo;goroutine&amp;rdquo;. Donc, même si &amp;ldquo;mailsQueue&amp;rdquo; est plein à craquer, on a déjà répondu à l&amp;rsquo;utilisateur que le mail est traité.&lt;/p&gt;
&lt;p&gt;On aura bien des centaines de goroutines qui vont attendre de pouvoir écrire dans le canal mais ça ne coute pas grand chose. Une goroutine ne prend que &lt;strong&gt;quelques kilo-octets en mémoire&lt;/strong&gt;. Ce qui compte c&amp;rsquo;est que &lt;strong&gt;nous n&amp;rsquo;aurons pas des centaines de connexions simultanées sur notre SMTP&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Bref, il reste un dernier point, lancer autant de &amp;ldquo;MailSender&amp;rdquo; qu&amp;rsquo;on veut pour gérer l&amp;rsquo;envoi en simultané. On peut le mettre dans notre fonction &amp;ldquo;&lt;code&gt;main&lt;/code&gt;&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func main(){
    // on lance 10 MailSender en parallèle 
    // et on passe à la suite
    for i := 0 ; i &amp;lt; 10; i++ {
        go MailSender()
    }
    
    // on lance notre service web
    kwiscale.NewAppFromConfig()
    kwiscale.ListenAndServe()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est fini.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nous avons 10 goroutines qui tentent de lire le canal &amp;ldquo;&lt;code&gt;mailsQueue&lt;/code&gt;&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Quand un client poste un message, on crée une goroutine &amp;ldquo;&lt;code&gt;sendMail()&lt;/code&gt;&amp;rdquo; qui envoie un &amp;ldquo;&lt;code&gt;Mail&lt;/code&gt;&amp;rdquo; dans &amp;ldquo;&lt;code&gt;mailsQueue&lt;/code&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;On répond &lt;strong&gt;tout de suite&lt;/strong&gt; au client que le mail est traité, sans attendre la fin de la goroutine&lt;/li&gt;
&lt;li&gt;Pendant ce temps (à Veracruz) &amp;ldquo;&lt;code&gt;MailSender&lt;/code&gt;&amp;rdquo; lit le canal et envoie le message&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si 11 clients appuient en même temps sur &amp;ldquo;Envoyer&amp;rdquo; de notre formulaire:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les 10 premiers appels vont ajouter un &amp;ldquo;&lt;code&gt;Mail&lt;/code&gt;&amp;rdquo; dans le canal &amp;ldquo;&lt;code&gt;mailsQueue&lt;/code&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Les 10 goroutines &amp;ldquo;&lt;code&gt;MailSender&lt;/code&gt;&amp;rdquo; en parallèle vont lire le canal &amp;ldquo;&lt;code&gt;mailsQueue&lt;/code&gt;&amp;rdquo; et traiter la demande pendant 3 secondes&lt;/li&gt;
&lt;li&gt;Le 11ième &amp;ldquo;&lt;code&gt;Mail&lt;/code&gt;&amp;rdquo; se retrouve bloqué dans &amp;ldquo;&lt;code&gt;sendMail&lt;/code&gt;&amp;rdquo; au moment d&amp;rsquo;écrire dans le canal (car personne ne le lit pour le moment)&lt;/li&gt;
&lt;li&gt;La première goroutine &amp;ldquo;&lt;code&gt;MailSender&lt;/code&gt;&amp;rdquo; qui a traité un des &amp;ldquo;&lt;code&gt;Mail&lt;/code&gt;&amp;rdquo; va alors lire de nouveau &amp;ldquo;&lt;code&gt;mailsQueue&lt;/code&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Et par conséquent la 11ième goroutine &amp;ldquo;&lt;code&gt;sendMail&lt;/code&gt;&amp;rdquo; se débloque puisque le canal dans lequel elle écrit vient d&amp;rsquo;être lut&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On a traité le souci. On ne traite que 10 mails en parallèle, on ne bloque pas le client et on a soulagé le SMTP.&lt;/p&gt;
&lt;h1 id=&#34;en-bref&#34;&gt;En bref&lt;/h1&gt;
&lt;p&gt;Les BlendWebMix 2015 m&amp;rsquo;ont donné l&amp;rsquo;occasion de parler à un grand nombre de développeurs en tout genre. Java, PHP, Ruby, AnjularJS, ReactJS, etc. Mais aussi de décisionnaires, de gestionnaires et de professionnels du marketing. Ce que je leur ai montré a, selon eux, donné dujet à discussion, ouvert des possibilités pour pas mal de problématiques à résoudre et c&amp;rsquo;est le contrat que je voulais remplir. Certes Golang ne répond pas à tout. Il a aussi ses défauts, ses contraintes, ses complexités. Mais dans l&amp;rsquo;ensemble, je suis très content d&amp;rsquo;avoir démontré l&amp;rsquo;intelligence de ce langage et qu&amp;rsquo;il n&amp;rsquo;a rien d&amp;rsquo;archaïque, de compliqué et qu&amp;rsquo;il n&amp;rsquo;est pas fait que pour le système.&lt;/p&gt;
&lt;p&gt;Petit et grands sites peuvent utiliser Go sans souci: ce blog est codé en Go, et un grand nombre de grand comptes se sont penché sur cette technologie pour différentes raisons (scalling, streaming, &amp;hellip;).&lt;/p&gt;
&lt;p&gt;En attendant la mise en ligne de la vidéo de ma conférence, vous pouvez toujours visiter le slide interfactif sur GoTalks: &amp;ldquo;&lt;a href=&#34;http://go-talks.appspot.com/github.com/metal3d/blendwebmix2015/blendwebmix2015.slide#1&#34;&gt;Golang pour le web&lt;/a&gt;&amp;rdquo; en prenant bien en compte que &lt;strong&gt;les exemples Martini ne peuvent pas tourner sur le serveur GoTalks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Les exemples se trouvent sur Github: &lt;a href=&#34;http://github.com/metal3d/blendwebmix2015/&#34;&gt;github.com/metal3d/blendwebmix2015/&lt;/a&gt;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Fish et les notifications de Gnome terminal</title>
      <link>https://www.metal3d.org/blog/2015/fish-et-les-notifications-de-gnome-terminal/</link>
      <pubDate>Sun, 18 Oct 2015 08:54:35 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/fish-et-les-notifications-de-gnome-terminal/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Sur Fedora 22, gnome-terminal vous permet d&amp;rsquo;avoir une notification quand une commande se termine et que vous êtes sur une autre fenêtre. Très pratique quand vous avez executé une commande &amp;ldquo;curl&amp;rdquo; ou &amp;ldquo;wget&amp;rdquo; dans un coin ou si vous lancez des procédure d&amp;rsquo;installation ou de compilation qui prennent un certain temps. Or, si vous passez au shell &amp;ldquo;fish&amp;rdquo;, les notifications ne marchent plus. Voilà comment on s&amp;rsquo;y prend pour les retrouver.&lt;/p&gt;
&lt;h1 id=&#34;tldr&#34;&gt;TL;DR;&lt;/h1&gt;
&lt;p&gt;Dans .config/fish/config.fish:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;if test &amp;quot;$VTE_VERSION&amp;quot; -ge 3405
    switch &amp;quot;$TERM&amp;quot;
        case &#39;vte*&#39; &#39;xterm*&#39;
            function __notify_vte_command_completed --on-event fish_postexec --description &#39;Notify VTE of command completion&#39;
                printf &#39;\e]777;notify;Command completed;%s\a&#39; (echo &amp;quot;$argv&amp;quot; | cat --show-nonprinting | tr --delete \;)
            end
    end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et relancez votre sessions.&lt;/p&gt;
&lt;h1 id=&#34;détaillons&#34;&gt;Détaillons&lt;/h1&gt;
&lt;p&gt;Depuis quelques temps les terminaux VTE (Virtual Terminal Emulator, le widget gnome qui permet d&amp;rsquo;afficher un terminal) comprend quelques &amp;ldquo;séquences d&amp;rsquo;échappement&amp;rdquo; (escape sequence) qui permettent de s&amp;rsquo;amuser un peu. L&amp;rsquo;une de ces séquence est &lt;code&gt;\e]777&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;En général, il suffit d&amp;rsquo;afficher une suite d&amp;rsquo;argument à cette séquence:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;\e]777;argument 1; argument 2; etc...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le premier argument définit quelle action exécuter. Dans notre cas, ce sera &amp;ldquo;notify&amp;rdquo;. Les arguments suivant sont définis en fonctione de l&amp;rsquo;action. Pour &amp;ldquo;notify&amp;rdquo; on défini:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le titre&lt;/li&gt;
&lt;li&gt;le texte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La notification ne se fera que si votre focus se trouve sur une autre fenêtre. Donc l&amp;rsquo;idée c&amp;rsquo;est d&amp;rsquo;ouvrir un terminal gnome et une autre fenêtre (par exemple restez sur cette page web).&lt;/p&gt;
&lt;p&gt;Dans le terminal, tapez:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# sur fish
sleep 2; and echo -e &#39;\e]777&#39;;notify;Le titre;Le contenu&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rapidement, passez sur une autre fenêtre et vous devez avoir une notification de bureau.&lt;/p&gt;
&lt;h1 id=&#34;fish-config&#34;&gt;Fish config&lt;/h1&gt;
&lt;p&gt;En cherchant un peu, je suis tombé sur ce ticket: &lt;a href=&#34;https://github.com/fish-shell/fish-shell/issues/2102&#34;&gt;Issue 2102&lt;/a&gt; - apparement fish avait intégré le fait d&amp;rsquo;envoyer des notification Gnome mais certaines personnes ont eut quelques soucis. Du coup, l&amp;rsquo;auteur à retiré (temporairement ?) le test qui permettait l&amp;rsquo;envoit de notification.&lt;/p&gt;
&lt;p&gt;Mais, le commit qu&amp;rsquo;il a retiré est lisible &lt;a href=&#34;https://github.com/fish-shell/fish-shell/commit/5c4acc8ee1485e40b00ea0578aeff338078cfb8b&#34;&gt;ici&lt;/a&gt; et on voit que c&amp;rsquo;est une simple fonction à ajouter dans la configuration.&lt;/p&gt;
&lt;p&gt;Il suffit donc de prendre le bloc qu&amp;rsquo;il a supprimé, et le mettre dans votre propre configuration.&lt;/p&gt;
&lt;p&gt;Sachez qu&amp;rsquo;en l&amp;rsquo;état, c&amp;rsquo;est un peu moins fin que ce que nous avions avec Bash. En effet, vous n&amp;rsquo;aurez que des notifications qui vous indiqueront qu&amp;rsquo;une commande est terminée mais pas son état. Alors que sur bash, on avait connaissance du fait que la commande avait planté ou réussi. Il doit y avoir une solution à cela, mais en l&amp;rsquo;état c&amp;rsquo;est déjà bien utile (selon moi) d&amp;rsquo;avoir au moins la notification.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Docker Apache Mysql PHP</title>
      <link>https://www.metal3d.org/blog/2015/docker-apache-mysql-php/</link>
      <pubDate>Thu, 24 Sep 2015 11:33:18 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/docker-apache-mysql-php/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Ce matin un collègue me demande &amp;ldquo;comment tu ferais pour travailler en PHP avec Docker ?&amp;rdquo; - la question étant de pouvoir &amp;ldquo;développer localement en PHP + Apache + MySQL sans avoir à tout installer&amp;rdquo;. Ma réponse a été &amp;ldquo;docker-compose et des liens + des volumes&amp;rdquo;. Du coup, un petit billet pour expliquer la méthode s&amp;rsquo;imposait. Voici comment je procède.&lt;/p&gt;
&lt;h1 id=&#34;docker-et-docker-compose&#34;&gt;Docker et Docker-Compose&lt;/h1&gt;
&lt;p&gt;En premier lieu, il vous faut Docker et docker-compose. En théorie, votre distribution propose des paquets tout prêts, il s&amp;rsquo;agit de les installer. Personnellement j&amp;rsquo;ai quand même installé &amp;ldquo;docker-compose&amp;rdquo; avec &amp;ldquo;pip&amp;rdquo;. La raison est simple, je peux mettre à jour l&amp;rsquo;outil rapidement sans avoir à attendre que le packageur propose un paquet.&lt;/p&gt;
&lt;p&gt;Donc, en gros (sous Fedora 22):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo dnf install python-pip
pip install --user docker-compose
docker-compose --help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà. Maintenant on s&amp;rsquo;atèle à mettre en place notre pile de conteneurs.&lt;/p&gt;
&lt;h1 id=&#34;les-conteneurs&#34;&gt;Les conteneurs&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Rappel:&lt;/strong&gt; il est fortement conseillé d&amp;rsquo;utiliser &lt;strong&gt;un conteur par service&lt;/strong&gt;, comprennez bien &amp;ldquo;un conteneur pour mysql et un autre pous apache&amp;rdquo; dans notre cas. La raison est simple: je peux changer de version de php ou de mysql indépendament.&lt;/p&gt;
&lt;p&gt;Bref, on se penche sur nos deux images Docker à utiliser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;php: &lt;a href=&#34;https://hub.docker.com/_/php/&#34;&gt;https://hub.docker.com/_/php/&lt;/a&gt; =&amp;gt; le tag &amp;ldquo;apache&amp;rdquo; permet d&amp;rsquo;avoir PHP sur Apache&lt;/li&gt;
&lt;li&gt;mysql: &lt;a href=&#34;https://hub.docker.com/_/mysql/&#34;&gt;https://hub.docker.com/_/mysql/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour qu&amp;rsquo;on soit prêt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker pull php:apache
docker pull mysql:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, on a les images sur notre système. Sachez que si vous ne faites pas le &amp;ldquo;pull&amp;rdquo;, docker-compose le fera pour vous, mais je préfère préparer le terrain pour ne pas avoir à attendre le téléchargement quand je vais tester mon installation.&lt;/p&gt;
&lt;h1 id=&#34;docker-compose&#34;&gt;Docker-compose&lt;/h1&gt;
&lt;p&gt;Maintenant on va paramétrer notre composition de conteneur. En gros il faut que Apache démarre et qu&amp;rsquo;il sache lire mes sources PHP. Il faut que MySQL démarre, et il faut que PHP sache où trouver le serveur MySQL. Car je vous le rapelle, le conteneur MySQL peut changer d&amp;rsquo;IP à chaque redémarrage.&lt;/p&gt;
&lt;p&gt;Le principe, pour partager un peu de configuration entre deux conteneurs, est d&amp;rsquo;utiliser un &amp;ldquo;lien&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Bref, on prépare le terrain.&lt;/p&gt;
&lt;p&gt;Créez un répertoire de travail, par exemple &lt;code&gt;$HOME/workspace/monsite&lt;/code&gt; et allez dans ce répertoire.&lt;/p&gt;
&lt;p&gt;Dans ce répertoire, on crée:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un répertoire de sources php&lt;/li&gt;
&lt;li&gt;un fichier docker-compose.yml&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voyons le fichier YAML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apache:
    image: php:apache
    volumes:
    - &amp;quot;src/:/var/www/html&amp;quot;
    links:
    - mysql
    ports:
    - &amp;quot;8080:80&amp;quot;
    privileged: true

mysql:
    image: mysql:latest
    environment:
        MYSQL_ROOT_PASSWORD: passroot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Explications:&lt;/p&gt;
&lt;p&gt;La première entrée que j&amp;rsquo;ai nommé &amp;ldquo;apache&amp;rdquo; correspond à un conteneur. Il va utiliser une image &amp;ldquo;php:apache&amp;rdquo;. Dans ce conteneur, je &amp;ldquo;monte&amp;rdquo; le répertoire &amp;ldquo;src&amp;rdquo; local (qui contiendra mes sources php) sur le répertoire &lt;code&gt;/var/www/html&lt;/code&gt; &lt;strong&gt;du conteneur&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ainsi, quand apache va lire ce répertoire, il verra en fait ce que j&amp;rsquo;ai sur mon poste.&lt;/p&gt;
&lt;p&gt;Je crée un lien dans la section &amp;ldquo;links&amp;rdquo; (qui est une liste), où je spécifie que mon conteneur &amp;ldquo;mysql&amp;rdquo; (défini plus bas) doit être démarré avant pour qu&amp;rsquo;il me fournisse des information (ip, nom, ports, etc&amp;hellip;)&lt;/p&gt;
&lt;p&gt;Je spécifie ensuite que je &amp;ldquo;bind&amp;rdquo; le port 8080 de ma machine sur le port 80 du conteneur. C&amp;rsquo;est le port 80 qu&amp;rsquo;écoutera apache sur le conteneur. Du coup, quand je vais requêter ma machine sur le port &amp;ldquo;8080&amp;rdquo; via http://localhost:8080, c&amp;rsquo;est le conteneur qui va répondre.&lt;/p&gt;
&lt;p&gt;En dernier lieu, parce que je suis sur Fedora et que j&amp;rsquo;ai pas trouvé plus simple pour corriger un souci de droit que je déteste, je met &amp;ldquo;privileged: true&amp;rdquo;. Cela permet à mon conteneur d&amp;rsquo;avoir le droit de lire le volume monté. Le jour où je trouve une autre solution (qui ne me demande pas de labelliser avec SELinux) je donnerai des nouvelles.&lt;/p&gt;
&lt;p&gt;Ensuite vient la section &amp;ldquo;mysql&amp;rdquo;, elle va utiliser l&amp;rsquo;image &amp;ldquo;mysql:latest&amp;rdquo;, et comme le spécifie la documentation je définir un mot de passe root pour la base.&lt;/p&gt;
&lt;p&gt;Allons dans le répertoire &amp;ldquo;src&amp;rdquo; et créons un simple fichier &amp;ldquo;test.php&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php
phpinfo()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, on démarre le conteneur:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous allez voir les logs des deux conteneurs, vous pouvez aller voir l&amp;rsquo;adresse http://localhost:8080/test.php&lt;/p&gt;
&lt;h1 id=&#34;variables-denvironement&#34;&gt;Variables d&amp;rsquo;environement&lt;/h1&gt;
&lt;p&gt;Descendez dans la page, et chercher la section &amp;ldquo;PHP Variables&amp;rdquo; où se trouve les entrées &amp;ldquo;_ENV&amp;rdquo;. Vous allez voir ce genre de chose:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;_ENV[&amp;quot;MYSQL_1_PORT_3306_TCP&amp;quot;]&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;tcp://172.17.0.22:3306&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;_ENV[&amp;quot;HOSTNAME&amp;quot;]&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;813ce8655fd2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;_ENV[&amp;quot;MYSQL_ENV_MYSQL_ROOT_PASSWORD&amp;quot;]&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;passroot&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;_ENV[&amp;quot;DOCKER_MYSQL_1_PORT_3306_TCP_ADDR&amp;quot;]&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;172.17.0.22&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;_ENV[&amp;quot;DOCKER_MYSQL_1_PORT_3306_TCP_PROTO&amp;quot;]&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;tcp&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;_ENV[&amp;quot;MYSQL_1_PORT_3306_TCP_PROTO&amp;quot;]&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;tcp&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;_ENV[&amp;quot;MYSQL_1_PORT_3306_TCP_PORT&amp;quot;]&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;3306&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Ces variables proviennent du lien que nous avons effectué dans docker-compose.yml. Cela va vous permettre de modifier la configuration de vos scripts ou du framework en utilisant la fonction php &amp;ldquo;getenv()&amp;rdquo; pour spécifier où se trouve le serveur mysql. Notez que docker permet aussi au conteneur qui reçoit le link d&amp;rsquo;utiliser le nom de conteneur en tant que nom de machine.&lt;/p&gt;
&lt;p&gt;Or, docker-compose renomme les conteneurs en les suffixant avec un numéro (car docker-compose sait démarrer plusieurs conteneurs d&amp;rsquo;une même entrée). Il faudra alors tricher un peu et utiliser, par exemple, &amp;ldquo;mysql_1&amp;rdquo; au lieu de &amp;ldquo;mysql&amp;rdquo;. Je rappelle que &amp;ldquo;mysql&amp;rdquo; est le nom donné dans le fichier yaml, et non pas le nom de l&amp;rsquo;image.&lt;/p&gt;
&lt;p&gt;En d&amp;rsquo;autres termes, vous utiliserez:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php
//via les variables d&#39;environnement
$mysql_server = getenv(&amp;quot;DOCKER_MYSQL_1_PORT_3306_TCP_ADDR&amp;quot;);

//ou
// en admettant qu&#39;on puisse utiliser un nom de machine
// ce qui est vrai pour le module php-mysql
$mysql_server = &amp;quot;mysql_1&amp;quot;; 


//
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;ne-pas-perdre-la-base-de-donnée-&#34;&gt;Ne pas perdre la base de donnée ?&lt;/h1&gt;
&lt;p&gt;Effectivement dans notre configuration on ne sauve pas la base localement. Si vous supprimez le conteneur mysql, vous perdez vos données. On règle le souci:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apache:
    image: php:apache
    volumes:
    - &amp;quot;src/:/var/www/html&amp;quot;
    links:
    - mysql
    ports:
    - &amp;quot;8080:80&amp;quot;
    privileged: true

mysql:
    image: mysql:latest
    volumes:
    - &amp;quot;data/:/var/lib/mysql&amp;quot;
    environment:
        MYSQL_ROOT_PASSWORD: passroot
    privileged: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On a rajouté un volume à notre conteneur mysql, désormais le répertoire &lt;code&gt;/var/lib/mysql&lt;/code&gt; où mysql stocke toutes ses bases sera sur notre répertoire local &amp;ldquo;data&amp;rdquo;. Pensez à le créer avant.&lt;/p&gt;
&lt;p&gt;Et encore une fois, &amp;ldquo;privileged: true&amp;rdquo; si vous êtes sous Fedora ou une distribution qui utilise SELinux et qui refuse l&amp;rsquo;écriture depuis le conteneur.&lt;/p&gt;
&lt;h1 id=&#34;bilan&#34;&gt;Bilan:&lt;/h1&gt;
&lt;p&gt;En un seul fichier &amp;ldquo;docker-compose.yml&amp;rdquo; on peut aisément démarrer un développement PHP/MySQL sans trop d&amp;rsquo;effort, encore faut-il être sur Linux et avoir Docker d&amp;rsquo;installer. Mais vue la tendance du moment&amp;hellip;&lt;/p&gt;
&lt;p&gt;Intérêt certain, il est possible de prendre cette installation, de la déployer sur un serveur de production (même si Docker dit qu&amp;rsquo;ils sont pas encore prêt pour de la production&amp;hellip;) et lancer docker-compose en tant que service. Restera alors à votre charge de faire un reverse-proxy (nginx, apache&amp;hellip;) qui pointera sur localhost:8080&lt;/p&gt;
&lt;p&gt;Vous n&amp;rsquo;aurez donc pas à installer php et mysql, et vous pourrez aisément mettre en production plusieurs sites avec différentes versions de PHP.&lt;/p&gt;
&lt;p&gt;Voilà comment on passe de LAMP à DAMP :)&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Golang, résoudre le souci d&#39;indexation de type défini</title>
      <link>https://www.metal3d.org/blog/2015/golang-r%C3%A9soudre-le-souci-dindexation-de-type-d%C3%A9fini/</link>
      <pubDate>Sat, 19 Sep 2015 14:04:46 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/golang-r%C3%A9soudre-le-souci-dindexation-de-type-d%C3%A9fini/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Golang permet de créer ses prores types et notamment de faire un alias de &amp;ldquo;map&amp;rdquo;, mais un jour, en voulant récupérer une valeur indexée dans le map, vous recevez un fameux &amp;ldquo;type *Foo does not support indexing&amp;rdquo; lors de la compilation. Ce souci peut-être rapidement réglé à condition de comprendre pourquoi et comment cette erreur arrive.&lt;/p&gt;
&lt;h1 id=&#34;tldr&#34;&gt;TL;DR&lt;/h1&gt;
&lt;p&gt;Vous vous trompez ! Un map est déjà un pointeur, donc arrêtez d&amp;rsquo;attendre un pointeur sur ce type. Changez votre fonction pour attendre &amp;ldquo;Foo&amp;rdquo; et non &amp;ldquo;*Foo&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Si vraiment vous voulez utiliser un pointeur sur pointeur, il faut déréférencer, c&amp;rsquo;est à dire: utiliser &lt;code&gt;(*var)[&amp;quot;index&amp;quot;]&lt;/code&gt; au lieu &lt;code&gt;var[&amp;quot;index&amp;quot;]&lt;/code&gt; dans votre fonction&lt;/p&gt;
&lt;h1 id=&#34;explication-détaillée&#34;&gt;Explication détaillée&lt;/h1&gt;
&lt;h2 id=&#34;go-serait-il-aussi-chiant-que-cc-alors-&#34;&gt;Go serait-il aussi chiant que C/C++ alors ?&lt;/h2&gt;
&lt;p&gt;En gros, que ce soit en C/C++ ou en Go, les tableaux sont des pointeurs. Le fait de définir un type qui surcharge un tableau (map, slice, etc&amp;hellip;) prête à confusion.&lt;/p&gt;
&lt;p&gt;Imaginons un type Foo qui surcharge un &lt;code&gt;map[string]interface{}&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;type Foo map[string]interface{}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Un fonction qui utiliserai ce type pourrait être:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;func Modify(f *Foo) {
    //... fait des choses 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le souci est que &amp;ldquo;f&amp;rdquo; est un pointeur sur un map, donc un pointeur sur un pointeur&amp;hellip; et la modification (expliquée dans la seconde partie) demande un déréférencement. C&amp;rsquo;est-à-dire &amp;ldquo;(*f)[&amp;hellip;]&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;En réalité, la fonction devrait être:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;func Modify(f Foo) {
    //... fait des choses 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, &amp;ldquo;f&amp;rdquo; est un pointeur puisque c&amp;rsquo;est un &amp;ldquo;map&amp;rdquo;. Donc:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;package main

import &amp;quot;fmt&amp;quot;

type Foo map[string]interface{}

func Modify(f Foo) {
	f[&amp;quot;hello&amp;quot;] = &amp;quot;world&amp;quot;
}

func main() {
	a := make(Foo)
	Modify(a)
	fmt.Println(a)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On teste:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go prog.go
map[hello:world]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Cela fonctionne, car &amp;ldquo;Foo&amp;rdquo; est un type &amp;ldquo;pointeur&amp;rdquo;&lt;/strong&gt; de par le fait qu&amp;rsquo;il est un &amp;ldquo;map&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;cas-dun-pointeur-sur-pointeur&#34;&gt;Cas d&amp;rsquo;un pointeur sur pointeur&lt;/h2&gt;
&lt;p&gt;Dans certains cas, on se force à uiliser un pointeur sur le type défini, même se celui-ci est un map. Cela va induire que la fonction qui récupère un &amp;ldquo;*Foo&amp;rdquo; récupère un pointeur sur pointeur, puisque c&amp;rsquo;est un pointeur sur un map et qu&amp;rsquo;un map est un poitneur&amp;hellip; Vous suivez ?&lt;/p&gt;
&lt;p&gt;Prenons un exemple simple.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;package main

import &amp;quot;fmt&amp;quot;

// type alias pour un map[string]interface{}
type Foo map[string]interface{}

// affiche l&#39;index &amp;quot;test&amp;quot;
func Test(f *Foo) {
    // l&#39;erreur arrive ici:
    if v, ok := f[&amp;quot;test&amp;quot;]; ok {
        fmt.Println(v)    
    }    
}

func main() {
    // on crée notre variable Foo
	a := make(Foo)
	// on assigne une valeur à l&#39;index &amp;quot;test&amp;quot;
	a[&amp;quot;test&amp;quot;] = &amp;quot;hello&amp;quot;
	// on l&#39;affiche
	Test(&amp;amp;a)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go run prog.go
prog.go:11: invalid operation: f[&amp;quot;test&amp;quot;] (type *Foo does not support indexing)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le problème est simple. La variable &amp;ldquo;f&amp;rdquo; est ici un pointeur. &lt;strong&gt;Comprennez bien que &amp;ldquo;f&amp;rdquo; est donc une adresse !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Et comme à cette adresse on trouve&amp;hellip; un autre adresse - car la valeur est un map et qu&amp;rsquo;un map correspond à une adresse&amp;hellip; houlalala&amp;hellip; ça se complique&amp;hellip;&lt;/p&gt;
&lt;p&gt;Bon..:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;f&lt;/code&gt; est de type Foo&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Foo&lt;/code&gt; est un map, donc un pointeur&lt;/li&gt;
&lt;li&gt;&lt;code&gt;f *Foo&lt;/code&gt; =&amp;gt; f est donc un pointeur sur un pointeur&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, la réponse est simple, il faut &lt;strong&gt;déréférencer&lt;/strong&gt; la variable, ou pour être plus près de la vérité, il faut &amp;ldquo;utiliser la valeur pointée&amp;rdquo; par l&amp;rsquo;adresse récupérée dans la fonction.&lt;/p&gt;
&lt;p&gt;Ce que je veux dire c&amp;rsquo;est que &amp;ldquo;f&amp;rdquo; étant une adresse, si on récupère la valeur à cette adresse, on tombe sur le &amp;ldquo;map&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Donc dans la fonction &amp;ldquo;Test&amp;rdquo; on va utiliser &amp;ldquo;&lt;code&gt;(*f)&lt;/code&gt;&amp;rdquo; pour retrouver le map&amp;hellip; Ça va ?&lt;/p&gt;
&lt;p&gt;En clair:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-golang&#34;&gt;package main

import &amp;quot;fmt&amp;quot;

// type alias pour un map[string]interface{}
type Foo map[string]interface{}

// affiche l&#39;index &amp;quot;test&amp;quot;
func Test(f *Foo) {
    // On corrige, on utilise maintenant &amp;quot;la valeur pointée par f&amp;quot;
    if v, ok := (*f)[&amp;quot;test&amp;quot;]; ok {
        fmt.Println(v)    
    }    
}

func main() {
    // on crée notre variable Foo
	a := make(Foo)
	// on assigne une valeur à l&#39;index &amp;quot;test&amp;quot;
	a[&amp;quot;test&amp;quot;] = &amp;quot;hello&amp;quot;
	// on l&#39;affiche
	Test(&amp;amp;a)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette foi:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go run prog.go
hello
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;et-pourquoi-pas-ftest-&#34;&gt;et pourquoi pas &amp;ldquo;&lt;code&gt;*f[&amp;quot;test&amp;quot;]&lt;/code&gt;&amp;rdquo; ?&lt;/h1&gt;
&lt;p&gt;Simplement parce que &lt;code&gt;*f[&amp;quot;test&amp;quot;]&lt;/code&gt; veut dire &amp;ldquo;valeur pointée à l&amp;rsquo;adresse &lt;code&gt;f[&amp;quot;test&amp;quot;]&lt;/code&gt;&amp;rdquo; - et c&amp;rsquo;est pas ce que nous voulons.&lt;/p&gt;
&lt;p&gt;Alors que &amp;ldquo;&lt;code&gt;(*f)[&amp;quot;test&amp;quot;]&lt;/code&gt;&amp;rdquo; veut dire &amp;ldquo;prend la valeur pointée en f (donc on a le map) et récupère dans ce map l&amp;rsquo;index &amp;rsquo;test&amp;rsquo;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Sémantiquement, c&amp;rsquo;est différent.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Git et les fichiers zip</title>
      <link>https://www.metal3d.org/blog/2015/git-et-les-fichiers-zip/</link>
      <pubDate>Sat, 01 Aug 2015 14:07:40 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/git-et-les-fichiers-zip/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Si un projet se base sur un fichier zip (pas seulement les fichier &amp;ldquo;.zip&amp;rdquo;, mais tout fichier qui zipe le contenu comme les fichiers libreoffice, openoffice, vym, etc&amp;hellip;) alors vous allez vous confronter au souci de versionnement par Git. Car celui-ci les gère comme étant des fichiers binaires. Or vous voulez versionner le contenu. Et bien voilà ma méthode (certes pas des plus jolies mais ça marche)&lt;/p&gt;
&lt;p&gt;Je me suis trouvé confronté à cette situation en voulant versionner un livre sur le langage Go que je tape depuis pas mal de temps. Jusqu&amp;rsquo;ici je versionnais le fichier &amp;ldquo;.vym&amp;rdquo; (car j&amp;rsquo;utilise vym pour ça, je parle bien de View Your Mind hein, mais j&amp;rsquo;en reparelarai bientôt de tout ça) qui est ni plus ni moins qu&amp;rsquo;un fichier zippé et renommé en &amp;ldquo;.vym&amp;rdquo;. Or, je me suis fais une bonne frayeur en écransant &lt;em&gt;connement&lt;/em&gt; mon fichier après un &amp;ldquo;&lt;code&gt;git pull&lt;/code&gt;&amp;rdquo; trop rapidement validé.&lt;/p&gt;
&lt;p&gt;Résultat, git a écrasé le fichier sans faire de &amp;ldquo;diff&amp;rdquo; - ce qui est &lt;strong&gt;logique&lt;/strong&gt; car il ne le fait pas pour les fichier binaires.&lt;/p&gt;
&lt;p&gt;Bref, pour éviter ce genre de boulette je me suis simplement ateler à versionner &lt;strong&gt;non plus le fichier, mais son contenu&lt;/strong&gt;. Ça vous parait peut-être barbarre mais ça marche.&lt;/p&gt;
&lt;p&gt;Premier chose à faire, on teste !&lt;/p&gt;
&lt;h1 id=&#34;testons-la-construction&#34;&gt;Testons la construction&lt;/h1&gt;
&lt;p&gt;On commence par se placer dans un dossier et on va décompresser le contenu du fichier (vous pouvez le faire avec un fichier .odt, un .vym ou un .docx si vous êtes nostalgique de Word&amp;hellip;)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir ~/test/constructor &amp;amp;&amp;amp; cd ~/test/constructor
unzip /chemin/vers/fichier.vym
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On voit alors quelques fichiers et répertoires, on reconstruit:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;zip -r test ./*
mv test.zip ../fichier-reconstruit.vym
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et on ouvre ce fichier (placé dans &amp;ldquo;&lt;code&gt;~/test/&lt;/code&gt;&amp;rdquo;). Tout à l&amp;rsquo;air ok ! Bien on passe à la suite.&lt;/p&gt;
&lt;h1 id=&#34;automatison&#34;&gt;Automatison&lt;/h1&gt;
&lt;p&gt;Dans mon répertoire git, j&amp;rsquo;ai donc créé un Makefile, et voilà ce que j&amp;rsquo;ai entré:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;
FILE=&amp;quot;monfichier.vym&amp;quot;

explode:
    mkdir src/ || :
    cd src &amp;amp;&amp;amp; unzip ../$(FILE)
    
implode:
    cd src &amp;amp;&amp;amp; zip -r ../temp ./*
    mv temp.zip $(FILE)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ainsi, quand je tape &amp;ldquo;&lt;code&gt;make explode&lt;/code&gt;&amp;rdquo; le contenu de mon projet est décompressé dans le répertoire &amp;ldquo;&lt;code&gt;src&lt;/code&gt;&amp;rdquo; que je vais pouvoir versionner. Et quand je récupère le projet depuis &amp;ldquo;git&amp;rdquo;  (&amp;quot;&lt;code&gt;git pull&lt;/code&gt;&amp;quot;) alors je tape simplement &amp;ldquo;&lt;code&gt;make implode&lt;/code&gt;&amp;rdquo; pour reconstruire mon fichier.&lt;/p&gt;
&lt;h1 id=&#34;coté-git&#34;&gt;Coté git&lt;/h1&gt;
&lt;p&gt;Coté git, &lt;strong&gt;je ne sauvegarde pas le fichier de travail&lt;/strong&gt; (ici mon fichier &amp;ldquo;.vym&amp;rdquo;) car il va être reconstruit par le makefile. Donc mon fichier .gitignore est de la forme:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;*.vym
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Du coup, je suis tranquile, et surtout que la suite des opérations va engendrer une création de fichiers de sauvgarde, par conséquent on les ignorera aussi puisqu&amp;rsquo;ils utiliseront l&amp;rsquo;extension &amp;ldquo;.vym&amp;rdquo; (encore une fois, adaptez mes exemples à votre type de fichier)&lt;/p&gt;
&lt;p&gt;Maintenant&amp;hellip; les problèmes commencent&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;les-problèmes-et-les-solutions&#34;&gt;Les problèmes et les solutions&lt;/h1&gt;
&lt;p&gt;Il se peut que votre &amp;ldquo;fichier zip&amp;rdquo; ait besoin de répertoires vides, par exemple Vym demande un répertoire &amp;ldquo;flags&amp;rdquo; et un autre &amp;ldquo;images&amp;rdquo; non vide. Or, git ne les versionne pas. Git ne peut gérer que des fichiers en fonction de leur chemin, et par conséquent un répertoire &amp;ldquo;vide&amp;rdquo; n&amp;rsquo;est pas un fichier, il va le zapper.&lt;/p&gt;
&lt;p&gt;La solution, est simplement de créer des fichiers vides dans ces répertoires. Ainsi dans le Makefile, on va créer une cible de préparation &amp;ldquo;_prep&amp;rdquo; (le underscore pour éviter de le faire apparaitre dans l&amp;rsquo;autocompletion de bash) qui va écrire des fichiers nommés &amp;ldquo;.gitkeep&amp;rdquo; seulement dans les répertoires de projet (en évitant donc .git). Je remercie cette réponse de &lt;a href=&#34;http://stackoverflow.com/a/31249949/1472048&#34;&gt;StackOverFlow&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;
FILE=&amp;quot;monfichier.vym&amp;quot;

explode: _explode _prep

_explode:
    mkdir src/ || :
    cd src &amp;amp;&amp;amp; unzip ../$(FILE)
    
implode:
    cd src &amp;amp;&amp;amp; zip -r ../temp ./*
    mv temp.zip $(FILE)

_prep:
    find . -type d -empty -not -path &amp;quot;./.git/*&amp;quot; -exec touch \{\}/.gitkeep \;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;J&amp;rsquo;ai décalé la cible &amp;ldquo;explode&amp;rdquo; en &amp;ldquo;_explode&amp;rdquo; pour changer l&amp;rsquo;ordre d&amp;rsquo;appel. Si vous suivez bien le Makefile, &amp;ldquo;&lt;code&gt;make explode&lt;/code&gt;&amp;rdquo; va appeler &amp;ldquo;_explode&amp;rdquo; qui va decompresser le zip, puis &amp;ldquo;_prep&amp;rdquo; qui prépare les répertoires en &amp;ldquo;touchant&amp;rdquo; des fichier &amp;ldquo;.gitkeep&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Autre chose, j&amp;rsquo;ai décidé de sauvegarder mon fichier de travail &lt;strong&gt;avant&lt;/strong&gt; de le recréer. On ajoute une dernière règle nommée &amp;ldquo;_backup&amp;rdquo; qui copie le fichier en le datant à la seconde.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ajoute aussi un peu de propreté en séparant le nom de fichier et son extension.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;
BASE:=&amp;quot;monfichier&amp;quot;
EXT:=&amp;quot;.vym&amp;quot;
FILE:=$(BASE)$(EXT)
DATE=$(shell date &amp;quot;+%Y%m%d-%H%M%S&amp;quot;)

explode: _explode _prep

_explode:
    mkdir src/ || :
    cd src &amp;amp;&amp;amp; unzip ../$(FILE)
    
implode: _backup
    cd src &amp;amp;&amp;amp; zip -r ../temp ./*
    mv temp.zip $(FILE)

_prep:
    find . -type d -empty -not -path &amp;quot;./.git/*&amp;quot; -exec touch \{\}/.gitkeep \;

_backup:
    cp $(FILE) $(BASE)-$(DATE)$(EXT)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comme ça, chaque fois que j&amp;rsquo;appelle &amp;ldquo;&lt;code&gt;make implode&lt;/code&gt;&amp;rdquo; pour reconstruire mon fichier &amp;ldquo;vym&amp;rdquo;, j&amp;rsquo;ai une sauvegarde de faite.&lt;/p&gt;
&lt;p&gt;Petite parenthèse, dans un makefile on peut assigner une variable en utilisant &amp;ldquo;:=&amp;rdquo; ou &amp;ldquo;=&amp;rdquo;. La différence est que &amp;ldquo;:=&amp;rdquo; var interpréter &amp;ldquo;de suite&amp;rdquo; la valeur à assigner; alors que &amp;ldquo;=&amp;rdquo; attendra qu&amp;rsquo;on appelle la variable pour que sa valeur soit interprétée. Du coup, ici, la variable &amp;ldquo;$(DATE)&amp;rdquo; sera interprété au moment de l&amp;rsquo;appel dans &amp;ldquo;_backup&amp;rdquo;. C&amp;rsquo;est pas super utile ici mais par habitude&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;et-dans-la-vie-réelle-&#34;&gt;Et dans la vie réelle ?&lt;/h1&gt;
&lt;p&gt;Dans la vie réelle, j&amp;rsquo;ai testé et ça se passe bien ! Voici ce qu&amp;rsquo;il peut arriver (et qui d&amp;rsquo;ailleurs m&amp;rsquo;ait arrivé):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je prend mon pc portable et je commence à toucher mon fichier vym&lt;/li&gt;
&lt;li&gt;je suis content, je me dis que je peux pousser tout ça sur mon serveur git&lt;/li&gt;
&lt;li&gt;je lance &amp;ldquo;&lt;code&gt;make explode&lt;/code&gt;&amp;rdquo; puis &amp;ldquo;&lt;code&gt;git commit src &amp;amp;&amp;amp; git push&lt;/code&gt;&amp;rdquo; et là&amp;hellip; &lt;strong&gt;le drame !&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Git me dit que je suis pas à jour ! ça veut dire que j&amp;rsquo;ai oublié de faire un &amp;ldquo;pull&amp;rdquo; avant de pousser. Effectivement, la veille j&amp;rsquo;ai travaillé sur mon pc de bureau et j&amp;rsquo;avais poussé. Mais tête en l&amp;rsquo;ait que je suis, j&amp;rsquo;ai oublié cette scéance de boulot.&lt;/p&gt;
&lt;p&gt;Il va se passer quoi ?&lt;/p&gt;
&lt;p&gt;En premier lieu, je pull ! et je reconstruit mon fichier avec &amp;ldquo;&lt;code&gt;make implode&lt;/code&gt;&amp;rdquo;. Vym recharge mon fichier. Deux possibilités:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ça a l&amp;rsquo;air pas mal - auquel cas on peut refaire un &amp;ldquo;&lt;code&gt;make explode&lt;/code&gt;&amp;rdquo; et pousser les le merge.&lt;/li&gt;
&lt;li&gt;Tout est pété - là on transpire&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si tout est pété, comme on a été intelligent, on a une sauvegarde qui a été créé lors du &amp;ldquo;&lt;code&gt;make implode&lt;/code&gt;&amp;rdquo; dont le nom est la date d&amp;rsquo;aujourd&amp;rsquo;hui + l&amp;rsquo;heure de sauvegarde. Je peux reprendre cette version, me débrouiller pour reconstruire le fichier et commiter.&lt;/p&gt;
&lt;p&gt;Au pire, on peut toucher le fichier XML contenu dans &amp;ldquo;src&amp;rdquo; car, je vous le rappelle, &lt;em&gt;on travaille désormais avec des fichiers non compressés&lt;/em&gt; ce qui a pour effet de vous donner les &amp;ldquo;diffs&amp;rdquo;, voir les lignes qui ont sautées, etc.&lt;/p&gt;
&lt;p&gt;Donc en clair, ma méthode de décompression + makefile qui fait le travaille de sauvegarde à ma place, ça me sauve des heures de travail et ma tension me remercie.&lt;/p&gt;
&lt;p&gt;Notez aussi que cette technique peut s&amp;rsquo;appliquer aux fichier &amp;ldquo;.tar&amp;rdquo;, &amp;ldquo;.tar.gz&amp;rdquo; ou encore &amp;ldquo;.7z&amp;rdquo;. Le tout étant simplement de s&amp;rsquo;adapter au format. Le principe, je vous le rapelle, étant de versionner le contenu du fichier compressé et non pas le fichier compressé lui-même.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;autres solutions ? d&amp;rsquo;autres méthodes ? faites le moi savoir !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Neovim va-t-il remplacer Vim ?</title>
      <link>https://www.metal3d.org/blog/2015/neovim-va-t-il-remplacer-vim-/</link>
      <pubDate>Mon, 06 Jul 2015 22:40:50 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/neovim-va-t-il-remplacer-vim-/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Lorsque &lt;a href=&#34;http://www.nicolasengel.fr/&#34;&gt;Nicolas Engel&lt;/a&gt; m&amp;rsquo;a parlé de &lt;a href=&#34;http://neovim.io/&#34;&gt;neovim&lt;/a&gt;, je n&amp;rsquo;ai pas eut l&amp;rsquo;occasion de tester étant donné que j&amp;rsquo;étais sur Fedora 20 et qu&amp;rsquo;aucun paquet n&amp;rsquo;avait été fourni. Mais depuis mon passage en Fedora 22, j&amp;rsquo;ai accès à &lt;a href=&#34;https://copr.fedoraproject.org/&#34;&gt;Copr&lt;/a&gt; et donc à &lt;a href=&#34;https://copr.fedoraproject.org/coprs/dperson/neovim/&#34;&gt;un build de neovim&lt;/a&gt;. Voilà mon avis: Whaouuu !&lt;/p&gt;
&lt;p&gt;Début des hostilités: je passe à Fedoda 22 et je rage de voir le terminal &amp;ldquo;Terminator&amp;rdquo; ne pas me donner de notification quand une commande se termine en arrière plan. Résultat des courses je reviens à ce bon vieux &amp;ldquo;gnome-terminal&amp;rdquo; qui, finalement, est pas si mal. Mais j&amp;rsquo;utilise Vim depuis des lustres, et j&amp;rsquo;adorais avoir un split de terminal pour avoir d&amp;rsquo;un coté l&amp;rsquo;éditeur, et de l&amp;rsquo;autre un terminal pour lancer une commande ou laisser les tests unitaires tourner.&lt;/p&gt;
&lt;p&gt;Bref, je trouve un plugin pas dégueulasse: &lt;a href=&#34;https://github.com/vim-scripts/Conque-Shell&#34;&gt;Conque&lt;/a&gt; - sérieusement, pour ceux qui ne veulent pas aller plus loin et passer à Neovim, utilisez ce plugin c&amp;rsquo;est tout bonnement génial.&lt;/p&gt;
&lt;p&gt;Mais voilà, je me souvenais que neovim permettait directement d&amp;rsquo;avoir un terminal dans l&amp;rsquo;éditeur. Alors je retourne sur le projet Git et je vois que l&amp;rsquo;installation sur Fedora se fait en un rien de temps, et ce grâce à Copr.&lt;/p&gt;
&lt;h1 id=&#34;installation&#34;&gt;Installation&lt;/h1&gt;
&lt;p&gt;Ça va aller vite:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo dnf copr enable dperson/neovim
sudo dnf install neovim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà&amp;hellip; c&amp;rsquo;est tout!&lt;/p&gt;
&lt;p&gt;Comme le spécifie la doc: &lt;a href=&#34;https://copr.fedoraproject.org/coprs/dperson/neovim/&#34;&gt;https://copr.fedoraproject.org/coprs/dperson/neovim/&lt;/a&gt; - vous pouvez lier votre &amp;ldquo;&lt;code&gt;.vimrc&lt;/code&gt;&amp;rdquo; et votre &amp;ldquo;&lt;code&gt;.vim&lt;/code&gt;&amp;rdquo; respectivement sur &amp;ldquo;&lt;code&gt;.nvimrc&lt;/code&gt;&amp;rdquo; et &amp;ldquo;&lt;code&gt;.nvim&lt;/code&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Reste à installer les module python:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install --user neovim
pip3 install --user neovim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comme ça on est tranquile pour les version de python 2.7.x et 3.x, les plugins vous diront merci plus tard.&lt;/p&gt;
&lt;h1 id=&#34;alors-ça-change-quoi-&#34;&gt;Alors ça change quoi ?&lt;/h1&gt;
&lt;p&gt;Au premier regard, absolument rien. Mais alors rien !&lt;/p&gt;
&lt;p&gt;Si, à la limite, la commande pour lancer neovim:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nvim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous avez un éditeur vim comme d&amp;rsquo;habitude, vous vous demandez presque pourquoi vous l&amp;rsquo;avez installé. Et puis vous tapez&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:terminal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là, vous comprennez déjà que rien que pour ça vous allez adorer NeoVim.&lt;/p&gt;
&lt;p&gt;Avoir un terminal dans nvim me sert absolument à tout, entre lancer des tests ou démarrer &amp;ldquo;Gdb&amp;rdquo;, en passant par un &amp;ldquo;htop&amp;rdquo; dans un coin&amp;hellip; bref, plus besoin d&amp;rsquo;avoir un Terminator.&lt;/p&gt;
&lt;p&gt;Truc qui me fait délier: lancer &amp;ldquo;&lt;code&gt;mutt&lt;/code&gt;&amp;rdquo; sur le coté pour avoir les mails pendant que je code :) Et j&amp;rsquo;en passe, comme weechat par exemple. En bref &lt;strong&gt;tous ce qui se lance dans un terminal&lt;/strong&gt; est accessible dans nvim. C&amp;rsquo;est un bonheur sans nom.&lt;/p&gt;
&lt;p&gt;Puisque la touche &amp;ldquo;echap&amp;rdquo; est nécessaire pour certains logiciels dans le terminal, il a fallut changer de raccourci pour quitter le mode &amp;ldquo;term&amp;rdquo;. Pour reprendre la main et passer en mode &amp;ldquo;normal&amp;rdquo;, pressez CTRL+\ puis (rapidement) CTRL+n ou simplement maintenez la touche Control et pressez &amp;ldquo;&amp;quot; puis &amp;ldquo;n&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Pour ceux qui parle courament le vim: &amp;ldquo;&lt;code&gt;&amp;lt;C-\&amp;gt;&amp;lt;C-n&amp;gt;&lt;/code&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Libre à vous de splitter les fenêtres, lancer des commandes, utiliser le mode &amp;ldquo;visual&amp;rdquo; pour copier des résultats de commandes dans un buffer, bref le pied. (Je rappelle la commande pour spliter: Control+W puis &amp;ldquo;s&amp;rdquo; ou Control+w puis &amp;ldquo;v&amp;rdquo; pour le split vertical)&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//static/upload/paste-d18659f6-dfbc-4a80-9ca9-13cb114169e1.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;quoi-dautre&#34;&gt;Quoi d&amp;rsquo;autre&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Autre petit truc que j&amp;rsquo;adore, lors d&amp;rsquo;une recherche en presant &amp;ldquo;/&amp;rdquo;, le buffer scrolle durant la frappe de recherche. C&amp;rsquo;est tout bête mais c&amp;rsquo;est très confortable.&lt;/p&gt;
&lt;p&gt;Pour le reste je vous laisse taper:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:help vim-differences
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On notera des options par défaut (comme le mode &amp;ldquo;mouse=a&amp;rdquo;) et les options de compatibilité avec les anciennes versions de &amp;ldquo;vi&amp;rdquo; supprimées. En clair, on fait table rase.&lt;/p&gt;
&lt;p&gt;Neovim est aussi plus léger, il a toutes les features compilées (alors que vim doit être compilé en mode &amp;ldquo;HUGE&amp;rdquo; pour avoir accès à toutes les features) et il promet aussi pour la suite: une meilleure intégration dans n&amp;rsquo;importe quel GUI, support de pleins de langages, plus rapide, plus proche des dernières configurations, bref NeoVim donne envie.&lt;/p&gt;
&lt;p&gt;Attention toutefois, NeoVim n&amp;rsquo;est pas &amp;ldquo;stable&amp;rdquo; (perso j&amp;rsquo;ai eu aucun crash pour le moment, et pourtant j&amp;rsquo;ai une foule de plugins, je code en Go dedans, j&amp;rsquo;ai un tagbar et un nerdtree en boucle&amp;hellip;), et rien ne dit que la version Copr maintenue par &amp;ldquo;dperson&amp;rdquo; soit viable longtemps. Bref, vous prennez vos responsabilités en le testant.&lt;/p&gt;
&lt;h1 id=&#34;alors-le-verdict-&#34;&gt;Alors, le verdict ?&lt;/h1&gt;
&lt;p&gt;Digne successeur de Vim, ou plutôt &amp;ldquo;nouvelle génération&amp;rdquo; de vim, je pense que NeoVim va faire du bruit dans quelques mois. Le projet est développé depuis longue date et les commits sont réguliers, donc la communauté est active et le projet se stabilise. Il est clair que Vim (déjà lui-même un remplaçant de &amp;ldquo;vi&amp;rdquo;) commence à vieillir. Un nouveau souffle avec ce projet va certainement donner de nouvelles perspectives.&lt;/p&gt;
&lt;p&gt;En tout cas, pour moi, c&amp;rsquo;est &amp;ldquo;&lt;code&gt;alias vim=nvim&lt;/code&gt;&amp;rdquo;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Assigner une variable lors de la compilation en Go</title>
      <link>https://www.metal3d.org/blog/2015/assigner-une-variable-lors-de-la-compilation-en-go/</link>
      <pubDate>Sun, 21 Jun 2015 15:57:57 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/assigner-une-variable-lors-de-la-compilation-en-go/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Je viens de faire une release de mon outil &lt;a href=&#34;https://github.com/metal3d/idok/releases&#34;&gt;idok&lt;/a&gt; et je me suis posé cette fameuse question: comment faire pour assigner la version dans mon binaire sans avoir à modifier le code source ? Et bien c&amp;rsquo;est simple comme tout mais il faut le savoir. Voilà la méthode.&lt;/p&gt;
&lt;p&gt;Prenons un exemple de programme dont vous avez assigné une valeur de version et qui l&amp;rsquo;affiche:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
    &amp;quot;fmt&amp;quot;
)

const VERSION=&amp;quot;heuuuu&amp;quot;

func main(){
    fmt.Println(VERSION)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le résultat est logiquement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ go run main.go
heuuu
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Très bien&amp;hellip; sauf que là c&amp;rsquo;est &amp;ldquo;en dur&amp;rdquo; et qu&amp;rsquo;à chaque release je dois penser à monter la version dans le source. Un peu de recherche et je comprends qu&amp;rsquo;on peut modifier les drapeaux &amp;ldquo;ld&amp;rdquo;. En gros, on peut &amp;ldquo;modifier les variables au moment de l&amp;rsquo;édition de lien&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attention, on ne parle pas de &amp;ldquo;ld&amp;rdquo;, l&amp;rsquo;éditeur de lien GNU, mais de celui de Go !&lt;/strong&gt;, voir &lt;a href=&#34;https://golang.org/cmd/ld/&#34;&gt;https://golang.org/cmd/ld/&lt;/a&gt; et on voit une ligne très intéressante:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;-X symbol value&lt;/p&gt;
&lt;p&gt;Set the value of a string variable. The symbol name
should be of the form importpath.name, as displayed
in the symbol table printed by &amp;ldquo;go tool nm&amp;rdquo;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Et c&amp;rsquo;est la variable &amp;ldquo;main.VERSION&amp;rdquo; qu&amp;rsquo;on va changer, c&amp;rsquo;est à dire &amp;ldquo;la variable VERSION dans le package main&amp;rdquo;. On va utiliser le drapeau &amp;ldquo;X&amp;rdquo; de la librairie &amp;ldquo;ld&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Cela dit vous devez penser à utiliser &lt;strong&gt;une variable et non une constante&lt;/strong&gt;. Donc on modifie le programme en :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
    &amp;quot;fmt&amp;quot;
)

// on met une variable
var VERSION=&amp;quot;heuuuu&amp;quot;

func main(){
    fmt.Println(VERSION)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et on va tenter:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ go run main.go
heuuu
$ go run -ldflags &#39;-X main.VERSION 0.0.1&#39; main.go
0.0.1
$ go run -ldflags &#39;-X main.VERSION alpha1&#39; main.go
alpha1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Du coup, tout devient plus simple. On peut utiliser un Makefile qui utilise, par exemple, git pour avoir le tag en cours:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;VERSION:=$(shell git describe --tags)
OPTS:=-ldflags &#39;-X main.VERSION $(VERSION)&#39;

build:
    go build $(OPTS) main.go
    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reste alors à changer &amp;ldquo;main.go&amp;rdquo; pour ajouter la possibilité d&amp;rsquo;avoir une option &lt;code&gt;-version&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
    &amp;quot;flag&amp;quot;
    &amp;quot;fmt&amp;quot;
    &amp;quot;os&amp;quot;
)

// on met une variable - UNDEF 
var VERSION=&amp;quot;UNDEF&amp;quot;

func main(){
    v := flag.Bool(&amp;quot;version&amp;quot;, false, &amp;quot;get the current version:&amp;quot;+ VERSION )
    flag.Parse()
    
    if *version {
        fmt.Println(VERSION)
        os.Exit(0)
    }
    
    fmt.Println(&amp;quot;No version flag set...&amp;quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et donc:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ git tag 0.0.1
$ make
$ ./main -version
0.0.1
$ git tag 0.0.2
$ make
$ ./main -version
0.0.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quand je vous disais que c&amp;rsquo;était simple&amp;hellip;&lt;/p&gt;
&lt;p&gt;Juste une petite note, je n&amp;rsquo;ai pas trouvé comment assigner autre chose qu&amp;rsquo;une chaine de caractères&amp;hellip; si vous avez la solution, merci de me prévenir que je modifie le billet.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Rendre homogène une équipe de dev avec docker et docker-compose</title>
      <link>https://www.metal3d.org/blog/2015/rendre-homog%C3%A8ne-une-%C3%A9quipe-de-dev-avec-docker-et-docker-compose/</link>
      <pubDate>Sun, 14 Jun 2015 12:28:11 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/rendre-homog%C3%A8ne-une-%C3%A9quipe-de-dev-avec-docker-et-docker-compose/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Quand on veut bosser avec Docker sur des projets plus ou moins complexes, il existe un outil qui permet de vous soulager de pas mal de contraintes: le bien nommé &lt;a href=&#34;https://docs.docker.com/compose/&#34;&gt;&amp;ldquo;docker-compose&amp;rdquo;&lt;/a&gt; anciennement nommé &amp;ldquo;fig&amp;rdquo;. Bien plus qu&amp;rsquo;un &amp;ldquo;makefile pour docker&amp;rdquo; il peut entrer dans votre projet de développement en équipe.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://docker.io&#34;&gt;Docker&lt;/a&gt; a vraiment un potentiel plus qu&amp;rsquo;intéressant. En ce qui concerne son intégration dans les système de &amp;ldquo;cloud&amp;rdquo;, pour déployer des services ou encore pour tester une installation ponctuellement c&amp;rsquo;est déjà un outil merveilleux. Mais j&amp;rsquo;ai l&amp;rsquo;impression que son utilisation dans un pole de développement n&amp;rsquo;est pas encore très répandu. Peut-être parce que beaucoup (trop) d&amp;rsquo;équipes de développement travaillent encore sur Windows et que Docker n&amp;rsquo;est pas encore un produit prêt à l&amp;rsquo;emploi sur cet OS (quoique maintenant&amp;hellip; il existe &lt;a href=&#34;https://www.docker.com/docker-toolbox&#34;&gt;Docker Toolbox&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;En ce qui me concerne, je travaille avec une équipe qui a:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;soit un pc sur Linux&lt;/li&gt;
&lt;li&gt;soit une VM pour lancer les services&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Du coup, on a put utiliser Docker dans nos projets.&lt;/p&gt;
&lt;h1 id=&#34;la-problématique-du-prérequi&#34;&gt;La problématique du prérequi&lt;/h1&gt;
&lt;p&gt;C&amp;rsquo;est tout bête. Nous devons travailler par exemple sur une application AngularJS qui interroge une API REST. Il nous faut NodeJS pour installer des outils tels que gulp, karma, protractor, des plugins gulp, bower, etc&amp;hellip; Nous devions donc installer tout ça avec les bonnes versions puis démarrer chacun de notre coté le projet (phase d&amp;rsquo;init d&amp;rsquo;installation).&lt;/p&gt;
&lt;p&gt;Imaginez un peu, il faut installer &amp;ldquo;node&amp;rdquo; dans la version demandée, donc utiliser &amp;ldquo;nvm&amp;rdquo; - puis toucher les &lt;code&gt;~/.bashrc&lt;/code&gt;, ensuite installer &amp;ldquo;globalement&amp;rdquo; des outils tels que &amp;ldquo;bower&amp;rdquo;, ou &amp;ldquo;gulp&amp;rdquo; - ce qui du coup impose une écriture avec les droits administrateurs. Vous allez me dire que ça se fait en 3 ou 4 commandes mais ce n&amp;rsquo;est pas si évident en pratique.&lt;/p&gt;
&lt;p&gt;En clair, on passait beaucoup de temps à initialiser le projet sans compter les soucis à régler sur certains postes, entre les soucis de version et la capacité des ingénieurs à installer les prérequis (sans compter que certains développeurs ne sont pas forcément à l&amp;rsquo;aise avec Linux).&lt;/p&gt;
&lt;p&gt;Autre problème, on passait souvent d&amp;rsquo;un projet à l&amp;rsquo;autre, parfois 3 à 4 fois dans la journée. Les projets n&amp;rsquo;ont pas forcément les mêmes prérequis. Le pire étant qu&amp;rsquo;une installation globale va masquer une dépendance manquante dans un autre projet, et on se pose pas mal de questions quand Jenkins nous balance une erreur sur des tests qui passent super bien sur nos postes.&lt;/p&gt;
&lt;p&gt;Donc, en bref, c&amp;rsquo;est pas si évident d&amp;rsquo;installer et d&amp;rsquo;isoler les développements sur un poste. Mais quand j&amp;rsquo;entend &amp;ldquo;isolement&amp;rdquo;, je ne peux m&amp;rsquo;empêcher de penser &amp;ldquo;virtualisation&amp;rdquo;, &amp;ldquo;para-virtualisation&amp;rdquo; et &amp;ldquo;Docker&amp;rdquo;. Et pour cause.&lt;/p&gt;
&lt;p&gt;Docker va nous abstraire de ces soucis, surtout en l&amp;rsquo;alliant à Docker-Compose. Tout va être géré via cet outil.&lt;/p&gt;
&lt;h1 id=&#34;limage-docker---la-base&#34;&gt;L&amp;rsquo;image Docker - la base&lt;/h1&gt;
&lt;p&gt;On utilise notre exemple: un dev AngularJS utilisant bower et gulp.&lt;/p&gt;
&lt;p&gt;On part du principe que, par défaut, on utilise la cible &amp;ldquo;gulp serve&amp;rdquo; qui va lancer un service http pour voir notre projet angular tourner. On a produit un Dockerfile tout simple qui suffit à notre démarrage de projet (dans le monde réel, il est un peu plus fourni)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM node:10.3
MAINTAINER Patrice FERLET &amp;lt;metal3d@...&amp;gt;

VOLUMES [&amp;quot;/project&amp;quot;]

RUN npm install -g bower karma gulp

WORKDIR /project
CMD [&amp;quot;gulp&amp;quot;,&amp;quot;serve&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Du coup, on peut &amp;ldquo;builder&amp;rdquo; l&amp;rsquo;image et la fournir aux développeurs&amp;hellip; Là encore, trois solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on fourni l&amp;rsquo;image buildée sur un serveur&lt;/li&gt;
&lt;li&gt;on utilise un registry privé&lt;/li&gt;
&lt;li&gt;chaque dev build l&amp;rsquo;image&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le premier point est lourd, le développeur qui gère la création du Dockerfile va devoir builder l&amp;rsquo;image puis la pousser sur un serveur. Si la connexion est lente, que le serveur est loin ou que quelqu&amp;rsquo;un met le pied sur le câble LAN, vous imaginez l&amp;rsquo;angoisse. Bref..&lt;/p&gt;
&lt;p&gt;Le second est intelligent et rapide, mais&amp;hellip; chaque poste doit avoir le certificat du registre installé, il faut gérer le registre, penser à sauvegarder, etc. Donc du boulot en trop.&lt;/p&gt;
&lt;p&gt;Le troisième point est sympa, on pourrait donc fournir notre fichier dans le dépot du projet. Mais le souci c&amp;rsquo;est que les options de démarrage d&amp;rsquo;un conteneur ne sont pas forcément faciles à trouver - on documenterai autant qu&amp;rsquo;on veut, on aurait toujours un membre de l&amp;rsquo;équipe pour nous péter à la gueule un &amp;ldquo;ça marche pas ça m&amp;rsquo;énerve&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Et c&amp;rsquo;est là que &amp;ldquo;docker-compose&amp;rdquo; entrera en jeu. Mais voyons d&amp;rsquo;abord ce qu&amp;rsquo;il se passe sans cet outil.&lt;/p&gt;
&lt;h1 id=&#34;sans-docker-compose-cest-un-peu-plus-compliqué&#34;&gt;Sans docker-compose, c&amp;rsquo;est un peu plus compliqué&lt;/h1&gt;
&lt;p&gt;Sans docker-compose, un membre de l&amp;rsquo;équipe de développement va devoir builder l&amp;rsquo;image et lancer l&amp;rsquo;init dans le bon ordre.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# En premier lieu
docker build -t jeanjacques/imageprojet docker/.

# Ensuite il va intialiser npm et bower via un conteneur temporaire
docker run -it --rm -v /rep/vers/projet jeanjaques/imageprojet npm install
docker run -it --rm -v /rep/vers/projet jeanjaques/imageprojet bower install

# et enfin créer un conteneur qui lance, par défaut, &amp;quot;gulp serve&amp;quot;
docker run -it --name projet1 -v /rep/vers/projet jeanjaques/imageprojet
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le développeur coupe le conteneur, et le lendemain il veut relancer les tests (comme tous les jours):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker run -it --rm -v /rep/vers/projet jeanjaques/imageprojet gulp test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous le voyez, plein de commandes compliquées avec pas mal d&amp;rsquo;options, il faut qu&amp;rsquo;il pense à réutiliser l&amp;rsquo;image de base, et comprendre les mécanismes de docker. En clair, le confort est pas si évident.&lt;/p&gt;
&lt;p&gt;Au pire, on fournirai un &amp;ldquo;Makefile&amp;rdquo; qui fait tout ça, mais justement il existe un autre outil spécialisé pour docker qui ressemble à un makefile, c&amp;rsquo;est ce fameux &amp;ldquo;docker-compose&amp;rdquo; que je vous rabache depuis le début du billet.&lt;/p&gt;
&lt;h1 id=&#34;et-docker-compose-arrive-sur-son-cheval-blanc&#34;&gt;Et Docker-compose arrive sur son cheval blanc&lt;/h1&gt;
&lt;p&gt;Le prérequis, demander à l&amp;rsquo;équipe d&amp;rsquo;installer &amp;ldquo;docker-compose&amp;rdquo; en suivant &lt;a href=&#34;https://docs.docker.com/compose/install/&#34;&gt;cette page de doc&lt;/a&gt; - c&amp;rsquo;est relativement rapide et simple.&lt;/p&gt;
&lt;p&gt;Docker-compose va donc être bien plus pratique car il évite au développeur de réfléchir à &amp;ldquo;Docker en lui même&amp;rdquo;. Il ne va finalement que préfixer les commandes habituelles d&amp;rsquo;un projet &amp;ldquo;node - gulp&amp;rdquo; par &amp;ldquo;docker-compose run [nom du projet]&amp;rdquo; ou utiliser &amp;ldquo;docker-compose up&amp;rdquo;. Voyez plutôt (le chien de Mickey):&lt;/p&gt;
&lt;p&gt;Voici un exemple de fichier &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;site:
    build: ./docker/
    ports:
    - &amp;quot;3000:3000&amp;quot;
    volumes:
    - ./:/project
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je précise, &amp;ldquo;build&amp;rdquo; donne le répertoire où se trouve le Dockerfile pour builder localement l&amp;rsquo;image, et &amp;ldquo;volumes&amp;rdquo; permet ici de monter le répertoire courrant dans un répertoire définis.&lt;/p&gt;
&lt;p&gt;Vous avouerez que c&amp;rsquo;est pas si long à taper hein. Voyons comment un développeur va démarrer son projet:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# initialise le projet
docker-compose run --rm site npm install 
docker-compose run --rm site bower install 

# pour lancer le &amp;quot;gulp serve&amp;quot; de base
docker-compose up


# pour lancer les tests
docker-compose run --rm site gulp test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La première fois que vous lancez une commande docker-compose, l&amp;rsquo;image se build. Si une mise à jour est nécessaire, il suffit de faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker-compose build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est incroyablement simple, facile à comprendre et ça ne demande pas trop de configuration.&lt;/p&gt;
&lt;p&gt;En gros, dans nos projets, nous avons au minimum:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un ficher docker-compose.yml&lt;/li&gt;
&lt;li&gt;un répertoire docker avec le Dockerfile et les dépendances (script entrypoint, des truc à déposer si besoin dans l&amp;rsquo;image&amp;hellip;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Comme tout est versionné en tant que fichier texte (pour le yaml et le Dockerfile), c&amp;rsquo;est vraiment pratique à gérer.&lt;/p&gt;
&lt;p&gt;Et le pompon sur la cerise: Jenkins peut utiliser la commande docker-compose pour lancer les tests lui-même. Si ça c&amp;rsquo;est pas géant !&lt;/p&gt;
&lt;h1 id=&#34;les-soucis-à-régler&#34;&gt;Les soucis à régler&lt;/h1&gt;
&lt;p&gt;Il y a des soucis à régler. Ici je vous ai donné un Docker file et un docker-compose.yml très simples. Or il ne faut pas oublier que Docker va exécuter les commandes en &amp;ldquo;root&amp;rdquo;. Et lors de la génération de fichiers, sur votre répertoire de travail, ce sera bien &amp;ldquo;root&amp;rdquo; qui est utilisé. &amp;ldquo;Bower&amp;rdquo; va vous engueuler, et vous allez vous battre avec les droits localement sur votre poste. Je vais vous donner quelques trucs qui permettent de ne pas avoir ce genre de soucis.&lt;/p&gt;
&lt;h2 id=&#34;souci-numéro-1-mapper-les-uid-et-gid-à-lutilisateur-hôte&#34;&gt;Souci numéro 1: mapper les UID et GID à l&amp;rsquo;utilisateur hôte&lt;/h2&gt;
&lt;p&gt;[EDIT] Cette solution de création d&amp;rsquo;utilisateur + entrypoint qui fait un &amp;ldquo;&lt;code&gt;sudo&lt;/code&gt;&amp;rdquo; est valable pour les anciennes version de de docker et docker-compose. Merci de prendre en compte la solution qui utilise &amp;ldquo;&lt;code&gt;user&lt;/code&gt;&amp;rdquo; dans le fichier yaml.&lt;/p&gt;
&lt;h3 id=&#34;solution-pour-les-nouvelles-versions&#34;&gt;Solution pour les nouvelles versions&lt;/h3&gt;
&lt;p&gt;Depuis peu, il est possible de forcer les identifiants utilisateur d&amp;rsquo;un compte qui se trouve dans le conteneur.&lt;/p&gt;
&lt;p&gt;Par exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;docker run -it --rm --user 1000:1000 busybox
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela induit que l&amp;rsquo;utilisateur qui va lancer le premier processus dans le conteneur écrira avec les identifiant user:group à 1000:1000. Dans &amp;ldquo;compose&amp;rdquo;, il suffit d&amp;rsquo;utiliser la directive &amp;ldquo;user&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;site:
    build: ./docker/
    ports:
    - &amp;quot;3000:3000&amp;quot;
    volumes:
    - ./:/project
    user: &amp;quot;1000:1000&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous allez me dire &amp;ldquo;ouais&amp;hellip; et si nos développeurs ont pas le même uid sur leurs postes&amp;rdquo;, ne vous inquiétez pas j&amp;rsquo;ai une solution.&lt;/p&gt;
&lt;p&gt;Docker-compose a désormais la capacité de faire de &lt;em&gt;l&amp;rsquo;héritage de définitions&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;L&amp;rsquo;idée et d&amp;rsquo;utiliser cette capacité en générant un fichier &amp;ldquo;de base&amp;rdquo; qui va être dynamiquement renseigné. On va y placer les uid et gid de l&amp;rsquo;utilisateur en cours (le développeur) avec un Makefile.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;On commence par un &amp;ldquo;POC&amp;rdquo;, on crée un fichier &amp;ldquo;.docker-base.yaml&amp;rdquo; (je mets un &amp;ldquo;.&amp;rdquo; devant le nom pour faire un fichier caché):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;mother:
    user: &amp;quot;1000:1000&amp;quot;
    environment:
        USER: root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour être clair, je laisse &amp;ldquo;$USER&amp;rdquo; à &amp;ldquo;root&amp;rdquo; pour garder le nom d&amp;rsquo;utilisateur malgré le changement de &amp;ldquo;uid&amp;rdquo; et &amp;ldquo;gid&amp;rdquo;. Sans cela, vous aurez un message &amp;ldquo;I have no name&amp;rdquo; dans le prompt shell&amp;hellip; c&amp;rsquo;est juste gênant à la lecture, pas à l&amp;rsquo;utilisation. Bref&amp;hellip;&lt;/p&gt;
&lt;p&gt;Il arrive aussi parfois que j&amp;rsquo;ai besoin d&amp;rsquo;assigner la variable &amp;ldquo;HOME&amp;rdquo;, car en changean les uid:gid et le nom d&amp;rsquo;utilisateur, certains services ne retrouvent plus le chemin du dossier utilisateur. Donc je le place dans &amp;ldquo;/tmp&amp;rdquo; ou dans le volume par défaut, au choix et selon le projet.&lt;/p&gt;
&lt;p&gt;Dans mon yaml de service maintenant, je vais hériter de ce fichier et utiliser &amp;ldquo;mother&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;site:
    build: ./docker/
    ports:
    - &amp;quot;3000:3000&amp;quot;
    volumes:
    - ./:/project
    extends:
        file: .docker-base.yaml
        service: mother
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Désormais, le service &amp;ldquo;site&amp;rdquo; hérite de &amp;ldquo;mother&amp;rdquo; depuis le fichier &amp;ldquo;.docker-base.yaml&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Si vous lancez &amp;ldquo;docker-compose up&amp;rdquo; et bien désormais, c&amp;rsquo;est un utilisateur &amp;ldquo;root&amp;rdquo; ayant les uid et gid à 1000 qui va lancer les processus (gulp, etc.) Pratique n&amp;rsquo;est-ce pas ?&lt;/p&gt;
&lt;p&gt;Bon, et bien on va utiliser un makefile qui va générer ce fichier &amp;ldquo;de base&amp;rdquo; et lancer les services:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;UID:=$(shell id -u)
GID:=$(shell id -g)
CC:=docker-compose

serve: _docker-base
    $(CC) run --rm gulp run

init: _docker-base
    $(CC) run --rm npm install
    $(CC) run --rm bower install

test: _docker-base
    $(CC) gulp test
    
_docker-base:
    @echo &amp;quot;mother:&amp;quot; &amp;gt; .docker-base.yaml
    @echo &amp;quot;    user: $(UID):$(GID)&amp;quot; &amp;gt;&amp;gt; .docker-base.yaml
    @echo &amp;quot;    environment:&amp;quot; &amp;gt;&amp;gt; .docker-base.yaml
    @echo &amp;quot;        USER: root&amp;quot; &amp;gt;&amp;gt; .docker-base.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je vous explique, ne paniquez pas.&lt;/p&gt;
&lt;p&gt;On commence par la dernière directive de Makefile, &amp;ldquo;&lt;code&gt;_docker-base&lt;/code&gt;&amp;rdquo;. Cette directive va créer un fichier &amp;ldquo;.docker-base.yaml&amp;rdquo; qui contient notre service de base, le service &amp;ldquo;mother&amp;rdquo;. Dans ce service, on défini l&amp;rsquo;environnement et les identifiants à utiliser qui correspondent à $UID et $GID (définis dans l&amp;rsquo;entête du Makefile).&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai ensuite 3 directives: serve, init et test. Ces trois directives dépendent de &amp;ldquo;&lt;code&gt;_docker-base&lt;/code&gt;&amp;rdquo;. Donc chaque fois que je vais lancer une tâche &amp;ldquo;make&amp;rdquo;, le fichier sera généré avec les bons UID et GID, et docker-compose exécutera mes conteneurs en mappant tout ça bien gentillement.&lt;/p&gt;
&lt;p&gt;Cette solution marche à condition que vos postes aient bien des versions récentes de docker et docker-compose. Sans cela, ça ne marchera pas.&lt;/p&gt;
&lt;h3 id=&#34;solution-pour-les-anciennes-versions&#34;&gt;Solution pour les anciennes versions&lt;/h3&gt;
&lt;p&gt;Pour éviter que tout soit écrit par root, on va demander à Docker d&amp;rsquo;utiliser un compte utilisateur local. On va l&amp;rsquo;appeler &amp;ldquo;dev&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée est de créer un utilisateur dans le conteneur qui ait les mêmes IDs que celui du compte développeur sur l&amp;rsquo;hôte. Par défaut, avec un peu de bol, vous avez un UID et GID vallant &amp;ldquo;1000&amp;rdquo;. Donc je modifie le Dockerfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM node:10.3
MAINTAINER Patrice FERLET &amp;lt;metal3d@...&amp;gt;

VOLUMES [&amp;quot;/project&amp;quot;]

RUN npm install -g bower karma gulp

# création d&#39;un utilisateur normal
RUN useradd -u 1000 -g 1000 dev

USER dev
WORKDIR /project
CMD [&amp;quot;gulp&amp;quot;,&amp;quot;serve&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais ça peut ne pas suffir&amp;hellip; malheureusement.&lt;/p&gt;
&lt;p&gt;Mon PC a deux comptes, et celui que j&amp;rsquo;utilise pour développer a un UID et GUID &amp;ldquo;1002&amp;rdquo;, ce qui pose un souci. Pour résoudre ça, j&amp;rsquo;ai ajouté un &amp;ldquo;entrypoint&amp;rdquo; au conteneur (dans le répertoire docker):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash

# force le changement d&#39;id utilisateur
if [ ! -z $_UID ] &amp;amp;&amp;amp; [ $_UID != &amp;quot;1000&amp;quot; ]; then
    su -c &#39;usermod -u $_UID dev&#39;
fi

# change l&#39;id de groupe de l&#39;utilisateur
if [ ! -z $_GID ] &amp;amp;&amp;amp; [ $_GID != &amp;quot;1000&amp;quot; ]; then
    su -c &#39;groupmod -g $_GID dev&#39;
fi

exec $@
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et par conséquent, le Dockerfile:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM node:10.3
MAINTAINER Patrice FERLET &amp;lt;metal3d@...&amp;gt;

VOLUMES [&amp;quot;/project&amp;quot;]

RUN npm install -g bower karma gulp

# création d&#39;un utilisateur normal
RUN useradd -u 1000 -g 1000 dev

# je supprime le mot de passe root pour que
# la commande &amp;quot;su&amp;quot; ne bloque pas
RUN usermod -p &amp;quot;&amp;quot; root

ADD entrypoint.sh /entrypoint.sh

USER dev
WORKDIR /project
ENTRYPOINT [&amp;quot;sh&amp;quot;, &amp;quot;/entrypoint.sh&amp;quot;]
CMD [&amp;quot;gulp&amp;quot;,&amp;quot;serve&amp;quot;]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;EDIT: j&amp;rsquo;ai ajouté la solution Makefile&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ensuite deux solutions, soit un script qui lance docker-compose avec les variables d&amp;rsquo;environnement:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash
# fichier: ./start.sh

UID=$(id -u)
GID=$(id -g)

action=$1
shift

docker-compose $action -e _UID=${UID} -e _GID=${GID} $@
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et donc, pour lancer mes commandes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./start.sh up
./start.sh run gulp serve
./start.sh run gulp test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Soit passer par un Makefile, c&amp;rsquo;est encore plus joli:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-makefile&#34;&gt;UID:=$(shell id -u)
GID:=$(shell id -g)
CC:=docker-compose

serve:
    $(CC) run -e _UID=$(UID) -e _GID=$(GID) gulp run

init:
    $(CC) run -e _UID=$(UID) -e _GID=$(GID) npm install
    $(CC) run -e _UID=$(UID) -e _GID=$(GID) bower install

test:
    $(CC) run -e _UID=$(UID) -e _GID=$(GID) gulp test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui donne à l&amp;rsquo;utilisateur:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;make&lt;/code&gt; ou &lt;code&gt;make serve&lt;/code&gt; -&amp;gt; lance le serveur&lt;/li&gt;
&lt;li&gt;&lt;code&gt;make init&lt;/code&gt; -&amp;gt; initialise les dépendances npm et bower&lt;/li&gt;
&lt;li&gt;&lt;code&gt;make test&lt;/code&gt; -&amp;gt; qui lance les tests&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A vous de faire votre sauce&amp;hellip;&lt;/p&gt;
&lt;p&gt;Mais pour résumer: à chaque fois, l&amp;rsquo;id et le gid sont passés à la commande docker-compose, l&amp;rsquo;entrypoint vérifie sur l&amp;rsquo;id et le gid sont présents et différent de l&amp;rsquo;id de base, et le change au besoin. C&amp;rsquo;est rapide et ça fait gagner du temps.&lt;/p&gt;
&lt;h2 id=&#34;souci-numéro-2-lutilisation-de-git-et-ssh&#34;&gt;Souci numéro 2: l&amp;rsquo;utilisation de git et SSH&lt;/h2&gt;
&lt;p&gt;Il se peut que vous ayez besoin de git dans votre conteneur, par exemple nous avons deux projets angular dont un doit récupérer le build de l&amp;rsquo;autre via bower. Dans le fichier &amp;ldquo;bower.json&amp;rdquo;, nous avons une url de dépendance sous la forme &amp;ldquo;git&amp;rdquo;, et ce dépot est &lt;strong&gt;privé&lt;/strong&gt;. Or, pour s&amp;rsquo;y connecter et permettre la récupération de code, il faut un compte SSH valide.&lt;/p&gt;
&lt;p&gt;Il est &lt;strong&gt;inconçevable&lt;/strong&gt; de devoir créer une clef à chaque conteneur et la poser dans notre compte github, du coup, voilà comment procéder.&lt;/p&gt;
&lt;p&gt;Il suffit, bêtement, de &amp;ldquo;monter ~/.ssh en volume&amp;rdquo;, donc dans le fichier &amp;ldquo;docker-compose.yml&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;site:
    build: ./docker/
    ports:
    - &amp;quot;3000:3000&amp;quot;
    volumes:
    - ./:/project
    - ~/.ssh:/home/dev/.ssh:ro
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le &amp;ldquo;:ro&amp;rdquo; final dit clairement de le monter en mode &amp;ldquo;read only&amp;rdquo; (lecture seule) pour éviter un malencontreux &amp;ldquo;pétage de répertoire&amp;rdquo; qui va vous rendre dingue. Autant dire que le risque serait minime, mais on bosse sérieusement et on préfère faire attention.&lt;/p&gt;
&lt;p&gt;Du coup, l&amp;rsquo;appel à &amp;ldquo;bower install&amp;rdquo; utilise la clef SSH du poste client et tout se passe bien.&lt;/p&gt;
&lt;h1 id=&#34;en-bref&#34;&gt;En bref&lt;/h1&gt;
&lt;p&gt;En clair, on a commencé, chez mon client, à travailler de cette manière. Le résultat est que nous ne passons plus des plombes à installer base de développpement, à nous battre avec les versions des outils, tout le monde a, quelque soit sa distribution, un services standard pour exécuter les commandes. On appelle ça &amp;ldquo;l&amp;rsquo;homogènéité&amp;rdquo; :)&lt;/p&gt;
&lt;p&gt;Nous avons par exemple créé des conteneurs pour:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python Google App Engine&lt;/li&gt;
&lt;li&gt;Gulp + protractor + karma + node + etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Récupérer le code depuis GitHub et lancer la commande &amp;ldquo;docker-compose up&amp;rdquo; est la seule chose à faire pour démarrer les projets. Certains projets ont un makefile partagé avec le conteneur - les cibles permettant de faire des packages, des builds, de simplifier des appels. Par exemple, de faire: &lt;code&gt;docker-compose run site make init&lt;/code&gt; qui lance tout la phase de préparation (npm install, bower clean cache, bower install, etc.)&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>grep et vimgrep plus rapide, passez à &#34;ag&#34;</title>
      <link>https://www.metal3d.org/blog/2015/grep-et-vimgrep-plus-rapide-passez-%C3%A0-ag/</link>
      <pubDate>Thu, 11 Jun 2015 11:46:14 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/grep-et-vimgrep-plus-rapide-passez-%C3%A0-ag/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Utilisant vim depuis des années pour développer, je suis toujours étonné de découvrir des trucs à droite à gauche qui améliorent encore mon confort de travail. Et même sans utiliser vim, la ligne de commande est mon outils quotidien. Aujourd&amp;rsquo;hui, parlons d&amp;rsquo;un programme qui pourrait bien remplacer votre bon vieux &amp;ldquo;grep&amp;rdquo; et l&amp;rsquo;utilisation de &amp;ldquo;vimgrep&amp;rdquo;: &amp;ldquo;the silver searcher&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Découvert sur &lt;a href=&#34;https://github.com/ggreer/the_silver_searcher&#34;&gt;la page de projet github&lt;/a&gt;, &amp;ldquo;the silver searcher&amp;rdquo; est une réimplémentation de &amp;ldquo;ack&amp;rdquo;, un outil de recherche de texte dans un répertoire comme le ferai &amp;ldquo;grep&amp;rdquo;. L&amp;rsquo;intérêt principal est tout simplement sa vitesse. Elle est tout bonnement impressionnante.&lt;/p&gt;
&lt;p&gt;Petite note: &amp;ldquo;the silver searcher&amp;rdquo; est une référence direct à &amp;ldquo;the silver surfer&amp;rdquo; alias &amp;ldquo;le surfeur d&amp;rsquo;argent&amp;rdquo;, le personnage de comics.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;ag&amp;rdquo; est compatible avec le prototype de &amp;ldquo;ack&amp;rdquo;, un autre outil de recherche dont elle respecte les options et format ce qui lui confère la possibilité d&amp;rsquo;utiliser le plugin &amp;ldquo;ack&amp;rdquo; pour vim&amp;hellip; je vous perds ? Bon on y va pas à pas.&lt;/p&gt;
&lt;h1 id=&#34;le-programme-ag&#34;&gt;Le programme &amp;ldquo;ag&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;Comme expliqué sur la page github, &amp;ldquo;the silver searcher&amp;rdquo; est déjà packagé sur pas mal de distros. donc:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt-get install silversearcher-ag
# ou
yum install the_silver_searcher
# ou encore avec pacma, emerge, etc.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous avez maintenant la commande &lt;code&gt;ag&lt;/code&gt; sur votre ordinateur.&lt;/p&gt;
&lt;p&gt;Pour lancer une recherche, par exemple trouver &amp;ldquo;foo&amp;rdquo; dans le répertoire courant:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ag foo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On remarque d&amp;rsquo;abord qu&amp;rsquo;on a pas à préciser le répertoire, ce qui est plus sympa quand on veut taper vite&amp;hellip; et ensuite, la vitesse de réponse et le format agréable de lecture:&lt;/p&gt;
&lt;p&gt;![Capture sortie ag](/static/upload/adb744f7-79ed-4912-934b-83e582fb4de9-Capture d&amp;rsquo;écran de 2015-06-11 10:26:17.png)&lt;/p&gt;
&lt;p&gt;Deux remarques:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le &amp;ldquo;pattern&amp;rdquo; à chercher peut être une expression régulière&lt;/li&gt;
&lt;li&gt;le format de sortie est adaptable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vous pouvez préciser le répertoire si vous ne voulez pas chercher dans le répertoire courant, et en lisant la &amp;ldquo;man page&amp;rdquo; vous allez découvrir des tonnes d&amp;rsquo;options.&lt;/p&gt;
&lt;p&gt;Si vous voulez comprendre pourquoi je suis passer à &amp;ldquo;ag&amp;rdquo;, regardez juste les benchmarks réalisés dans la section &lt;a href=&#34;https://github.com/ggreer/the_silver_searcher#whats-so-great-about-ag&#34;&gt;&amp;ldquo;What&amp;rsquo;s so great about Ag?&amp;rdquo;&lt;/a&gt; - c&amp;rsquo;est sans appel&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;mais-encore-&#34;&gt;Mais encore ?&lt;/h1&gt;
&lt;p&gt;Avec &amp;ldquo;grep&amp;rdquo;, dans un projet &amp;ldquo;git&amp;rdquo;, je devais me débrouiller pour gérer des &amp;ldquo;exclusions&amp;rdquo;, en d&amp;rsquo;autres termes il me fallait éviter de chercher dans le répertoire &amp;ldquo;.git&amp;rdquo; sous peine de me prendre des résultats complètement inutiles dans la face. &amp;ldquo;ag&amp;rdquo; évite ces répertoires par défaut, la commande reste donc très légère et confortable.&lt;/p&gt;
&lt;p&gt;Il est possible de dire à &amp;ldquo;ag&amp;rdquo; dans quel type de fichier on veut trouver le pattern. Pour connaitre les types supportés:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ag --list-file-types
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La liste est longue, croyez moi. Donc par exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ag --pyhton foo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ne cherchera &amp;ldquo;foo&amp;rdquo; que dans les fichier de type &amp;ldquo;python&amp;rdquo;. L&amp;rsquo;option est d&amp;rsquo;autant plus intéressante qu&amp;rsquo;un type de fichier peut avoir plusieurs extensions possibles. Par exemple, le HTML peut se trouver sour la forme &amp;ldquo;.html&amp;rdquo; ou &amp;ldquo;.htm&amp;rdquo;. et le &amp;ldquo;markdown&amp;rdquo; peut être suffixé par &amp;ldquo;.markdown&amp;rdquo;, &amp;ldquo;.mdown&amp;rdquo;, &amp;ldquo;.mdwn&amp;rdquo;, &amp;ldquo;.mkdn&amp;rdquo;, &amp;ldquo;.mkd&amp;rdquo; ou &amp;ldquo;.md&amp;rdquo;. Du coup, pas de commande à rallonge pour filtrer les extensions. Si ça c&amp;rsquo;est pas utile, faut m&amp;rsquo;expliquer votre pensé.&lt;/p&gt;
&lt;h1 id=&#34;dans-vim-deux-méthodes&#34;&gt;Dans Vim, deux méthodes&lt;/h1&gt;
&lt;p&gt;Deux solutions. Soit vous suivez la &amp;ldquo;manpage&amp;rdquo; (&lt;code&gt;man ag&lt;/code&gt;) et vous ajoutez à votre &amp;ldquo;vimrc&amp;rdquo; ces lignes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set grepprg=ag\ --vimgrep\ $*
set grepformat=%f:%l:%c:%m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui va vous permettre de taper (en remplaçant &amp;ldquo;pattern&amp;rdquo; par ce que vous cherchez):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:grep pattern
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puis tapez, comme avec vimgrep, &lt;code&gt;:cwindow&lt;/code&gt; pour voir la liste des occurences, et  de pouvoir ouvrir le fichier au bon endroit en pressant &amp;ldquo;Enter&amp;rdquo; sur le fichier en question.&lt;/p&gt;
&lt;p&gt;Soit vous passez par le plugin &lt;a href=&#34;https://github.com/mileszs/ack.vim&#34;&gt;Ack.vim&lt;/a&gt; et vous placez dans votre &amp;ldquo;vimrc&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if executable(&#39;ag&#39;)
  let g:ackprg = &#39;ag --vimgrep&#39;
endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et vous pourrez utiliser la commande vim:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:Ack pattern
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette commande va faire la recherche, ouvrir la mini-fenêtre de résultats et vous laisser ouvrir les fichiers de différentes manières (en onglet, directement dans le buffer en cours, etc&amp;hellip; pressez &amp;ldquo;?&amp;rdquo; dans la fenêtre pour voir les options)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La vitesse de réponse est vraiment étonnante&lt;/strong&gt; - vimgrep me gênait énormément à cause de sa lenteur. Mais tellement pratique que je ne pouvais pas m&amp;rsquo;en passer. Le combo &amp;ldquo;ag + ack.vim&amp;rdquo; s&amp;rsquo;installe en moins de 5 minutes et me fait gagner du temps et réduit le stresse de l&amp;rsquo;attente de résultat.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>OpenSSH et les socket de contrôle</title>
      <link>https://www.metal3d.org/blog/2015/openssh-et-les-socket-de-contr%C3%B4le/</link>
      <pubDate>Sat, 30 May 2015 09:52:47 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/openssh-et-les-socket-de-contr%C3%B4le/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Garder une connexion SSH active en arrière plan et pouvoir la stopper quand on veut, sans garder son PID, quelle idée merveilleuse hein ? Personnellement j&amp;rsquo;en avais besoin. Autre possibilité, se connecter une fois à un serveur et ne pas avoir à se reloguer à chaque fois que je veux retourner sur le serveur, pareil ce serait bien. Et bien c&amp;rsquo;est possible. Suffisait de lire la doc en fait. Voilà un petit billet qui va vous parler de tout ça.&lt;/p&gt;
&lt;p&gt;Clairement j&amp;rsquo;avais besoin de lancer un tunnel SSH et le garder ouvert un certain temps, puis le couper à la volée. Et ce sans avoir un terminal ouvert dans un coin. Oui vous allez me dire &amp;ldquo;l&amp;rsquo;option &lt;code&gt;-f&lt;/code&gt; de ssh sert à mettre en background la connexion&amp;rdquo;, mais pour fermer la connexion il faut alors passer par &amp;ldquo;ps&amp;rdquo; pour trouver le PID, et se débrouiller par des moyens détournés. Je me doutais bien que OpenSSH proposait quelque chose pour ça, et j&amp;rsquo;ai visé juste.&lt;/p&gt;
&lt;p&gt;Il suffit de chercher sur le net des articles et documentations parlant de &amp;ldquo;multiplexed ssh connections&amp;rdquo; par exemple pour trouver son bonheur.&lt;/p&gt;
&lt;p&gt;La page qui m&amp;rsquo;a été super utile est &lt;a href=&#34;http://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing&#34;&gt;OpenSSH/Cookbook/Multiplexing&lt;/a&gt; - mais la &amp;ldquo;manpage&amp;rdquo; a aussi pas mal d&amp;rsquo;infos.&lt;/p&gt;
&lt;h1 id=&#34;comment-ça-marche-&#34;&gt;Comment ça marche ?&lt;/h1&gt;
&lt;p&gt;L&amp;rsquo;idée est qu&amp;rsquo;en se connectant à un serveur SSH, un fichier &amp;ldquo;socket unix local&amp;rdquo; soit créé. Ce socket permet de faire pas mal de choses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;se reconnecter au serveur dans une autre session sans avoir à recréer l&amp;rsquo;appel complet (donc pas de mot de passe demandé)&lt;/li&gt;
&lt;li&gt;pouvoir couper toutes les connexions au serveur (si elles utilisent ce socket)&lt;/li&gt;
&lt;li&gt;pouvoir vérifier si des connexions sont actives&lt;/li&gt;
&lt;li&gt;et j&amp;rsquo;en passe&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il y a deux façon (au moins, à ma connaissance) pour paramétrer cette fameuse création de socket:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;soit dans la ligne de commande&lt;/li&gt;
&lt;li&gt;soit dans la config ssh&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il faut aussi prendre en compte qu&amp;rsquo;une connexion sera considérée comme &amp;ldquo;maitresse&amp;rdquo; (master) et que si celle-ci est coupée alors les autres connexions &amp;ldquo;esclaves&amp;rdquo; vont se couper, par défaut. On peut tout à fait éviter cette coupure des esclaves en manipulant un peu les options. On va en parler, n&amp;rsquo;ayez pas peur.&lt;/p&gt;
&lt;p&gt;Voyons comment ça se passe.&lt;/p&gt;
&lt;h1 id=&#34;explication-avec-la-ligne-de-commande&#34;&gt;Explication avec la ligne de commande&lt;/h1&gt;
&lt;p&gt;Créons une connexion à serveur en la spécifiant comme &amp;ldquo;maitresse&amp;rdquo; (via l&amp;rsquo;iption &lt;code&gt;-M&lt;/code&gt;). L&amp;rsquo;option &lt;code&gt;-S&lt;/code&gt; spécifie le chemin du socket unix.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh -M -S /tmp/test.ssh.sock user@server.tld
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Normalement votre connexion fonctionne et un fichier &lt;code&gt;/tmp/test.ssh.sock&lt;/code&gt; vient d&amp;rsquo;apparaitre. Dans un autre terminal, sans couper cette connexion, vous allez pouvoir vous reconnecter au même serveur en utilisant cette socket unix et sans demande de mot de passe. Cette fois on ne demande pas à devenir &amp;ldquo;maitre&amp;rdquo;, donc on supprime l&amp;rsquo;option &lt;code&gt;-M&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh -S /tmp/test.ssh.sock user@server.tld
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors faites cette expérience; retourner sur le premier terminal et coupez la connexion en tapant &amp;ldquo;exit&amp;rdquo;. Ce terminal reste bloqué, et pour cause ! Cette connexion est &amp;ldquo;maitre&amp;rdquo;, elle attend la coupure des autres esclaves. Donc allez dans le second terminal où vous avez lancé une seconde connexion SSH et faites pareil: &amp;ldquo;exit&amp;rdquo;. Là, enfin, les deux connexions sont coupées.&lt;/p&gt;
&lt;h1 id=&#34;les-commandes-de-socket&#34;&gt;Les commandes de socket&lt;/h1&gt;
&lt;p&gt;Cette socket unix ne sert pas seulement à multiplexer les connexions. On peut y passer des commandes.&lt;/p&gt;
&lt;p&gt;Recommençons l&amp;rsquo;expérience précédente:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un terminal avec &lt;code&gt;ssh -M -S /tmp/test.ssh.sock user@server.tld&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;un second terminal avec &lt;code&gt;ssh -S /tmp/test.ssh.sock user@server.tld&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ouvrons un troisième terminal et tapons:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh -S /tmp/test.ssh.sock user@server.tld -O check
Master running (pid=5764)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Intéressant - nous pouvons savoir le PID du processus maitre. C&amp;rsquo;est le PID de la commande ssh qui utilise l&amp;rsquo;option &lt;code&gt;-M&lt;/code&gt;. Mais ce n&amp;rsquo;est pas tout, on sait qu&amp;rsquo;elle est active !&lt;/p&gt;
&lt;p&gt;Le prototype de l&amp;rsquo;appel est simplement: &lt;code&gt;ssh -S chemin_socket -O commande user@server.ssh&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;option &lt;code&gt;-O&lt;/code&gt; (la lettre &amp;ldquo;O&amp;rdquo;) permet de passer des commandes de contrôle. Ici on utilise &amp;ldquo;check&amp;rdquo; mais on peut aussi utiliser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stop - qui dit au socket de ne plus prendre d&amp;rsquo;autres connexions et de se couper après fermeture des maitres et esclaves - la socket disparait quand aucune connexion n&amp;rsquo;est présente (c&amp;rsquo;est le mode soft)&lt;/li&gt;
&lt;li&gt;exit - qui demande à couper toutes les connexions en cours sur ce socket (donc coupe les connexion ssh au serveur, c&amp;rsquo;est le mode &amp;ldquo;bourinasse&amp;rdquo;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ainsi, tapez:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh -S /tmp/test.ssh.sock user@server.tld -O exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Regardez les deux autres terminaux, la connexion SSH est coupée, maitre et esclave compris !&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est donc super pratique, par exemple pour lancer un tunnel SSH qu&amp;rsquo;on peut stopper quand on veut:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh -M -S /tmp/tunnel.sock -N -L :8889:localhost:10085 root@server.tld 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et pour le couper, plus tard:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh -S /tmp/tunnel.sock -O exit root@server.tld 
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;utiliser-la-conf&#34;&gt;Utiliser la conf&lt;/h1&gt;
&lt;p&gt;C&amp;rsquo;est bien sympa de mettre des options en ligne de commande, mais peut-on configurer SSH pour qu&amp;rsquo;il fasse ça tout seul ? La réponse est &amp;ldquo;oui&amp;rdquo; (sinon j&amp;rsquo;aurais pas fait cette section hein).&lt;/p&gt;
&lt;p&gt;Ouvrons notre fichier &lt;code&gt;~/.ssh/config&lt;/code&gt; et paramétrons un service:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Host server.tld
	ControlPath /tmp/%r@%h:%p
	ControlMaster auto
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, la prochaine connexion au serveur &amp;ldquo;server.tld&amp;rdquo; va créer un socket &lt;code&gt;/tmp/user@host:port&lt;/code&gt; (en remplacant user, host et port par les valeurs qui vont bien).&lt;/p&gt;
&lt;p&gt;Une nouvelle connexion au serveur, alors que l&amp;rsquo;autre est active, va fonctionner comme précédemment, et comme d&amp;rsquo;habitude vous pourrez utiliser l&amp;rsquo;option &lt;code&gt;-O&lt;/code&gt; pour passer des commandes de contrôle.&lt;/p&gt;
&lt;p&gt;Une autre option super intéressante est &amp;ldquo;ControlPersist&amp;rdquo; qui, si elle vaut &amp;ldquo;yes&amp;rdquo;, va laisser la socket même si aucun maitre n&amp;rsquo;est présent.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Host server.tld
	ControlPath /tmp/%r@%h:%p
	ControlMaster auto
	ControlPersist yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est plutôt sympa et ça permet de vraiment accélérer les nouvelles connexions au serveur. Si vous désirez forcer la fermeture de ce socket c&amp;rsquo;est simple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ssh user@server.tld -O stop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aucune connexion ne sera acceptée, et quand tous les esclaves auront quitté, la socket sera supprimée.&lt;/p&gt;
&lt;p&gt;Plutôt que de mettre &lt;code&gt;yes&lt;/code&gt;, vous pouvez aussi donner une valeur de temps: &lt;code&gt;10m&lt;/code&gt; pour 10 minutes, &lt;code&gt;1h&lt;/code&gt; pour une heure&amp;hellip; la socket va donc persister le temps donné si aucune connexion maitresse n&amp;rsquo;est en cours. C&amp;rsquo;est un chouillat plus sécurisé.&lt;/p&gt;
&lt;h1 id=&#34;utilité&#34;&gt;Utilité&lt;/h1&gt;
&lt;p&gt;On parle de pouvoir contrôller des connexions à un serveur. Si vous sauvez une configuration dans &lt;code&gt;/etc/ssh/ssh_config&lt;/code&gt; alors toutes les connexions de n&amp;rsquo;importe quel user sur le poste seront accélérées et contrôllables, donc par exemple vous pourrez fermer toutes les connexions à un serveur en particulier simplement en utilisant le socket adéquat.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;autre utilité est clairement la possibilité de créer des tunnels et de les couper rapidement sans passer par la case &amp;ldquo;ps&amp;rdquo;, trouver le pid etc. Une commande &amp;ldquo;exit&amp;rdquo; dans le socket et le tour est joué.&lt;/p&gt;
&lt;p&gt;Il y a bien d&amp;rsquo;autres options et utilités, je vous laisse regarder la page que j&amp;rsquo;ai donné en haut de l&amp;rsquo;article.&lt;/p&gt;
&lt;p&gt;Bref, encore une option OpenSSH que je suis content d&amp;rsquo;avoir découvert. Entre le tunnelling, la création de pseudo VPN, les sockets de contrôle et tous les outils qui suivent, y&amp;rsquo;a pas, SSH c&amp;rsquo;est le pied.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Mise à jour du blog en Go</title>
      <link>https://www.metal3d.org/blog/2015/mise-%C3%A0-jour-du-blog-en-go/</link>
      <pubDate>Sun, 12 Apr 2015 21:47:31 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2015/mise-%C3%A0-jour-du-blog-en-go/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Si vous connaissiez mon blog, vous avez remarqué qu&amp;rsquo;à partir d&amp;rsquo;aujourd&amp;rsquo;hui sa tête a changé. Je me suis décidé à refondre la totalité du site, ce qui va apporter son lot de soucis car tester le tout est pas simple du tout. Mais dans l&amp;rsquo;ensemble ça va aller vachement mieux. Le blog fonctionne maintenant en Go (Golang), dans un conteneur Docker et avec mon framework (&lt;a href=&#34;http://github.com/kwiscale&#34;&gt;kwiscale&lt;/a&gt;). Je vous raconte ?&lt;/p&gt;
&lt;p&gt;Il y a des mois de cela, j&amp;rsquo;avais sauté le pas depuis PHP (sous Copix) et NodeJS (avec un framework maison nommé Knotter). Pour le coup j&amp;rsquo;étais super content. Et puis au fil des semaines et des mois les soucis se sont accumulés. Commentaires en double, voir plus de commentaire du tout, runtime node qui tombait (car je tentais de gérer de l&amp;rsquo;interprocess), et j&amp;rsquo;avais accumulé du code franchement pas beau. Bon clairement j&amp;rsquo;ai pas été bon sur le coup. Javascript étant très permissif, j&amp;rsquo;ai vraiment mal codé certaines parties et j&amp;rsquo;ai eut du mal à corriger l&amp;rsquo;ensemble. Coder des systèmes asynchrones est, certes, génialissime en terme de concept, mais très compliqué à maintenir si, pour N raisons, vous avez une coquille dans l&amp;rsquo;architecture logicielle.&lt;/p&gt;
&lt;p&gt;Je vais calmer le jeu tout de suite: j&amp;rsquo;adore NodeJS. Mon mini framework marchait, le principe était pas mauvais. Le souci c&amp;rsquo;est que j&amp;rsquo;aime coder de manière &amp;ldquo;modulaire&amp;rdquo; et avoir des couches de séparation entre mes structures fonctionnelles. Et malheureusement le concept de mon code de gestionnaire de blog était trop éparpillé pour un tel framework sur cette technologie.&lt;/p&gt;
&lt;p&gt;Autre souci, et pas des moindres, je ne maitrisais pas bien MongoDB. Depuis j&amp;rsquo;ai énormément lut et testé et je me suis rendu compte que j&amp;rsquo;avais vraiment fait n&amp;rsquo;importe quoi sur le blog.&lt;/p&gt;
&lt;p&gt;Aujourd&amp;rsquo;hui je sais assurer une écriture viable ou une modification de document dans la base. Et ça change la vie.&lt;/p&gt;
&lt;p&gt;Mongo, j&amp;rsquo;en parlerai dans un prochain ticket. Il y a des problèmes qu&amp;rsquo;un débutant peut rencontrer, ne pas savoir ou simplement imaginer de mauvais concepts. J&amp;rsquo;ai d&amp;rsquo;ailleurs utilisé une chose que j&amp;rsquo;avais laissé de coté sur Mongo: la recherche full-text.&lt;/p&gt;
&lt;h1 id=&#34;technos-utilisées&#34;&gt;Technos utilisées&lt;/h1&gt;
&lt;p&gt;J&amp;rsquo;en parle depuis un moment au boulot, j&amp;rsquo;adore Go (ou &lt;a href=&#34;http://golang.org&#34;&gt;Golang&lt;/a&gt;). Ce langage apporte tellement de plaisirs à coder que me suis lancé dans la création d&amp;rsquo;un vrai framework nommé &lt;a href=&#34;http://github.com/kwiscale&#34;&gt;kwiscale&lt;/a&gt;. Ce qui m&amp;rsquo;a étonné en codant ce framework, c&amp;rsquo;est que la langage pousse à faire en sorte que tout se passe bien. Si si, vraiment. Pas moyen de faire &amp;ldquo;n&amp;rsquo;importe quoi n&amp;rsquo;importe comment&amp;rdquo;. On est proche du C, sans en avoir la complexité. On s&amp;rsquo;étonne à utiliser des pointeurs partout et sans en avoir peur. On s&amp;rsquo;impressionne à réussir à lancer et synchroniser des &amp;ldquo;goroutines&amp;rdquo; (ou coroutine, c&amp;rsquo;est à dire des tâches parallèles) sans se taper les neuronnes dans le fond du crâne. En clair, ce langage est un pied d&amp;rsquo;enfer pour un développeur qui cherche à maitriser une grosse batterie modulaire.&lt;/p&gt;
&lt;p&gt;A l&amp;rsquo;inverse de mon framework &amp;ldquo;maison&amp;rdquo; nommé &amp;ldquo;Knotter&amp;rdquo; en NodeJS, je savais très bien où j&amp;rsquo;allais, comment j&amp;rsquo;allais amener la configuration, comment le rendre modulaire.&lt;/p&gt;
&lt;p&gt;Je me répète mais&amp;hellip; j&amp;rsquo;adore NodeJS et je ne crache pas sur PHP. J&amp;rsquo;ai codé près de 10 ans sur PHP et j&amp;rsquo;ai toujours défendu ce langage. L&amp;rsquo;idée c&amp;rsquo;est que Go m&amp;rsquo;apporte un peu plus de contôle, de maitrise, de bonnes performances et une gestion de la concurrence plus abordable et maitrisable. Je n&amp;rsquo;ai plus à me poser des questions sur l&amp;rsquo;implémentation, je me penche quasi essentiellement sur l&amp;rsquo;algo. Ça change la vie.&lt;/p&gt;
&lt;p&gt;Développer ce blog sur le framework Kwiscale m&amp;rsquo;a pris 3 soirées. Sachant que je gère:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la détection de spam via un filtre bayésien et gestion de demande de modération&lt;/li&gt;
&lt;li&gt;envoit de mails&lt;/li&gt;
&lt;li&gt;traitement des drafts (pour les tickets)&lt;/li&gt;
&lt;li&gt;page statique (ficheri markdown)&lt;/li&gt;
&lt;li&gt;ticket en base Mongo (format markdown)&lt;/li&gt;
&lt;li&gt;sitemaps&lt;/li&gt;
&lt;li&gt;flux RSS (au passage j&amp;rsquo;ai abandonné Feedburner)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;et j&amp;rsquo;en passe&amp;hellip; Je reparlerais de Kwiscale dans un autre ticket, le jour où je releaserai une version stable (disons en fin Avril 2015 si tout se passe bien).&lt;/p&gt;
&lt;p&gt;Ce qui m&amp;rsquo;a éclaté c&amp;rsquo;est la facilité à générer du XML ou du JSON avec ce que Go propose en built-in. Du coup, les flux RSS ou encore les sitemap m&amp;rsquo;ont pris 10-15 minutes à les réaliser. Un bonheur je vous dis.&lt;/p&gt;
&lt;p&gt;Bref, j&amp;rsquo;utilise Kwiscale et le plugin que j&amp;rsquo;ai créé pour utiliser le moteur de template &lt;a href=&#34;https://github.com/flosch/pongo2&#34;&gt;Pongo2&lt;/a&gt;. Ce moteur m&amp;rsquo;apporte la possibilité de traiter du markdown très facilement dans les templates, la syntaxe est proche de Jinja2. Pour un développeur comme moi qui a usé Python dans toutes les directions, c&amp;rsquo;est un gain de temps d&amp;rsquo;apprentissage non négligeable. Les développeurs PHP/Syfony connaissent aussi cette syntaxe via Twig.&lt;/p&gt;
&lt;h1 id=&#34;développer-en-go-sans-recompilation&#34;&gt;Développer en Go sans recompilation&lt;/h1&gt;
&lt;p&gt;Go demande une compilation de votre projet (tout comme C/C++ ou Java). L&amp;rsquo;intérêt est qu&amp;rsquo;il compile très vite, le temps est quasi négligeable (de l&amp;rsquo;ordre de la centaine de miliseconde). Du coup Go propose une commande &lt;code&gt;go run ...&lt;/code&gt; qui exécute le programme juste après la compilation. Ça donne l&amp;rsquo;impression d&amp;rsquo;utiliser un langage interprété, sans en avoir l&amp;rsquo;arrière gout.&lt;/p&gt;
&lt;p&gt;Le souci, c&amp;rsquo;est qu&amp;rsquo;il aurait fallut que je relance la commande &lt;code&gt;go run&lt;/code&gt; à chaque modification de code.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/codegangsta/gin&#34;&gt;Gin&lt;/a&gt; justement nous affranchit de cette contrainte. C&amp;rsquo;est un programme en Go qui permet de détecter et relancer la compilation au moindre changement de code. Il agit comme un proxy et transmet les connexions au programme que vous développez. C&amp;rsquo;est tout bête et ça marche bien.&lt;/p&gt;
&lt;p&gt;Il ne me restait que la base Mongo à installer, configurer&amp;hellip; mais non, j&amp;rsquo;ai utilisé Docker !&lt;/p&gt;
&lt;p&gt;Je vais pas trop développer le sujet dans ce ticket, mais j&amp;rsquo;ai trouvé un trio gagnant pour développer sans prise de tête un site en Go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/tools/godep&#34;&gt;Godep&lt;/a&gt; - gère les dépendances Go dans un workspace&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/codegangsta/gin&#34;&gt;Gin&lt;/a&gt; - un outil qui relance votre code lors d&amp;rsquo;une modification qui sait utiliser Godep&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.docker.com/compose/&#34;&gt;Docker-compose&lt;/a&gt; - anciennement &amp;ldquo;Fig&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En gros, j&amp;rsquo;ai créé un fichier docker-compose.yml qui lance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;un serveur nginx avec la conf montée dans le répertoire &lt;code&gt;/etc/nginx/conf.d&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;la base mongo (avec les data sauvé localement sur l&amp;rsquo;hôte)&lt;/li&gt;
&lt;li&gt;une image golang (de base) qui utilise un makefile qui installe ce qu&amp;rsquo;il faut (2 packages) et lance une commande gin avec l&amp;rsquo;option qui demande à utiliser Godep.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Toutes les dépendances étant enregistrées via Godep depuis mon host, et ayant &amp;ldquo;monté&amp;rdquo; mon répertoire de travail dans le conteneur, le conteneur Go n&amp;rsquo;avait besoin que de Gin et Godep. Godep se servant du répertoire &lt;code&gt;Godep&lt;/code&gt; qui se crée en tapant &lt;code&gt;godep save&lt;/code&gt; (je ferai un autre ticket pour ce sujet, patience). Seulement deux pacakges à installer via le Makefile, rien de méchant donc&amp;hellip; et pas la peine de le modifier si j&amp;rsquo;utilise un nouveau paquet dans mon projet. Godep se charge de tout.&lt;/p&gt;
&lt;p&gt;Arrivée en production, j&amp;rsquo;utilise un second docker-compose.yml nommé &amp;ldquo;docker-compose-prod.yml&amp;rdquo; qui, lui, n&amp;rsquo;instancie pas nginx (car j&amp;rsquo;en ai un sur le serveur qui tape sur d&amp;rsquo;autres services web) et surtout qui n&amp;rsquo;utilise plus Gin, juste Godep. Effectivement en production je n&amp;rsquo;ai pas besoin d&amp;rsquo;écouter les modifications de code.&lt;/p&gt;
&lt;p&gt;Cette fois, il utilise une autre directive du makefile qui ne lance que les commandes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go get github.com/tools/godep
godep go run main.go
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La commande &amp;ldquo;godep&amp;rdquo; va simplement spécifier GOPATH au compilateur &amp;ldquo;go&amp;rdquo;. c&amp;rsquo;est à dire le réperoire &amp;ldquo;Godep&amp;rdquo; livré avec mon projet. Donc, pour être clair:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je déploie depuis ma machine le code + le workspace Godep (qui contient tous les packages nécessaires) avec rsync&lt;/li&gt;
&lt;li&gt;docker-compose lance les deux instances (mongo et golang)&lt;/li&gt;
&lt;li&gt;le conteneur golang lance &amp;ldquo;make prod&amp;rdquo;&lt;/li&gt;
&lt;li&gt;le makefile installe godep puis lance le service en utilisant le workspace Gedep via la commande &lt;code&gt;godep go run main.go&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Promis, je ferai un ticket sur le sujet. Car je pense que mon modèle est loin d&amp;rsquo;être mauvais et peut être réutilisé. Je me la pète un peu non ? si&amp;hellip; tant pis&amp;hellip; soyons fier de ce qu&amp;rsquo;on réalise parfois !&lt;/p&gt;
&lt;p&gt;Comprennez juste que je suis &amp;ldquo;certain&amp;rdquo; que ce qui tourne sur mon poste va tourner de la même manière en production.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Et ça, c&amp;rsquo;est vraiment une révolution&lt;/p&gt;
&lt;p&gt;&amp;ndash; &lt;span class=&#34;emoji&#34;&gt;®&lt;/span&gt; S.Jobs&lt;/p&gt;&lt;/blockquote&gt;
&lt;h1 id=&#34;spam&#34;&gt;Spam&lt;/h1&gt;
&lt;p&gt;Mon site est fortement spammé. J&amp;rsquo;ai donc utilisé 2 bloquages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le premier est tout bête, le formulaire de commentaire a plein de champs inutiles qui, s&amp;rsquo;ils sont remplis, le sont à coup sûr par un robot - le CSS les cache, donc vous, vous ne les voyez pas&lt;/li&gt;
&lt;li&gt;un filtre bayésien que j&amp;rsquo;ai codé en 15 minutes - il charge un index de mot depuis les commentaires que j&amp;rsquo;ai classé en spam et en &amp;ldquo;commentaire sûr&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce filtre bayésien est sensible, car comme je vous le disais, mon blog à énormément de messages de spam. Du coup, si vous commentez vous avez de forte chance de vous retouver classé en tant que spammeur. Mais &lt;strong&gt;pas de panique&lt;/strong&gt;, Si vous le premier filtre est passé (donc vous n&amp;rsquo;êtes pas un robot qui a rempli des champs interdits) alors je vous propose sur une page de cliquer sur un bouton pour me signaler l&amp;rsquo;erreur. C&amp;rsquo;est automatique, ça m&amp;rsquo;envoit un mail, et j&amp;rsquo;ai plus qu&amp;rsquo;à valider votre commentaire.&lt;/p&gt;
&lt;p&gt;Le commentaire devenant &amp;ldquo;sûr&amp;rdquo;, il entre dans le jeu de données &amp;ldquo;message non spam&amp;rdquo;  (qu&amp;rsquo;on appelle &amp;ldquo;ham&amp;rdquo;). La prochaine fois, qu&amp;rsquo;un commentaire arrive, votre commentaire est aussi utilisé pour la vérification. C&amp;rsquo;est le principe de Bayes, la probabilité &amp;ldquo;à posteriori&amp;rdquo;. Ça marche pas mal.&lt;/p&gt;
&lt;p&gt;Plus vous commentez, plus le filtre est fiable.&lt;/p&gt;
&lt;h1 id=&#34;reprise-de-contenu-le-cauchemard&#34;&gt;Reprise de contenu, le cauchemard&lt;/h1&gt;
&lt;p&gt;Par contre, la reprise de contenu m&amp;rsquo;a pris une bonne semaine&amp;hellip; et le pire c&amp;rsquo;est que c&amp;rsquo;est toujours pas bon.&lt;/p&gt;
&lt;p&gt;La base utilisée est toujours &amp;ldquo;Mongo&amp;rdquo;. J&amp;rsquo;ai implémenter un moteur de recherche full text en partant de &lt;a href=&#34;http://docs.mongodb.org/manual/core/index-text/&#34;&gt;ce que propose MongoDB&lt;/a&gt;. Dans les prochains jours je parlerai de quelques aspects de MongoDB souvent négligés, voir des erreurs de base qu&amp;rsquo;on peut faire (et que j&amp;rsquo;avais fait avec mon ancien système de blog)&lt;/p&gt;
&lt;p&gt;Finalement, ce qui m&amp;rsquo;a pris le plus de temps, c&amp;rsquo;est de récupérer le contenu du site &amp;ldquo;ancienne version&amp;rdquo;. Et je dois bien avoué que j&amp;rsquo;ai baissé les bras à un moment donné. En effet, sous Copix j&amp;rsquo;utilisais une syntaxe &amp;ldquo;wiki&amp;rdquo; spéciale qui est très compliqué à transformer en Markdown. Du coup, les vieux messages ont de sérieux soucis&amp;hellip; pour ceux tapés en RST ça a été plus simple (merci à &lt;a href=&#34;http://johnmacfarlane.net/pandoc/&#34;&gt;Pandoc&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai corrigé les tickets les plus lut&amp;hellip; mais je sais qu&amp;rsquo;une passe complète ne sera jamais possible. Tant pis&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;les-anciennes-url&#34;&gt;Les anciennes URL&lt;/h1&gt;
&lt;p&gt;Restait un point qui me faisait grogner, j&amp;rsquo;ai modifié la forme d&amp;rsquo;url et la forme du slug (le titre rendu lisible dans l&amp;rsquo;url). Avant, la date du ticket apparaissaient dans l&amp;rsquo;url et j&amp;rsquo;ai décidé (à tort peut-être) de m&amp;rsquo;en passer. D&amp;rsquo;abord parce que je trouve pas ça beau, que la date apparait dans la page et enfin parce que j&amp;rsquo;ai utilisé un truc pour ne pas avoir de doublon de slug. Donc, inutile à mon gout.&lt;/p&gt;
&lt;p&gt;Mais voilà&amp;hellip; certains ont bookmarké quelques unes de mes pages. Il y a même quelqu&amp;rsquo;un qui a référencé un de mes tickets dans Wikipedia (pour Gstreamer), et la forme d&amp;rsquo;url a changé. Google va se plaindre, ça m&amp;rsquo;embêtait vraiment. La solution a été finalement simple. Ne sachant pas reproduire les vieux slug, j&amp;rsquo;ai décidé de ruser.&lt;/p&gt;
&lt;p&gt;Je prend un slug, je vire les tirets: ça me donne une liste de mots. Je lance une recherche full-text dans Mongo. Sachant que le titre correspond au slug, et que le titre a un poids important dans mon indexation (par rapport au contenu), je suis sûr à 99% que le premier résultat que me retournera la recherche est le ticket initial. Une réponse &amp;ldquo;HTTP Permanent Redirect (301)&amp;rdquo; et le tour est joué.&lt;/p&gt;
&lt;h1 id=&#34;style-css-en-avant-flexbox-&#34;&gt;Style CSS, en avant flexbox !&lt;/h1&gt;
&lt;p&gt;Exit Bootstrap ou Skeleton. Non pas que je ne les aime pas, mais ce blog est là pour que je teste des technos et que je donne des idées, des solutions et des méthodes. Bootstrap est génial, Skeleton est vraiment cool, mais CSS apporte depuis quelques temps la gestion de &amp;ldquo;boites flexibles&amp;rdquo; qui permet de réduire le travail.&lt;/p&gt;
&lt;p&gt;Du coup, j&amp;rsquo;ai tenté le coup. À mon grand étonnement, j&amp;rsquo;ai put sortir ce que je voulais: un design responsive en 205 lignes de CSS tout compris.&lt;/p&gt;
&lt;p&gt;Ok, Internet Explorer &amp;lt; 10 va me taper une gueulante, mais IE ne représente que 4% de mon auditoir. Par contre, ça marche bien sur Android (tablette et mobile).&lt;/p&gt;
&lt;p&gt;Question couleur, j&amp;rsquo;adore les couleurs &amp;ldquo;sombres&amp;rdquo;. J&amp;rsquo;ai utilisé la couleur du gopher (en haut à droite du site) pour les liens (merci à mon épouse pour l&amp;rsquo;idée) et je pense que ça reste sobre, lisible et pas aggressif.&lt;/p&gt;
&lt;h1 id=&#34;bilan&#34;&gt;Bilan&lt;/h1&gt;
&lt;p&gt;Golang est vraiment un langage prêt pour la production. Kwiscale approche de son but et mon blog fonctionne largement mieux qu&amp;rsquo;avant.&lt;/p&gt;
&lt;p&gt;Je suis plutôt content du résultat et je vais pouvoir réécrire des tickets plus régulièrement.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>En finir avec l&#39;erreur Virtualbox VERR_VD_IMAGE_READ_ONLY</title>
      <link>https://www.metal3d.org/blog/2014/en-finir-avec-lerreur-virtualbox-verr_vd_image_read_only/</link>
      <pubDate>Wed, 19 Nov 2014 14:44:52 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2014/en-finir-avec-lerreur-virtualbox-verr_vd_image_read_only/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Au travail, on est sur des poste Windows 7&amp;hellip; et comme on veut bosser on
a des VM Linux :) bref, qui dit Windows dit &amp;ldquo;anti-virus&amp;rdquo;, et qui dit
&amp;ldquo;anti-virus&amp;rdquo; dit un foutoir dans les lock&amp;hellip; Vous avez souvent, comme
moi, une erreur VERR_VD_IMAGE_READ_ONLY qui vous empêche de démarrer
votre VM ? vous n&amp;rsquo;arrivez pas à trouver quel processus a locker vos
disques ? moi j&amp;rsquo;y ai remédié avec Python :)&lt;/p&gt;
&lt;p&gt;Au travail, c&amp;rsquo;est ce bon vieux McAfee (toutes blagues avec Mac à Fille
sera rejeté) qui avait la fâcheuse tendance à me verrouiller mes disques
virtuels pour ma VM sur VirtualBox. J&amp;rsquo;ai trouvé, de ci, de là, des
commentaires dans des forums qui disent &amp;ldquo;ouiiiin j&amp;rsquo;ai une tâche qui lock
ma VM, je l&amp;rsquo;ai trouvé et killé et hop ça marche&amp;rdquo;. Bha un tour dans le
gestionnaire de tâche (et services) de Windows 7 m&amp;rsquo;a juste fait chialé.&lt;/p&gt;
&lt;p&gt;Et ne parlons pas des commandes de base dans &amp;ldquo;cmd&amp;rdquo;, un équivalent de
base de &amp;ldquo;lsof&amp;rdquo; vous le trouverez pas. Par contre, Microsoft propose des
outils à télécharger. Genre:
&lt;a href=&#34;http://technet.microsoft.com/en-us/sysinternals/bb896655.aspx&#34;&gt;http://technet.microsoft.com/en-us/sysinternals/bb896655.aspx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Vous pouvez donc récupérer &amp;ldquo;Handle.exe&amp;rdquo; et le placer dans un dossier
facilement accessible.&lt;/p&gt;
&lt;p&gt;Reste à faire un script pour cmd.exe, c&amp;rsquo;est tellement puissant,
tellement facile de traiter des chaines&amp;hellip; non de déconne. Installez
donc python 2.7 &lt;a href=&#34;https://www.python.org/downloads/windows/&#34;&gt;https://www.python.org/downloads/windows/&lt;/a&gt; installez
tout ça.&lt;/p&gt;
&lt;p&gt;Tiens une super astuce de la mort pour &amp;ldquo;forcer Windows à mettre à jour
les variables d&amp;rsquo;environnement&amp;rdquo;, accrochez-vous c&amp;rsquo;est jouissif !&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ouvrir le gestionnaire de tâche&lt;/li&gt;
&lt;li&gt;killez le processus &amp;ldquo;explorer.exe&amp;rdquo;&lt;/li&gt;
&lt;li&gt;créez une nouvelle tâche &amp;ldquo;explorer.exe&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est pratique hein ? (un corbeau passe derrière ma tête)&lt;/p&gt;
&lt;p&gt;reste à placer ce script python sur le bureau:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# -*- encoding: utf-8 -*-
import subprocess
import re

HANDLE = &amp;quot;c:\Users\pferlet\Handle.exe&amp;quot;

# bon oui, changez les chemins pour que ce soit en accord avec votre installation
vids = [&amp;quot;\Users\pferlet\VirtualBox VMs\Viperr\Viperr.vdi&amp;quot;,
    &amp;quot;\Users\pferlet\VirtualBox VMs\Viperr\Viperr2.vdi&amp;quot;]
reg = re.compile(r&amp;quot;pid: (\d+)&amp;quot;,re.MULTILINE|re.IGNORECASE)

for vid in vids:
    ret = subprocess.check_output([HANDLE, vid], shell=True)

    pids = reg.findall(ret)
    if len(pids) &amp;gt; 0:
        for pid in pids:
            subprocess.call(&amp;quot;taskkill /F /PID %d&amp;quot; % int(pid))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nommez le &amp;ldquo;unlock.py&amp;rdquo; par exemple, et pour l&amp;rsquo;exécuter, faites simplment
un clique droit dessus et &amp;ldquo;ouvrir avec&amp;hellip;&amp;rdquo;, cherchez &amp;ldquo;python.exe&amp;rdquo; et le
tour est joué.&lt;/p&gt;
&lt;p&gt;Quand ma VM se trouve locké, je lance ce script, ça explose un vieux pet
moisi devant le nez de McAfee qui fuit aux toilettes pour vomir. Le
temps qu&amp;rsquo;il revienne, la VM peut démarrer.&lt;/p&gt;
&lt;p&gt;Voilà pour le coté &amp;ldquo;vous devriez tous avoir python sur vos postes
Windows 7&amp;rdquo; et la question &amp;ldquo;Mac et Linux incluent Python, pourquoi pas
Windows ? ha oui c&amp;rsquo;est vrai&amp;hellip; licence tout ça&amp;hellip;&amp;rdquo;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>La commande deux-points en Bash</title>
      <link>https://www.metal3d.org/blog/2014/la-commande-deux-points-en-bash/</link>
      <pubDate>Tue, 18 Nov 2014 18:49:28 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2014/la-commande-deux-points-en-bash/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Que ce soit en bash, en sh, en ce que vous voulez, on trouve parfois des
subtilités assez intéressantes pour peu qu&amp;rsquo;on se penche un peu sur les
documentations. Une &amp;ldquo;commande&amp;rdquo; (oui s&amp;rsquo;en est une) que j&amp;rsquo;utilise souvent
est &amp;ldquo;:&amp;rdquo;, c&amp;rsquo;est à dire &amp;ldquo;deux-point&amp;rdquo; ou en anglais &amp;ldquo;colon&amp;rdquo;. A quoi ça sert
? Je vous montre !&lt;/p&gt;
&lt;p&gt;La commande &amp;ldquo;colon&amp;rdquo;, donc &amp;ldquo;:&amp;rdquo;, est extra, elle ne fait absolument rien !
Bon ok, en fait elle fait des choses, déjà elle retourne toujours &amp;ldquo;0&amp;rdquo;
(zéro), donc elle réussit à tous les coups.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ :
$ echo $?
0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La première chose à quoi m&amp;rsquo;a servi cette commande est simple, j&amp;rsquo;ai des
scripts qui ne doivent pas planter, et ne doivent pas retourner autre
chose qu&amp;rsquo;un succès, par exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;mkdir -p /tmp/test/machin || :
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quoiqu&amp;rsquo;il arrive, que la commande &amp;ldquo;mkdir&amp;rdquo; ait fonctionné ou pas, le
retour est un EXIT_SUCCESS (donc zéro). C&amp;rsquo;est très utile par exemple
dans des &amp;ldquo;entrypoint&amp;rdquo; de Docker.io&lt;/p&gt;
&lt;p&gt;Mais si ça ne servait qu&amp;rsquo;à ça&amp;hellip; autant utiliser la commande &amp;ldquo;true&amp;rdquo;.
Sauf que &amp;ldquo;:&amp;rdquo; est vraiment un peu plus poussée, elle peut prendre des
paramètres &lt;em&gt;dont elle ne se sert pas&lt;/em&gt;. Du coup, on peut lui balancer une
chaîne, ou une sortie de commande qu&amp;rsquo;elle n&amp;rsquo;affichera pas. Et du coup on
commence à comprendre ce qu&amp;rsquo;on peut faire&amp;hellip;&lt;/p&gt;
&lt;p&gt;Je m&amp;rsquo;en sers pour taper un commentaire en plein milieu d&amp;rsquo;une commande:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;command; command; : un commentaire rapide; command; command
# parce que la ligne suivante ne marche pas, le dièse commente la fin de ligne:
command; command; # un commentaire rapide; command; command
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou alors, à faire un &amp;ldquo;while&amp;rdquo; rapidement&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;while :; do command; done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Une autre chose intéressante, remplacer une redirection de la sortie
standard ! Je vous rappelle les faits: la commande prend en argument
n&amp;rsquo;importe quoi, qu&amp;rsquo;elle n&amp;rsquo;affiche pas, donc:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# on peut faire
command | :

# equivalent à :
: `command`
: $(command)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notez que ces commandes &lt;strong&gt;retournent toujours &amp;ldquo;0&amp;rdquo; même si la commande a
planté !&lt;/strong&gt; C&amp;rsquo;est super intéressant pour lancer une commande dont on se
fiche du résultat, et dont on se fiche de la sortie standard (STDERR
continue à s&amp;rsquo;afficher)&lt;/p&gt;
&lt;p&gt;Donc, que ce soit en conjonction avec un &amp;ldquo;&amp;amp;&amp;amp;&amp;rdquo; pour assurer un succès de
sortie, pour commenter &amp;ldquo;en ligne&amp;rdquo; ou encore pour stripper la sortie
standard, la commande &amp;ldquo;deux-points&amp;rdquo; (dites &amp;ldquo;colon&amp;rdquo; c&amp;rsquo;est plus pro :)) va
vous faire plaisirs. A la longue, ça devient naturel.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Créer une API en GO avec Gorilla</title>
      <link>https://www.metal3d.org/blog/2014/cr%C3%A9er-une-api-en-go-avec-gorilla/</link>
      <pubDate>Fri, 12 Sep 2014 10:55:34 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2014/cr%C3%A9er-une-api-en-go-avec-gorilla/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Bon, on va parler Go ou Golang (faudra me dire un jour quel est le bon
nom) (en fait je sais, je vais le dire dans l&amp;rsquo;article tiens)&amp;hellip; Après la
naissance de mon outils &amp;ldquo;idok&amp;rdquo;, on discutait au boulot de la création
d&amp;rsquo;une API et de savoir quelle techno utiliser. Vous me connaissez&amp;hellip;
j&amp;rsquo;ai répondu &amp;ldquo;Go&amp;rdquo;. Mais comment on fait ? Y&amp;rsquo;a des packages qui aident ?
Hooo oui&amp;hellip;&lt;/p&gt;
&lt;p&gt;Bon en premier, on règle la question de suite:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go = la technologie, c&amp;rsquo;est le compilo, les outils&amp;hellip;&lt;/li&gt;
&lt;li&gt;Golang = le langage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est réglé, on passe à la suite.&lt;/p&gt;
&lt;h1 id=&#34;commençons-par-le-commencement&#34;&gt;Commençons par le commencement&lt;/h1&gt;
&lt;p&gt;En premier lieu, on regarde la base d&amp;rsquo;un projet HTTP en Go. C&amp;rsquo;est
finalement assez simple, on utilise le package &amp;ldquo;http&amp;rdquo; et on crée des
handlers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main
import (
    &amp;quot;fmt&amp;quot;
    &amp;quot;net/http&amp;quot;
)

// Un handler qui répondrait à /api/v1/user

func GetUser(w http.ResponseWriter, r *http.Request) {
    // du blabla
    // et on répond
    w.Write(&amp;quot;Un appel à GetUser...&amp;quot;)
}

// la fonction principale (et pas de jeu de mot avec main et pied)
func main(){
    http.HandleFunc(&amp;quot;/api/v1/user, GetUser)
    http.ListenAndServe(&amp;quot;:8080&amp;quot;, nil)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour l&amp;rsquo;exemple, je ne fais qu&amp;rsquo;écrire la réponse en texte. Vous pourrez
utiliser le package &amp;ldquo;encoding/json&amp;rdquo; ou &amp;ldquo;encoding/xml&amp;rdquo; pour répondre
comme il se doit en REST :)&lt;/p&gt;
&lt;p&gt;Bon le &amp;ldquo;blabla&amp;rdquo; de la fonction GetUser est pas expliqué, tout va
dépendre de votre API, de votre base de données et des traitements à
effectuer.&lt;/p&gt;
&lt;p&gt;Mais vous allez vite comprendre que tout ça c&amp;rsquo;est bien joli, mais ça
reste limité. Pourquoi ?&lt;/p&gt;
&lt;h2 id=&#34;problème-1---le-verbe-http&#34;&gt;Problème 1 - Le verbe HTTP&lt;/h2&gt;
&lt;p&gt;J&amp;rsquo;aimerai d&amp;rsquo;abord pouvoir répondre à un GET de &amp;ldquo;api/v1/user/12345&amp;rdquo;,
&amp;ldquo;12345&amp;rdquo; étant un id d&amp;rsquo;utilisateur. Mais aussi à un PUT, ou PATCH sur la
même URL&amp;hellip; Sauf que là, la fonction GetUser(), mal nommée en plus,
prend toutes les requêtes. Il faudrait alors tester &amp;ldquo;r.Method&amp;rdquo;, et pour
chaque cas appeler d&amp;rsquo;autres fonctions.&lt;/p&gt;
&lt;h2 id=&#34;problème-2---les-paramètre-dans-le-path&#34;&gt;Problème 2 - Les paramètre dans le path&lt;/h2&gt;
&lt;p&gt;Et en plus, récupérer le &amp;ldquo;12345&amp;rdquo; en fin d&amp;rsquo;URL, ça va qu&amp;rsquo;en on a que ça à
récupérer, mais quand on commence à jouer un peu ça devient vite
problématique.&lt;/p&gt;
&lt;p&gt;On pourrait commencer à coder un autre Muxer et créer la méthode
&amp;ldquo;ServeHTTP&amp;rdquo; comme le dit la doc. Mais honnêtement, on va réinventer une
roue sans être sûr de bien l&amp;rsquo;avoir fait circulaire.&lt;/p&gt;
&lt;h2 id=&#34;conclusion-faut-une-surcouche&#34;&gt;Conclusion, faut une surcouche&amp;hellip;&lt;/h2&gt;
&lt;p&gt;Bref, pour quelque chose de très simple, ça suffit amplement. Mais un
projet qui demande un peu plus de cuisine va vite vous condraindre à
vous battre avec du code en veux tu en voilà. Le module HTTP de Go n&amp;rsquo;est
pas fait pour proposer des règles d&amp;rsquo;écritures strictes. C&amp;rsquo;est un module
simple pour faire des choses simples. Pour faire plus compliqué, il faut
coder&amp;hellip; ça vous ennui ? Heureusement il y a &lt;strong&gt;Gorilla&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id=&#34;gorilla-le-couteau-suisse&#34;&gt;Gorilla, le couteau suisse&lt;/h1&gt;
&lt;p&gt;Voyez la page: &lt;a href=&#34;http://www.gorillatoolkit.org/&#34;&gt;http://www.gorillatoolkit.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Gorilla est un ensemble de packages utltra intéressant qui rend un
nombre de services vraiment conséquent. On va regarder gorilla/mux à la
page: &lt;a href=&#34;http://www.gorillatoolkit.org/pkg/mux&#34;&gt;http://www.gorillatoolkit.org/pkg/mux&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ce muxer est vraiment cool et nous permet de faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main

import (
    &amp;quot;fmt&amp;quot;
    &amp;quot;net/http&amp;quot;
    &amp;quot;github.com/gorilla/mux&amp;quot;
)

//... je vous passe les détail de fonctions, on va y revenir
func User(w http.ResponseWriter, r *http.Request) {
    // maintenant, je peux avoir:
    vars := mux.Vars(r) // récupère les valeurs d&#39;url

    // vars[&amp;quot;id&amp;quot;] contient l&#39;id utilisateur... 
    fmt.Fprintf(w, &amp;quot;On demande l&#39;utilisateur %s&amp;quot;, vars[&amp;quot;id&amp;quot;]) 

    // id =&amp;gt; string, pas le choix, sinon on le converti avec strconv
}

func main(){
    // on crée un muxer
    r := mux.NewRouter() 

    // remarquez le &amp;quot;id&amp;quot; qu&#39;on utilise dans User()...
    r.HandleFunc(&amp;quot;/api/v1/user/{id:[0-9]*}&amp;quot;, User)

    // cette fois on passe le muxer à ListenAndServe, et non pas &amp;quot;nil&amp;quot;
    http.ListenAndServe(&amp;quot;:8080&amp;quot;, r)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, on peut faire joujou avec l&amp;rsquo;id, mais on ne sait pas encore
faire un choix entre GET et PUT&amp;hellip; Et bien c&amp;rsquo;est toujours aussi simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func main() {
    // on crée un muxer
    r := mux.NewRouter() 

    // on attache les fonctions en fonction des methodes HTTP
    r.HandleFunc(&amp;quot;/api/v1/user/{id:[0-9]*}&amp;quot;, GetUser).Methods(&amp;quot;GET&amp;quot;)
    r.HandleFunc(&amp;quot;/api/v1/user/{id:[0-9]*}&amp;quot;, PutUser).Methods(&amp;quot;PUT&amp;quot;)

    // cette fois on passe le muxer à ListenAndServe, et non pas &amp;quot;nil&amp;quot;
    http.ListenAndServe(&amp;quot;:8080&amp;quot;, r)

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reste alors à créer les deux fonctions GetUser() et PutUser(). En
réalité il faut comprendre comment ça se passe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;r.HandleFunc() =&amp;gt; enregistre la route (mux.Route) dans le muxer et
retourne un mux.Route puis&amp;hellip;&lt;/li&gt;
&lt;li&gt;.Methods(&amp;hellip;) =&amp;gt; la route retournée par HandleFunc ne fonctionnera
qu&amp;rsquo;avec la ou les méthodes spécifiées (on donner plusieurs verbes
HTTP à Methods())&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il existe aussi des manières de gérer les domaines, des groupes de
Routes, de nommer une route pour créer les urls&amp;hellip; la doc est très
complète et je vous invite à la lire.&lt;/p&gt;
&lt;p&gt;Bon, c&amp;rsquo;est une introduction succinte, je sais, mais ça vous donne des
billes pour commencer.&lt;/p&gt;
&lt;p&gt;Si vous voulez non pas créer une API (qui répond en json ou xml par
exemple) mais plutôt faire une des pages WEB (en html), il faudra vous
pencher sur les packages &amp;ldquo;html/template&amp;rdquo; (dans le SDK de Go) ou sur
d&amp;rsquo;autres moteurs comme Pongo2 (&lt;a href=&#34;https://github.com/flosch/pongo2&#34;&gt;https://github.com/flosch/pongo2&lt;/a&gt;)
semblable à Jinja (python) ou Twig (pour php).&lt;/p&gt;
&lt;p&gt;Et coté ORM, base données&amp;hellip; on en reparlera&amp;hellip; J&amp;rsquo;utilise &amp;ldquo;mgo&amp;rdquo; le
paquet pour MongoDB (&lt;a href=&#34;https://labix.org/mgo&#34;&gt;https://labix.org/mgo&lt;/a&gt;) et j&amp;rsquo;ai beaucoup aimé
l&amp;rsquo;ORM (&amp;ldquo;O&amp;rdquo; étant sujet à discussion puisque Go n&amp;rsquo;a pas d&amp;rsquo;objet&amp;hellip;) nommé
&amp;ldquo;gorm&amp;rdquo; (&lt;a href=&#34;https://github.com/jinzhu/gorm&#34;&gt;https://github.com/jinzhu/gorm&lt;/a&gt;) que j&amp;rsquo;intègre à mon framework.&lt;/p&gt;
&lt;p&gt;En bref, ne venez pas me dire que Go est &amp;ldquo;pauvre en package pour se
simplifier le travail&amp;rdquo;, parce que là&amp;hellip; franchement&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans le prochain épisode, je vous montrerai comment on peu &amp;ldquo;packager&amp;rdquo;
nos handlers, et faire un fichier de configuration pour mapper les
requêtes. Avec ça, on peut rendre sympathique la configuration de notre
API.&lt;/p&gt;
&lt;p&gt;Et rien ne vous empêche de créer un service qui crée automatiquement
l&amp;rsquo;API&amp;hellip; en fait tout est possible :)&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Serveur RSTP avec gstreamer et python</title>
      <link>https://www.metal3d.org/blog/2014/serveur-rstp-avec-gstreamer-et-python/</link>
      <pubDate>Tue, 02 Sep 2014 18:15:42 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2014/serveur-rstp-avec-gstreamer-et-python/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Avec mon petit raspberry-pi je me suis lancé le défit de streamer mon
bureau sur la télé sans avoir à installer quoique ce soit sur la
framboise. Utiliser gstreamer sur mon pc et hop&amp;hellip; quelle idée&amp;hellip; en
fait, c&amp;rsquo;est pas simple du tout, mais j&amp;rsquo;ai trouvé la ruse du sioux.
Python, gstreamer, et un peu de recherche, voilà ma solution.&lt;/p&gt;
&lt;p&gt;Je vous préviens, c&amp;rsquo;est clairement améliorable. Ce billet de blog va
juste vous servir à capter le principe. Et puis ça fait une bonne entrée
en matière pour le RSTP via gstreamer :)&lt;/p&gt;
&lt;p&gt;Bon, l&amp;rsquo;idée c&amp;rsquo;est que la capture du bureau est un flux &amp;ldquo;temps réel&amp;rdquo;. Si
le client ne lit pas le flux, on a pas envie de garder le tampon. Ca
parait bête comme ça, mais c&amp;rsquo;est pourtant tout le problème. Heureusement
pour nous, il existe des protocoles de communication qui servent à ça.
Un d&amp;rsquo;entre eux, assez connu, est le RTP (Real Time Protocol). Et vous
savez quoi, avec gstreamer c&amp;rsquo;est pas si compliqué. Sauf que&amp;hellip;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai l&amp;rsquo;habitude de &amp;ldquo;gstreamer&amp;rdquo;, et je me suis dit (connement) qu&amp;rsquo;un flux
via le protocole RTP suffirait. Que nenni mon garçon, c&amp;rsquo;est pas si
simple !&lt;/p&gt;
&lt;p&gt;On commence d&amp;rsquo;abord par le début, comment faire ouvrir une adresse de
media au XBMC (ici sur raspberry pi).&lt;/p&gt;
&lt;p&gt;La solution la plus &amp;ldquo;simple&amp;rdquo; si je puis dire est de créer un répertoire
contenant un fichier avec l&amp;rsquo;extension &amp;ldquo;strm&amp;rdquo;. C&amp;rsquo;est un simple fichier
texte qui contien une addresse de flux. Et, pas de bol, xbmc ne connait
que 3 protocoles (à ce que je sais&amp;hellip;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;http - on oublie, pas de flux temps réel&lt;/li&gt;
&lt;li&gt;mms - mais bien sûr&amp;hellip;&lt;/li&gt;
&lt;li&gt;rtsp - c&amp;rsquo;est quoi ce &amp;ldquo;s&amp;rdquo; en trop ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On va revenir à ce &amp;ldquo;S&amp;rdquo; de RSTP après&amp;hellip; d&amp;rsquo;abord parlons du raspberry (ou
tout xbmc&amp;hellip;)&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc créé dans le &amp;ldquo;home&amp;rdquo; de mon utilisateur &amp;ldquo;pi&amp;rdquo; sur le raspberry,
un répertoire &amp;ldquo;mes_streams&amp;rdquo;, et un fichier &amp;ldquo;test.strm&amp;rdquo; contenant
l&amp;rsquo;adresse d&amp;rsquo;une vidéo (http). J&amp;rsquo;ai ajouté la source dans l&amp;rsquo;interface (le
répertoire) et en ouvrant le fichier, Xbmc va bel et bien lire la vidéo.&lt;/p&gt;
&lt;p&gt;Donc le fichier &amp;ldquo;test.strm&amp;rdquo; contient:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://une.adresse.vers/un.fichier.avi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hop, je lance ce fichier strm sur le raspberry et miracle, ça marche.&lt;/p&gt;
&lt;p&gt;Maintenant on va parler de RTSP&amp;hellip;&lt;/p&gt;
&lt;p&gt;Bon je vous l&amp;rsquo;ai dit, j&amp;rsquo;ai été idiot de croire que faire du RTSP était
facile. RTSP est un protocole de streaming (Real Time Streaming
Protocol) qui utilise RTP (Real Time Protocol). Sauf que voilà, ajouter
un &amp;ldquo;S&amp;rdquo; à RTP ne suffit pas :)&lt;/p&gt;
&lt;p&gt;On va détailler, c&amp;rsquo;est le but de ce ticket.&lt;/p&gt;
&lt;p&gt;RTP c&amp;rsquo;est le protocole qui encapsule les trames de media.&lt;/p&gt;
&lt;p&gt;Gstreamer a une chiée de plugins pour créer des &amp;ldquo;payloads&amp;rdquo; RTP. Un
Payload est un contenu formaté correctement pour définir les type de
media transférés et comment les lire. Bref, un gros bouzin compliqué
qu&amp;rsquo;on ne va pas creuser en détail.&lt;/p&gt;
&lt;p&gt;Ainsi, pour créer un payload h264, on peut faire ça:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch videotestsrc ! x26enc ! rtph264pay ! ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui va vous intéresser, c&amp;rsquo;est ce qui suit (les trois petits points
que j&amp;rsquo;ai laissé). Car en définitive, là on crée bien un payload, mais on
l&amp;rsquo;envoit nulle part.&lt;/p&gt;
&lt;p&gt;Et c&amp;rsquo;est là que j&amp;rsquo;ai déchanté&amp;hellip; en fait il n&amp;rsquo;existe pas de (librement)
de plugin RTSP. J&amp;rsquo;avais commencé à tenter de balancer le payload dans un
&amp;ldquo;udpsink&amp;rdquo; mais sans succés. Non monsieur, RTSP c&amp;rsquo;est pas si simple.&lt;/p&gt;
&lt;p&gt;Parce que moi, je me suis dit &amp;ldquo;allez te prend pas la tête, balance ça en
udp&amp;hellip;&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch videotestsrc ! x264enc ! rtph264pay ! udp host=127.0.0.1 port=1234
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et je l&amp;rsquo;ouvrai avec:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch udpsrc port=1234 ! rtph264depay ! decodebin ! autovideosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tu parles ! déjà ça marche pas, car il faut définir le &amp;ldquo;caps&amp;rdquo; à lire, en
plus le raspberry il comprend rien à &amp;ldquo;&lt;a href=&#34;udp://&#34;&gt;udp://&lt;/a&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Et oui, en fait, le RTSP est un protocole de serveur spécifique qui sert
justement à tout ça. M&amp;rsquo;a fallu un moment pour comprendre&amp;hellip;&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc passé des heures à chercher, lire des docs, et je suis tombé
sur des exemples en C. C&amp;rsquo;est donc avec dépis que je me dis &amp;ldquo;ok, on ne
peut pas le faire avec gst-launch, mais on va se le faire en python&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Effectivement, il faut une librairie spécifique pour créer un serveur
rtsp. Et heureusement pour nous, c&amp;rsquo;est en général packagé pour python,
C/C++, et autres. Alors comme moi j&amp;rsquo;adore le python, on va pas se
priver.&lt;/p&gt;
&lt;p&gt;On commence par installer les packages, sur fedora ça donne:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo yum install gstreamer-python gstreamer-rtsp-python
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Petite remarque&amp;hellip; pas de possibilité avec gstreamer-1.0 apparement, à
ce jour.&lt;/p&gt;
&lt;p&gt;Bon, on y va&amp;hellip; on crée le fichier &amp;ldquo;rtsp_test.py&amp;rdquo;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import pygst

pygst.require(&amp;quot;0.10&amp;quot;)

import gst
import gobject
import sys

from gst import rtspserver

server=rtspserver.Server()

factory = rtspserver.MediaFactory()
factory.set_launch(&amp;quot; videotestsrc ! ffmpegcolorspace ! x264enc ! rtph264pay name=pay0 pt=96 &amp;quot;)

server.get_media_mapping().add_factory(&#39;/test&#39;, factory)
server.attach()

try:
    gobject.MainLoop().run()
except KeyboardInterrupt :
    print &amp;quot;bye !&amp;quot;
    sys.exit()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;J&amp;rsquo;explique. RTSP nécessiste une factory, c&amp;rsquo;est une usine à stream. Au
moment où un client se connecte, il lance si besoin la création du
stream. Ensuite on ajoute cette factory au serveur en lui spécifiant le
chemin. Ainsi &amp;ldquo;/test&amp;rdquo; correspond au &amp;ldquo;test vidéo&amp;rdquo; que je fais (avec la
mire videotestsrc)&lt;/p&gt;
&lt;p&gt;Si je lance ce script, et que je vais ouvrir (avec vlc, totem, au choix)
&lt;a href=&#34;rtsp://127.0.0.1:8554/test&#34;&gt;rtsp://127.0.0.1:8554/test&lt;/a&gt; alors je vois cette fameuse mire !&lt;/p&gt;
&lt;p&gt;En mettant ce chemin dans le fichier test.strm de mon raspberry:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rtsp://192.168.1.29:8554/test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et bien ça fonctionne ! (192.168.1.29 est l&amp;rsquo;ip de mon pc, et j&amp;rsquo;ai ouvert
le port 8554).&lt;/p&gt;
&lt;p&gt;Ha au fait, pour ouvrir rapidos le port sur le pc:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;firewall-cmd --add-port=8554/tcp --zone=public
firewall-cmd --add-port=8554/udp --zone=public
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quand le service de firewall se rechargera (avec reload&amp;hellip;) ou au
redémarrage du pc, ce port sera fermé. Si vous voulez garder la conf,
ajoutez &amp;ldquo;&amp;ndash;permanent&amp;rdquo; aux deux commandes précédentes.&lt;/p&gt;
&lt;p&gt;Rest à ajouter un autre chemin, celui qui capture le bureau.&lt;/p&gt;
&lt;p&gt;Ajoutez, juste après la création de la factory de test:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;factorydesktop = rtspserver.MediaFactory()
factorydesktop.set_launch(&amp;quot; ximagesrc ! ffmpegcolorspace ! x264enc ! rtph264pay name=pay0 pt=96 &amp;quot;)
server.get_media_mapping().add_factory(&#39;/desktop&#39;, factorydesktop)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et relancez le script.&lt;/p&gt;
&lt;p&gt;Vous avez donc 2 chemins:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;rtsp://192.168.1.29:8554/test&#34;&gt;rtsp://192.168.1.29:8554/test&lt;/a&gt; qui montre une mire&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;rtsp://192.168.1.29:8554/desktop&#34;&gt;rtsp://192.168.1.29:8554/desktop&lt;/a&gt; qui montre votre bureau&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reste à créer un autre fichier strm sur le raspberry-pi (ou votre XBMC
encore une fois) et d&amp;rsquo;ouvrir ce dernier&amp;hellip; attendez quelques secondes
(la latence, j&amp;rsquo;ai pas trouvé comment la réduire) et hop voilà votre
bureau sur votre télé :)&lt;/p&gt;
&lt;p&gt;Bon voilà, le résultat est sympa, ça ne permet pas de faire grand-chose
mais c&amp;rsquo;est intéressant de comprendre le principe.&lt;/p&gt;
&lt;p&gt;Sachez que vous pouvez y mettre ce que vous voulez dans le flux RTP, là
on a mis de la vidéo (la capture de bureau ou la mire de test), mais les
&amp;ldquo;payloader&amp;rdquo; RTP de gstreamer sont nombreux. On peut donc streamer la
musique, le son du pc, des images, etc&amp;hellip; Il suffit de savoir faire un
pipeline gstreamer. J&amp;rsquo;ai traité ça ici
&lt;a href=&#34;http://metal3d.org/ticket/2012/08/13/didacticiel-gstreamer&#34;&gt;http://metal3d.org/ticket/2012/08/13/didacticiel-gstreamer&lt;/a&gt; et là
&lt;a href=&#34;http://metal3d.org/ticket/2012/08/14/gstreamer-la-suite&#34;&gt;http://metal3d.org/ticket/2012/08/14/gstreamer-la-suite&lt;/a&gt;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Installer localement Golang</title>
      <link>https://www.metal3d.org/blog/2014/installer-localement-golang/</link>
      <pubDate>Thu, 31 Jul 2014 13:33:48 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2014/installer-localement-golang/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Toujours en cours d&amp;rsquo;écriture d&amp;rsquo;un livre sur le langage Go, ou du moins
sur la pratique de ce langage, je mets de temps à autres des petits
articles sur mon blog pour vous donner quelques points clefs. Et
aujourd&amp;rsquo;hui, je vais vous montrer comment installer &amp;ldquo;localement&amp;rdquo; le
compilateur et mettre les variables d&amp;rsquo;environnement au clair.&lt;/p&gt;
&lt;p&gt;Go (&lt;a href=&#34;http://golang.org&#34;&gt;http://golang.org&lt;/a&gt;) est un langage, mais il vient avec sa suite
d&amp;rsquo;outils et une gestion de package téléchargeables depuis le net. Les
paquets (packages) s&amp;rsquo;installent dans un répertoire et sont directement
importé à la demande lors de la compilation&amp;hellip; à condition de dire au
compilateur où ils se trouvent. C&amp;rsquo;est le travail de la variable
d&amp;rsquo;environnement GOPATH&lt;/p&gt;
&lt;p&gt;GOROOT quant à elle, est utilisé pour savoir où trouver les outils
(gotest, govet, gofmt&amp;hellip;) et autres joyeusetés. Depuis la version 1.1,
il est nécessaire de séparer les deux répertoires, ce qui (à mon goût)
n&amp;rsquo;est pas super pratique au premier abord, car malheureusement Go ne
donne pas de chemin par défaut pour GOPATH qui soit viable. En d&amp;rsquo;autres
termes, quand vous allez utiliser &amp;ldquo;go install XXX&amp;rdquo;, vous allez vous
prendre une belle erreur qui vous dit que vous devez fournir un chemin
pour les packages externes.&lt;/p&gt;
&lt;p&gt;Pour ma part, j&amp;rsquo;ai pas mal d&amp;rsquo;autres applications non packagées que j&amp;rsquo;ai
installé &amp;ldquo;localement&amp;rdquo;. Quand je parle de &amp;ldquo;local&amp;rdquo;, je parle en fait de
mon répertoire utilisateur. Et comme je n&amp;rsquo;aime pas attendre 6 mois pour
avoir un paquet de Go mis à jour dans les paquets de ma distributions,
y&amp;rsquo;a des choses que j&amp;rsquo;installe à la mimine.&lt;/p&gt;
&lt;p&gt;Bref, dans notre exemple, on parle de Go.&lt;/p&gt;
&lt;p&gt;Personnellement, j&amp;rsquo;utilise une configuration d&amp;rsquo;installation qui me
permet de ne pas me mélanger les paluches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la distro de Go dans ~/.go&lt;/li&gt;
&lt;li&gt;le reste dans ~/Projets/goprojects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pourquoi ?&lt;/p&gt;
&lt;p&gt;Le répertoire de distribution de Go, normalement, vous n&amp;rsquo;avez pas à y
toucher. Donc on se fiche de le voir apparaître dans le &amp;ldquo;home&amp;rdquo;
directement. Je rappelle que sous Linux/Unix un point devant un nom de
fichier sert à cacher le fichier (ou le répertoire)&lt;/p&gt;
&lt;p&gt;Par contre, il est conseillé de développer dans le GOPATH/src afin de ne
pas se battre avec les répertoires lors des instruction d&amp;rsquo;import de vos
programmes. Comme je ne code pas que en Go, j&amp;rsquo;ai un répertoire Projets
dans mon home, et tout ce qui est &amp;ldquo;externes à la distributions de go&amp;rdquo; va
être placée dans ce répertoire.&lt;/p&gt;
&lt;p&gt;Voilà comment ça se passe.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget http://golang.org/dl/go1.3.linux-amd64.tar.gz
tar xf go1.3.linux-amd64.tar.gz
mv go ~/.go
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc, là, on vient de télécharger le paquet de Go, on a décompressé le
répertoire qui a créé ~/go. Je le déplace en &amp;ldquo;~/.go&amp;rdquo; pour le cacher de
ma vue.&lt;/p&gt;
&lt;p&gt;On crée maintenant le répertoire pour installer les paquets externes et
développer nos propre packages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p ~/Projets/goprojects
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reste plus qu&amp;rsquo;à changer les variables qui vont bien dans ~/.bashrc:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export GOROOT=~/.go
export GOPATH=~/Projets/goprojects
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La variable PATH comprend donc le répertoire ~/.go/bin (qui contient
les commandes go) et le répertoire ~/Projets/gprojects/bin où seront
ajoutés les programmes externes (comme par exemple godev, gocode&amp;hellip;).&lt;/p&gt;
&lt;p&gt;Reste plus qu&amp;rsquo;à essayer, soit vous faites:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Soit vous vous reloguez, ou vous ouvrez un autre terminal. Si vous
tapez:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ca doit bien se passer&amp;hellip;&lt;/p&gt;
&lt;p&gt;Installez un paquet, genre gocode (dont je suis fan, je l&amp;rsquo;utilise à
foison via vim):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go get -u github.com/nsf/gocode
gocode -h
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ca marche, la commande gocode est bien accessible.&lt;/p&gt;
&lt;p&gt;Voilà, de cette manière vous avez un bon contrôle sur votre installation
locale de Go, et en plus c&amp;rsquo;est facile à virer&amp;hellip; le jour où vous voulez
tout supprimer:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rm -rf ~/.go ~/Projets/goprojects
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et le tour est joué.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Le modèle CSS3 flex-box</title>
      <link>https://www.metal3d.org/blog/2014/le-mod%C3%A8le-css3-flex-box/</link>
      <pubDate>Sun, 13 Jul 2014 13:37:37 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2014/le-mod%C3%A8le-css3-flex-box/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Que ce soit sur téléphone, tablette ou bureau, un des casse-têtes
quotidiens des intégrateurs HTML est la position des boites et leur
étirement. En gros, on cherche toujours à ce qu&amp;rsquo;une image soit
positionné à coté d&amp;rsquo;un bloc de texte, le tout dans un conteneur qui
s&amp;rsquo;étire de la même manière que le reste&amp;hellip; basé sur le contenu de ce qui
l&amp;rsquo;entoure. En bref, l&amp;rsquo;arrachage de cheveux dans les règles. Mais en
fait, il existe une série de directives CSS3 qui soulage fortement le
monteur, bien que mal connue, le système de &amp;ldquo;flex&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Flex, comme flexible, étirable. A ne pas confondre avec Flex pour Flash.
Ainsi, au lieu de parler de &amp;ldquo;flex&amp;rdquo; qui, dans Google, remonte des
articles parlant de cette techno Flash, il vaut mieux parler de
&amp;ldquo;flex-boxes CSS3&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Ce qu&amp;rsquo;il y a de sympa avec ces directives, c&amp;rsquo;est qu&amp;rsquo;elle marche presque
partout. De IE à Chrome en passant par Firefox&amp;hellip; ça se passe pas mal.
Bon ok, souvent il faudra préfixer la directive pour les version un peu
anciennes et permettre d&amp;rsquo;avoir le comportement attendu. Si vous utilisez
Less ou Sass, ça ira très vite.&lt;/p&gt;
&lt;p&gt;Je ne vais pas vous faire un cours magistral sur toutes les possibilités
des flex boxes, mais vous montrer deux ou trois exemples qui m&amp;rsquo;ont
permis de monter une interface HTML qui tourne sur pratiquement toutes
les plateformes que je cible. A savoir, une interface simple sur
Android, Firefox et Chrome. Je n&amp;rsquo;ai pas testé sur IE, mais je pense que
ça se passera bien.&lt;/p&gt;
&lt;p&gt;Avant tout, c&amp;rsquo;est quoi le système de flex ?&lt;/p&gt;
&lt;style&gt;
iframe {
    border: 1px solid black
}
&lt;/style&gt;
&lt;h1 id=&#34;des-boites-élastiques&#34;&gt;Des boites élastiques&lt;/h1&gt;
&lt;p&gt;Les flex boxes permettent de faire en sorte qu&amp;rsquo;un conteneur (div, main,
body&amp;hellip;) s&amp;rsquo;étire et place son contenu en fonction du reste. Pour que
votre boite gère le principe flexible, il suffit de donner donner la
valeur &amp;ldquo;flex&amp;rdquo; à la directive &amp;ldquo;display&amp;rdquo;. Ca donne ça:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-css&#34;&gt;div {
    display: flex;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Me dites pas que c&amp;rsquo;était compliqué ! Bon ok, là on a fait que le début.&lt;/p&gt;
&lt;p&gt;Ensuit va venir les attributs de flexibilité, on va permettre à une
boite de gérer son contenu en colonne, en ligne, de se placer avant ou
après le reste, et surtout de savoir combien de place par rapport au
reste elle va prendre.&lt;/p&gt;
&lt;p&gt;Les directives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;display: flex; avec des prefix -ms -webkit -moz&amp;hellip; pour les vieux
navigateurs, ça se passera bien.&lt;/li&gt;
&lt;li&gt;flex: G S B; G et S =&amp;gt; flex-grow (entier), S =&amp;gt; flex-shrink
(entier) et B =&amp;gt; flex-basis (auto, ou taille), par défaut la valeur
est &amp;ldquo;0 1 auto&amp;rdquo;&lt;/li&gt;
&lt;li&gt;flex-direction: column|row|row-reverse&amp;hellip;; y&amp;rsquo;en a plein&lt;/li&gt;
&lt;li&gt;order: N; Ordre d&amp;rsquo;affichage. Permet de placer une boite avant un
autre, sans bouger le DOM (ça c&amp;rsquo;est cool hein)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour clarifier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;flex-grow: force l&amp;rsquo;espacement par rapport aux autres élément dans le
conteneur. Si vous avez 3 boites, et que vous donner la valeur 1 à
deux boites, et 3 à une boite, cette dernière sera 3 fois plus
grande que les deux autres&amp;hellip;&lt;/li&gt;
&lt;li&gt;flex-shrink: par défaut à 1, permet de réduire la taille de la boite
si nécessaire&lt;/li&gt;
&lt;li&gt;flex-basis: défini la taille avant que l&amp;rsquo;espacement soit distribué
aux boites. &amp;ldquo;auto&amp;rdquo; par défaut.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ça peut paraître un peu compliqué comme ça, mais en fait c&amp;rsquo;est super
simple&amp;hellip; surtout quand on le met en pratique. Et on va le faire :)&lt;/p&gt;
&lt;h1 id=&#34;mise-en-pratique-le-layout-cardinal&#34;&gt;Mise en pratique: le layout cardinal&lt;/h1&gt;
&lt;p&gt;Le layout cardinal est simple. On veut 5 boites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une entête (bannière, titre&amp;hellip;)&lt;/li&gt;
&lt;li&gt;3 colonnes pour la navigation, le contenu principal et une barre
d&amp;rsquo;info à droite&lt;/li&gt;
&lt;li&gt;un footer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le DOM va être bêtement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;header&amp;gt;HEADER&amp;lt;/header&amp;gt;
&amp;lt;div id=&amp;quot;content&amp;quot;&amp;gt;
    &amp;lt;main&amp;gt;MAIN CONTENT&amp;lt;/main&amp;gt;
    &amp;lt;nav&amp;gt;NAV LEFT&amp;lt;/nav&amp;gt;
    &amp;lt;aside&amp;gt;ASIDE RIGHT&amp;lt;/aside&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;footer&amp;gt;FOOTER&amp;lt;/footer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous remarquez que j&amp;rsquo;ai placé le bloc &amp;ldquo;main&amp;rdquo; avant la navigation et le
bloc aside, alors qu&amp;rsquo;on veut l&amp;rsquo;avoir visuellement après la navigation.
On va gérer ça facilement avec la directive &amp;ldquo;order&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Ce qu&amp;rsquo;on va faire, pour commencer, c&amp;rsquo;est définir que nous gérons le mode
&amp;ldquo;flex&amp;rdquo; pour tout nos blocs définis. Allez c&amp;rsquo;est pas compliqué (et je le
fais en pur CSS):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-css&#34;&gt;header, #content, footer {
    display: -webkit-box;
    display: -moz-box;
    display: -ms-flexbox;
    display: -webkit-flex;
    display: flex;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ca c&amp;rsquo;est fait&amp;hellip; Ca donne ça:&lt;/p&gt;
&lt;iframe src=&#34;https://examples.metal3d.org/flex/index1.html&#34; frameborder=&#34;0&#34; style=&#34;width: 100%; height: 200px&#34;&gt;&lt;/iframe&gt;
Bon j&#39;ai ajouté de la couleur et une marge pour qu&#39;on se rendre mieux
compte des boites.
&lt;p&gt;On remarque donc, d&amp;rsquo;entrée de jeu, que les boites contenu &amp;ldquo;#content&amp;rdquo; se
trouvent en ligne, côte à côte, mais jusque là, rien d’impressionnant.
On pourrait le faire à base de float ou de &amp;ldquo;inline-block&amp;rdquo;. Voyons la
suite.&lt;/p&gt;
&lt;p&gt;Ce qu&amp;rsquo;on doit faire, maintenant, c&amp;rsquo;est forcer le fait que le bloc &amp;ldquo;main&amp;rdquo;
s&amp;rsquo;étire. On ajoute simplement &amp;ldquo;flex: 1&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-css&#34;&gt;main {
    flex: 1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et ça donne:&lt;/p&gt;
&lt;iframe src=&#34;https://examples.metal3d.org/flex/index2.html&#34; frameborder=&#34;0&#34; style=&#34;width: 100%; height: 200px&#34;&gt;&lt;/iframe&gt;
Bon, pas mal :), le bloc main est bien étiré, quelque soit le padding,
le margin des conteneurs, tout se cale correctement et c&#39;est déjà un
grand pas !
&lt;p&gt;Mais je sais ce que vous allez me dire&amp;hellip; oui, ok mais si on met du
contenu dans &amp;ldquo;main&amp;rdquo;, ça étire pas les bloc de navigation et aside dans
la hauteur. La preuve que si:&lt;/p&gt;
&lt;iframe src=&#34;https://examples.metal3d.org/flex/index3.html&#34; frameborder=&#34;0&#34; style=&#34;width: 100%; height: 550px&#34;&gt;&lt;/iframe&gt;
Avouez le, ça vous éclate :) Et on a presque pas codé de CSS, on a pas
une ligne de JS.
&lt;p&gt;Reste le dernier point, le truc qui fâche, comment faire pour que la
taille totale colle à 100% de la hauteur de la page ? Ou d&amp;rsquo;un conteneur
qui l&amp;rsquo;englobe.&lt;/p&gt;
&lt;p&gt;Et bien c&amp;rsquo;est super simple. On va juste forcer le fait que &amp;ldquo;body&amp;rdquo; soit
un model de boite, on lui donne une taille de 100% en hauteur. Ensuite
on demande à ce que &amp;ldquo;#content&amp;rdquo; fasse aussi 100% de la hauteur. Là où ça
devient intéressant, c&amp;rsquo;est que le modèle de boite se débrouille pour que
&amp;ldquo;100%&amp;rdquo; ne soit pas destructif. Il calcule son espace par rapport aux
autres boites. Et du coup, ça colle parfaitement, quelque soit la taille
de la fenêtre, quelque soit le contenu&amp;hellip;&lt;/p&gt;
&lt;p&gt;Comme on fait des lignes (header au dessus de #content qui est au
dessus de footer), le body est une grosse colonne.&lt;/p&gt;
&lt;p&gt;On va donc faire ça (j&amp;rsquo;ai viré les valeurs préfixées sinon ça fait trop
de choses à lire):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-css&#34;&gt;/* On force la taille de la page... et on modèle de boite global */
body, html {
    display: flex;
    flex-direction: column;
    height: 100%
}

/* et on s&#39;arrange pour que le contenu (ligne 2) prenne toute la place */
#content {
    height: 100%
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;J&amp;rsquo;ai ajouté aussi un overflox pour la balise &amp;ldquo;main&amp;rdquo; car le texte peut
déborder si vous réduisez la fenêtre. Bref, sans trop forcer, j&amp;rsquo;arrive à
ça:&lt;/p&gt;
&lt;iframe src=&#34;https://examples.metal3d.org/flex/index4.html&#34; frameborder=&#34;0&#34; style=&#34;width: 100%; height: 550px&#34;&gt;&lt;/iframe&gt;
Notez bien que j&#39;ai enlevé des paragraphes pour vous prouver que la
taille des blocs s&#39;étirent en hauteur.
&lt;p&gt;Aucun calcul, pas de JS, tout s&amp;rsquo;automatise&amp;hellip; si c&amp;rsquo;est pas génial ça !&lt;/p&gt;
&lt;h1 id=&#34;et-le-responsive-dans-tout-ça-&#34;&gt;Et le responsive dans tout ça ?&lt;/h1&gt;
&lt;p&gt;Ce qui est sidérant, c&amp;rsquo;est que ce modèle de boite permet de faire des
trucs vraiment puissant. Et quand on parle de responsive design on se
rend compte que tout est là pour nous rendre la vie douce est
mélodieuse.&lt;/p&gt;
&lt;p&gt;Reprenons, on a &amp;ldquo;aside&amp;rdquo;, &amp;ldquo;main&amp;rdquo; et &amp;ldquo;nav&amp;rdquo; cote à cote. Mais quand on va
descendre en résolution, on va avoir bobo&amp;hellip; Il suffit simplement de
dire à notre modèle qu&amp;rsquo;en dessous de 600px de largeur, notre bloc
&amp;ldquo;#content&amp;rdquo; est un modèle en colonnes. Comment ? simplement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-css&#34;&gt;@media all and (max-width: 600px) {
    #content {
        flex-flow: column
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais en plus, pour l&amp;rsquo;exemple, je veux que le bloc &amp;ldquo;nav&amp;rdquo; apparaisse après
le bloc main. Avant on devait se taper un JS terrible de déplacement
d&amp;rsquo;objet dans le DOM. Avec CSS et le modèle de boite&amp;hellip; comment dire&amp;hellip;
c&amp;rsquo;est d&amp;rsquo;un con&amp;hellip;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-css&#34;&gt;@media all and (max-width: 600px) {
    #content {
        flex-flow: column
    }

    nav, aside {
        order: 2
    }
    main {
        order: 1
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là, la magie:&lt;/p&gt;
&lt;iframe src=&#34;https://examples.metal3d.org/flex/index4.html&#34; frameborder=&#34;0&#34; style=&#34;width: 600px; height: 550px&#34;&gt;&lt;/iframe&gt;
Je vous laisse tenter le coup, ouvrez cet exemple:
&lt;https://examples.metal3d.org/flex/index4.html&gt; et amusez vous à
redimensionner la fenêtre. Vous allez voir les boites bouger en fonction
de la taille du navigateur. C&#39;est tout simplement pratique, facile,
clair et efficace.
&lt;h1 id=&#34;alors-pourquoi-personne-ny-pense-&#34;&gt;Alors, pourquoi personne n&amp;rsquo;y pense ?&lt;/h1&gt;
&lt;p&gt;Honnêtement, je ne sais pas pourquoi la communication est mal passée sur
ce principe de boite. Je l&amp;rsquo;ai découvert il y a peu de temps et j&amp;rsquo;ai
l&amp;rsquo;impression qu&amp;rsquo;on est encore peu nombreux à avoir analysé et testé ce
modèle CSS.&lt;/p&gt;
&lt;p&gt;Pourtant, il répond pratiquement à tous les soucis existants. Il permet
de caler parfaitement du contenu en bas de bloc (par exemple un bloc de
produit donc le prix doit apparaître en bas de div), de faire des
colonne flexibles, de résoudre des problèmes de largeur et de hauteurs.
Ca marche bien sur mobile et les prefix vendor sont apparemment présents
de puis des années&amp;hellip;&lt;/p&gt;
&lt;p&gt;Aujourd&amp;rsquo;hui, le mode boite est validée, et je suis persuadé qu&amp;rsquo;on va en
entendre parler, et surtout qu&amp;rsquo;il va être fortement utilisé par la
suite. Fini les table-cell et autre calc() CSS, terminé les padding avec
des bloc flottants là où on en a pas besoin. Le modèle flexbox va
fleurir.&lt;/p&gt;
&lt;p&gt;Je ne vous ai montré qu&amp;rsquo;un infime partie de ce que permet ce modèle, je
vous conseille d&amp;rsquo;aller voir deux trois pages que j&amp;rsquo;ai trouvé
intéressantes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://www.w3.org/TR/css3-flexbox/&#34;&gt;http://www.w3.org/TR/css3-flexbox/&lt;/a&gt; =&amp;gt; la référence avec des
exemples de bloc produit&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.alsacreations.com/tuto/lire/1493-css3-flexbox-layout-module.html&#34;&gt;http://www.alsacreations.com/tuto/lire/1493-css3-flexbox-layout-module.html&lt;/a&gt;
=&amp;gt; très bien expliqué de la part de alsacréation&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://css-tricks.com/snippets/css/a-guide-to-flexbox/&#34;&gt;http://css-tricks.com/snippets/css/a-guide-to-flexbox/&lt;/a&gt; =&amp;gt; page
avec des exemples et pas mal de simplifications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voilà pour aujourd&amp;rsquo;hui :)&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Générateur de tests Python pour nose</title>
      <link>https://www.metal3d.org/blog/2014/g%C3%A9n%C3%A9rateur-de-tests-python-pour-nose/</link>
      <pubDate>Wed, 18 Jun 2014 22:39:24 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2014/g%C3%A9n%C3%A9rateur-de-tests-python-pour-nose/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Python, j&amp;rsquo;en parle beaucoup, mais je l&amp;rsquo;utilise aussi pas mal. Faut dire,
je bosse à 99% du temps sur ce langage. Et aujourd&amp;rsquo;hui, avec mon acolyte
Jonathan Dray, on a réalisé un module qui nous a fait un peu plancher,
mais qui apporte beaucoup à notre projet: des tests automatiques,
c&amp;rsquo;est-à-dire générés. Ca peut paraitre touchy, overkill ou tout les mots
à la mode que vous voulez, je vous assure (et vous allez le voir) que ça
peut vous apporter beaucoup&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;au-début-il-ya-avait-un-beau-foutoir&#34;&gt;Au début, il y&amp;rsquo;a avait un beau foutoir&lt;/h1&gt;
&lt;p&gt;Je vous passe les détails. On a un module qui demande des tests
hebdomadaires (parce que c&amp;rsquo;est un peu long à traiter). Pour simplifier
l&amp;rsquo;explication: on parle des données sur le web, pour s&amp;rsquo;assurer que nos
règles de parsing restent valables j&amp;rsquo;avais imaginé ce genre d&amp;rsquo;archi:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;expected/
    siteA.py
    siteB.py
    siteC.py
    ...
test_SiteA.py
test_SiteB.py
test_SiteC.py
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans &amp;ldquo;expected.siteA&amp;rdquo; on a un truc du genre:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;data = {&#39;un titre de test&#39;; {
        &#39;url&#39; : &#39;http://...&#39;,
        &#39;expected&#39; : {
            # ... des datas traitées depuis cet url
        }
    },
    {&#39;un autre titre&#39;: {
        #idem qu&#39;au dessue, une url, des expected
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La raison pour laquelle j&amp;rsquo;avais sorti les &amp;ldquo;expected&amp;rdquo; (données attendues)
c&amp;rsquo;est que les données à tester sont relativement lourdes&amp;hellip; Du coup,
dans les tests unutaire on avait (en résumé):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from expected import siteA
# un truc de base qui gère des trucs redondants
from tests import testbase

# la class de test...
class TestSiteA(testbase):

    def test_url1(self):
        parsed = self.parse(siteA.data[&#39;un titre de test&#39;][&#39;url&#39;])
        self.assertEquals(parsed, siteA.data[&#39;un titre de test&#39;][&#39;expected&#39;])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et ce, répété pour chaque site, pour chaque url du dictionnaire &amp;ldquo;data&amp;rdquo;
contenu dans les module&amp;hellip; bref, imbuvable même sous la contrainte.
Jonathan pète un câble et me dit &amp;ldquo;non mais c&amp;rsquo;est complètement con, on
copie colle là&amp;rdquo;. Et hors de question de faire une boucle&amp;hellip; pouquoi ?
c&amp;rsquo;est simple comme choux: on utilise nose, on va perdre des options
cool.&lt;/p&gt;
&lt;h1 id=&#34;pas-de-boucle-et-pour-cause&#34;&gt;Pas de boucle, et pour cause&lt;/h1&gt;
&lt;p&gt;Je reviens à la dernière remarque. Si Jonathan n&amp;rsquo;avait pas été là,
j&amp;rsquo;aurais bêtement fait un truc qui charge toutes les données &amp;ldquo;expected&amp;rdquo;,
et puis roulez jeunesse. Mais si un test foire, chaque appel à
&amp;ldquo;nosetests&amp;rdquo; m&amp;rsquo;aurait rejoué toute la séquence.&lt;/p&gt;
&lt;p&gt;Parce que voilà, nose est cool, il permet de faire ce genre de chose:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nosetests -sv test:TestSiteA.test_url1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors le truc à faire, c&amp;rsquo;est donc de savoir générer les tests&amp;hellip; mais
vous l&amp;rsquo;aurez compris, on ne va pas réellement les générer en tant que
fichier, &lt;strong&gt;non&lt;/strong&gt;, car Python est une tuerie en terme de réflexion et
metaclasse. On va lire de la doc et créer un truc qui fait ça à la mode
bourinnasse.&lt;/p&gt;
&lt;h1 id=&#34;faut-que-vous-rencontriez-ce-type&#34;&gt;Faut que vous rencontriez ce type()&lt;/h1&gt;
&lt;p&gt;Vous me comprennez, je veux pouvoir utiliser &amp;ldquo;nose&amp;rdquo; comme si j&amp;rsquo;avais
créé les classes nécessaires, sachant qu&amp;rsquo;elles font toutes à peu près la
même chose. Le tout, c&amp;rsquo;est d&amp;rsquo;avoir un nom de classe consistant, et des
méthodes de tests qui correspondent à peu près au titre des données
attendues.&lt;/p&gt;
&lt;p&gt;Générer une classe est ultra simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;cl = type(nom, (parent1,...), {propriétés...})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;ldquo;cl&amp;rdquo; sera la classe elle-même, et je dis bien &amp;ldquo;la classe&amp;rdquo;, et non un
objet. Ca sent bon ça, mais le souci, c&amp;rsquo;est que je dois l&amp;rsquo;assigner au
module courant. Toujours aussi simple, on utilise le module &amp;ldquo;sys&amp;rdquo; qui
fourni la liste des modules chargés, y compris celui en cours. Et
comment on connait le nom du module courrant ? Bha oui: __name__&lt;/p&gt;
&lt;p&gt;Voyons voir:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import sys
import unitest

class BaseTest(unittest.TestCase)
    &amp;quot;&amp;quot;&amp;quot; Classe de base, on y reviendra &amp;quot;&amp;quot;&amp;quot;
    pass

def init():
    # hop une référence au module en cours
    current = sys.modules[__name__]

    # génère une classe pour faire mumuse
    cl = type(&amp;quot;Classe1&amp;quot;, (BaseTest,), {})

    # on l&#39;assigne au module en cours
    setattr(current, cl.__name__, cl)

init()
import pdb; pdb.set_trace()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si vous sauvez ça dans &amp;ldquo;./tests/__init__.py&amp;rdquo; et que vous exécutez:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nosetests -sv tests 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous vous retrouvez dans une console pdb, tapez simplement:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dir(sys.modules[__name__])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et là, miracle, vous voyez bien que votre module a une classe nommée
&amp;ldquo;Classe1&amp;rdquo;. Bha on continue !&lt;/p&gt;
&lt;h1 id=&#34;ok-mais-pour-les-expected-là&#34;&gt;OK, mais pour les expected là&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Bon c&amp;rsquo;est cool mais on va passer aux choses sérieuses. Je veux générer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une classe par module expected&lt;/li&gt;
&lt;li&gt;une méthode pour chaque url&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avoir la liste des modules a été le plus tordu&amp;hellip; c&amp;rsquo;est pkgutil. Et pour
m&amp;rsquo;assurer d&amp;rsquo;avoir bien le chemin du module expected, j&amp;rsquo;ai usé de ruse:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# on prend le chemin exact
pkgpath = os.path.dirname(expected.__file__) 

# et on a la liste des modules
for mod in pkgutil.iter_modules([pkgpath]):
    # on prend juste le nom:
    modname = mod[1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc là, en visitant
&lt;a href=&#34;https://docs.python.org/2/library/pkgutil.html#pkgutil.iter_modules&#34;&gt;https://docs.python.org/2/library/pkgutil.html#pkgutil.iter_modules&lt;/a&gt;
vous verrez que le nom du module est le second élément du tuple retourné
par &amp;ldquo;iter_modules&amp;rdquo;, c&amp;rsquo;est pourquoi j&amp;rsquo;ai récupéré le nom dans &amp;ldquo;mod[1]&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Va pour générer le nom de classe&amp;hellip;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# on prend le chemin exact
pkgpath = os.path.dirname(expected.__file__) 

# et on a la liste des modules
for mod in pkgutil.iter_modules([pkgpath]):
    # on prend juste le nom:
    modname = mod[1]
    cl = type(&amp;quot;Test%s&amp;quot; % modname, (BaseTest,), {}

    # par exemple, on aura TestSiteA pour le module expected.SiteA
    # etc...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok&amp;hellip; on va générer les méthodes, on s&amp;rsquo;y prend un peu comme pour la
classe. Seule problème, la méthode doit faire des trucs hein&amp;hellip; et
surtout utiliser les méthodes d&amp;rsquo;assertion. Et là, vous remerciez le ciel
que Python demande de passer explicitement la référence de la classe à
ces méthodes. Car de cette manière, on peut déclarer une fonction &lt;em&gt;hors
classe&lt;/em&gt; qui prend en argument un paramètre &amp;ldquo;self&amp;rdquo;, puis l&amp;rsquo;attacher à une
classe. Du coup, &amp;ldquo;self&amp;rdquo; sera bel et bien défini à la déclaration, et
utilisée par la classe. Haaaa douce mélodie qu&amp;rsquo;est le code Python.&lt;/p&gt;
&lt;p&gt;On se lance.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# un générateur de méthode, c&#39;est cool ça
def _method(data):
    # on défini la méthode qui gère &amp;quot;data&amp;quot;
    def the_method(self):
        # oui, python est magique, on a un self là
        # donc là, vous pouvez créer votre test générique, nous
        # on utilise notre parser
        parsed = self.parse(data[&#39;url&#39;])
        self.assertEquals(parsed, data[&#39;expected&#39;])
    return the_method
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc, si j&amp;rsquo;appelle:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;method = _method(data)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;la variable &amp;ldquo;method&amp;rdquo; sera une instance &amp;ldquo;the_method&amp;rdquo; qui peut utiliser
les &amp;ldquo;data&amp;rdquo;. Et quant à &amp;ldquo;self&amp;rdquo;, il sera refourgué par l&amp;rsquo;appel à la
méthode au travers de la classe. Voilà une raison toute bête de
l&amp;rsquo;utilité de déclarer explicitement le mot clef &amp;ldquo;self&amp;rdquo; dans les méthodes
de classe en Python, c&amp;rsquo;est simplement parce que vous pouvez générer des
méthodes en dehors des classes, et par conséquent, la variable &amp;ldquo;self&amp;rdquo;
doit exister dans le corps de la fonction. Donc, on la file en argument.
C&amp;rsquo;est pas compliqué boudiou !!!&lt;/p&gt;
&lt;p&gt;Et donc, là, on va l&amp;rsquo;utiliser. Après avoir généré la classe, on peut
générer la méthode et l&amp;rsquo;accrocher à la classe (cherchez &amp;ldquo;==&amp;gt; LA&amp;rdquo; dans
le code pour trouver où on génère la méthode):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# et on a la liste des modules
for mod in pkgutil.iter_modules([pkgpath]):
    # on prend juste le nom:
    modname = mod[1]

    # BaseTest c&#39;est notre classe de base qui hérite de unittest.TestCase
    # Et donc là, on aura par exemple TestSiteA, classe qui hérite de notre
    # classe de base
    cl = type(&amp;quot;Test%s&amp;quot; % modname, (BaseTest,), {}

    # on charge le module, on demande de charger &amp;quot;data&amp;quot; avec...
    module = __import__(modname, globals(), locals(), [&amp;quot;data&amp;quot;])

    # on itère sur les modules
    for key, val in module.data.items():

        # on génère le nom sans espace
        # par exemple &amp;quot;un titre de test&amp;quot; devient
        # un_titre_de_test (et je me fous des accents pour le moment)
        key = key.replace(&amp;quot; &amp;quot;,&amp;quot;_&amp;quot;) 

        # on récupère une méthode qui va utiliser
        # nos datas
        # ==&amp;gt; LA
        method = _method(val)

        # on génère le nom de la méthode
        # ce qui donnera &amp;quot;test_un_titre_de_test&amp;quot;
        method.__name__ = &amp;quot;test_%s&amp;quot; % key

        # hop, on assigne ça à la classe
        setattr(cl, method.__name__, method)

    #et voilà, on foure notre classe au module
    setattr(sys.module[__name__], cl.__name__, cl)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si vous avez tout suivi:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on load expected&lt;/li&gt;
&lt;li&gt;pour chaque module trouvé dans expected on récupère le nom et on
génère une classe&lt;/li&gt;
&lt;li&gt;on charge les données de test&lt;/li&gt;
&lt;li&gt;pour chaque données de test, on génère une méthode qui utilise les
assertions de unittest&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc, le code à peu près valide (oui là je le teste pas et comptez pas
sur moi pour vous refourguer le code de mon client hein :p ):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import unittest
import sys
import pkgutil
import expected

class BaseTest(unittest.TestCase):
    &amp;quot;&amp;quot;&amp;quot; Classe de base, elle peut faire un setUp, un tearDown
        et avoir des méthodes spécifique, comme &amp;quot;parse&amp;quot; dans notre
        cas... 
    &amp;quot;&amp;quot;&amp;quot; 

    def parse(seld, data):
        # méthode à nous pour parser une url
        return {}

def init():
    &amp;quot;&amp;quot;&amp;quot; Initialise les classes de test &amp;quot;&amp;quot;&amp;quot;

    # notre générateur de méthode
    def _method(data):

        def the_method(self):
            # le test en lui même
            # libre à vous de faire vos propres test ICI
            parsed = self.parse(data[&#39;url&#39;])

            #assertion qui viendra de unittest.TestCase
            self.assertEquals(parsed, data[&#39;expected&#39;])

        # retourne la fonction de test
        return the_method

    # on prend le chemin exact
    pkgpath = os.path.dirname(expected.__file__) 

    # et on a la liste des modules
    for mod in pkgutil.iter_modules([pkgpath]):

        # on prend juste le nom:
        modname = mod[1]

        # génération de la classe avec le nom du module
        cl = type(&amp;quot;Test%s&amp;quot; % modname, (BaseTest,), {}

        # on charge le module, on demande de charger &amp;quot;data&amp;quot; avec...
        module = __import__(modname, globals(), locals(), [&amp;quot;data&amp;quot;])

        # pour chaque module expected
        for key, val in module.data.items():

            # on génère le nom sans espace
            key = key.replace(&amp;quot; &amp;quot;,&amp;quot;_&amp;quot;) 

            # génération de la méthode test_nom_du_test
            method = _method(val)
            method.__name__ = &amp;quot;test_%s&amp;quot; % key

            # assignation dans la classe
            setattr(cl, method.__name__, method)

        # déclaration de la méthode dans le module
        setattr(sys.module[__name__], cl.__name__, cl)

# initialisation
init()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc, là&amp;hellip; on peut faire:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nosetests -sv tests
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;qui va lancer tous les tests générés.&lt;/p&gt;
&lt;p&gt;Ou:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nosetests -sv tests:TestSiteA.test_un_titre_de_test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;qui ne lancera que le test &amp;ldquo;un titre de test&amp;rdquo; choppé depuis le module
expected.SiteA.py&lt;/p&gt;
&lt;p&gt;Ou encore:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nosetests -sv tests:TestSiteB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;qui lancera tous les tests de TestSiteB.&lt;/p&gt;
&lt;h1 id=&#34;heu-ok-mais-du-coup-&#34;&gt;Heu&amp;hellip; ok&amp;hellip; mais du coup ?&lt;/h1&gt;
&lt;p&gt;Vous n&amp;rsquo;avez pas compris l&amp;rsquo;intérêt ? je vais vous faire un résumé.&lt;/p&gt;
&lt;p&gt;Avant on devait:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Créer un module qui contenait des données attendue, non placées dans
le test pour des raison de place, de lisibilité&amp;hellip;&lt;/li&gt;
&lt;li&gt;Créer une classe de test qui:&lt;/li&gt;
&lt;li&gt;charge le module expected.ModuleATester&lt;/li&gt;
&lt;li&gt;charge le parser&lt;/li&gt;
&lt;li&gt;fait les asserts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Alors que maintenant, on ne fait que la première partie&amp;hellip; on créer un
module dans &amp;ldquo;expected&amp;rdquo;, formatté corretement, et c&amp;rsquo;est tout. Notre
générateur va trouver le nouveau module, les données avec le titre, et
générer un test à la volée. On peut invoquer un test par son nom
logique, on peut même demander à nose de n&amp;rsquo;exécuter qu&amp;rsquo;une seule
méthode. Car la méthode existera réellement, Python génère les classes
comme si nous les avions réellement écrites.&lt;/p&gt;
&lt;p&gt;Si ça vous parait étrange, trop &amp;ldquo;brutal&amp;rdquo;, demandez vous comment vous
auriez fait pour réussir le même tour de force avec les même
contraintes, à savoir pouvoir utiliser nose &amp;ldquo;comme si les classes
existaient vraiement&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Depuis, on ne tape pas un seul test, on ne génère que le module de
données à tester.&lt;/p&gt;
&lt;p&gt;On a amélioré le système en permattant au développeur de spécifier des
assertions particulères dans les modules de &amp;ldquo;expected&amp;rdquo;, et même la
gestion des &amp;ldquo;skip&amp;rdquo;&amp;hellip; le temps de création et de maintenance de ces
tests est tout simplement réduit par 10.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Python en mode one liner</title>
      <link>https://www.metal3d.org/blog/2014/python-en-mode-one-liner/</link>
      <pubDate>Fri, 30 May 2014 14:11:36 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2014/python-en-mode-one-liner/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Python, c&amp;rsquo;est cool, c&amp;rsquo;est fun, c&amp;rsquo;est lisible et en plus c&amp;rsquo;est super bien
intégré aux Unix. Alors quand on peut en plus s&amp;rsquo;en servir comme &amp;ldquo;one
liner&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Bon tout le monde connait sed, awk, perl etc&amp;hellip; ils permettent d&amp;rsquo;être
utilisés en &amp;ldquo;one liner&amp;rdquo; c&amp;rsquo;est-à-dire à traiter en une ligne des données
injectées (généralement depuis un fichier ou une redirection dans
STDIN). Sauf que Python n&amp;rsquo;est pas en reste&amp;hellip; ou plutôt il l&amp;rsquo;est mais on
peut rapidement se créer son petit outil qui avoir le même effet que
sed, awk et consorts.&lt;/p&gt;
&lt;p&gt;Non parce que bon, sérieusement, au taff on me sort souvent &amp;ldquo;pfff mais
c&amp;rsquo;est quoi ce truc&amp;rdquo; en me montrant du doigt des instructions awk que
j&amp;rsquo;ai tapé&amp;hellip; ou encore &amp;ldquo;hey mais comment tu fais pour récupérer tel
élément avec sed là ?&amp;rdquo;. Car voilà, au taff, on est surtout des
Pythonistes&amp;hellip; alors quand ils voient des variables avec un dolar devant
ils sont perdus (si un collègue lis mon article: te vexe pas, je taquine
:) )&lt;/p&gt;
&lt;p&gt;Il existe un projet nommé &lt;a href=&#34;https://github.com/ksamuel/Pyped&#34;&gt;Pyped&lt;/a&gt; que
je trouve vraiment super, mais bon il demande pas mal de deps et pis bon
hein&amp;hellip; c&amp;rsquo;est du Python 2.7. Non pas que j&amp;rsquo;aime pas Python 2.7, mais
faut commencer à utiliser Python 3 un jour&amp;hellip;&lt;/p&gt;
&lt;p&gt;Du coup je vous montre celui que j&amp;rsquo;ai codé, et dans la foulée ça fera un
petit tuto. On commence, allons-y gaiement !&lt;/p&gt;
&lt;h1 id=&#34;lire-stdin-tout-un-programme&#34;&gt;Lire STDIN, tout un programme&lt;/h1&gt;
&lt;p&gt;L&amp;rsquo;idée est de pouvoir injecter des lignes dans le STDIN du script, et ce
depuis pas mal de sources possible (fichier, commande shell, &amp;hellip;).&lt;/p&gt;
&lt;p&gt;On va utiliser &amp;ldquo;sys.stdin&amp;rdquo; (quelle suprise !). Pour lire une ligne, on
utilise &amp;ldquo;sys.stdin.readline()&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Jusque là c&amp;rsquo;est facile. Mais j&amp;rsquo;aime les piments, alors on pimente un
peu: je sais qu&amp;rsquo;un jour ou l&amp;rsquo;autre je vais me prendre plusieurs
centaines de mega, voir des gigas de donnée dans STDIN&amp;hellip; alors je vais
&amp;ldquo;itérer&amp;rdquo; sur les lignes de données. Et devinez quoi, y&amp;rsquo;a un built-in
Python génialissime pour ça: iter().&lt;/p&gt;
&lt;p&gt;&amp;ldquo;iter&amp;rdquo; est un petit cachotier, car on le connait pour sa forme
&lt;em&gt;iter(list)&lt;/em&gt;, mais bien moins pour sa forme &lt;em&gt;&amp;ldquo;iter(callable,
sentinel)&amp;rdquo;&lt;/em&gt;. C&amp;rsquo;est très simple il va retourner ce que le callable génère
jusqu&amp;rsquo;à ce que la sentinel soit trouvée. En l&amp;rsquo;occurence, nous, on veut
lire sys.stdin.readline jusqu&amp;rsquo;à ce qu&amp;rsquo;elle nous retourne plus rien&amp;hellip;
hahaha on tente ?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;#!/usr/bin/python3 -u
import sys

for x in iter(sys.stdin.readline, &#39;&#39;):
    x = x.rstrip(&#39;\n&#39;)
    print(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remarquez bien que j&amp;rsquo;utilise &amp;ldquo;python 3&amp;rdquo; et l&amp;rsquo;option &amp;ldquo;-u&amp;rdquo; pour utiliser
STDIN et STDOUT en mode &amp;ldquo;non bufferisé&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Bon, vous rendez ce p&amp;rsquo;tit script exécutable et vous testez:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ chmod +x pype.py
$ dmesg | tail | pype.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool hein, on vient de faire un script qui affiche la sortie standard.
Voilà c&amp;rsquo;est tout pour aujourd&amp;rsquo;hui&amp;hellip; non je déconne, on va aller plus
loin :)&lt;/p&gt;
&lt;p&gt;Bon, on sait lire de manière propre une entrée standard. Reste
maintenant à faire en sorte de traiter ces lignes.&lt;/p&gt;
&lt;p&gt;Dans Python (version 3 pour l&amp;rsquo;heure) il existe la commande &amp;ldquo;exec&amp;rdquo;. Elle
permet d&amp;rsquo;une part d&amp;rsquo;éxecuter du code python, mais prend en second et
troisième paramètres des dictionnaires qui représentent respectivement
les variables globales et locales. On appelle souvent ces attributs des
&amp;ldquo;contextes&amp;rdquo; (ceux qui utilisent Jinja connaissent le principe). Chaque
clef du dict contexte est le nom d&amp;rsquo;une variable accessible dans les
instructions passées à &amp;ldquo;exec&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Donc, on va permettre de prendre en argument un truc à exécuter. Vous
avez compris&amp;hellip; sys.argv va être notre allié.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;#!/usr/bin/python3 -u
    # -*- encoding: utf-8 -*-

import sys

ctx = globales()
cmd = sys.argv[1]

for x in iter(sys.stdin.readline, &#39;&#39;):
            # on va donner une variable &amp;quot;x&amp;quot; qui est
            # la ligne STDIN récupérée
    ctx[&amp;quot;x&amp;quot;] = x.rstrip(&#39;\n&#39;)
    exec(cmd, ctx)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On va donc s&amp;rsquo;amuser, par exemple lister le répertoire &amp;ldquo;/etc&amp;rdquo; et
n&amp;rsquo;afficher que les fichiers qui ont &amp;ldquo;.conf&amp;rdquo; dans leur nom:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -1 /etc | pype.py &#39;if x.find(&amp;quot;.conf&amp;quot;): print(x)&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pas mal hein :) on va maintenant aller un peu plus loin. J&amp;rsquo;adore
&amp;ldquo;enumerate&amp;rdquo; qui retourne l&amp;rsquo;index et l&amp;rsquo;entrée d&amp;rsquo;une liste. Allez, on y
va.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;#!/usr/bin/python3 -u
    # -*- encoding: utf-8 -*-

import sys

ctx = globales()
cmd = sys.argv[1]

for i, x in enumerate(iter(sys.stdin.readline, &#39;&#39;)):
            # on va donner une variable &amp;quot;x&amp;quot; qui est
            # la ligne STDIN récupérée
            # et &amp;quot;i&amp;quot; est le numéro de ligne
    ctx[&amp;quot;x&amp;quot;] = x.rstrip(&#39;\n&#39;)
    ctx[&amp;quot;i&amp;quot;] = i
    exec(cmd, ctx)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On peut tester:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dmesg | tail | pype.py &amp;quot;print(&#39;%d -- %s&#39; % (i, x))&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si tout se passe bien, vous devez avoir le numéro de la ligne, puis deux
tirets et le message récupéré dans STDIN, sinon c&amp;rsquo;est que j&amp;rsquo;ai foiré un
truc dans mes exemples&amp;hellip;&lt;/p&gt;
&lt;p&gt;Bon reste un souci&amp;hellip;&lt;/p&gt;
&lt;p&gt;Si j&amp;rsquo;ai besoin d&amp;rsquo;un import &amp;ldquo;lourd&amp;rdquo;, et que je le met dans la ligne
d&amp;rsquo;exécution, chaque ligne rencontrée va lancer l&amp;rsquo;import du module. C&amp;rsquo;est
rageant. Le mieux, tout comme le propose awk, c&amp;rsquo;est d&amp;rsquo;avoir un block
&amp;ldquo;begin&amp;rdquo; qui n&amp;rsquo;est exécuté qu&amp;rsquo;au début, et pourquoi pas un un block
&amp;ldquo;end&amp;rdquo;. Et comme Python est founis en outils pour gérer facilement des
trucs, on va utiliser &amp;ldquo;optparse&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Le résultat est cool:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;#!/usr/bin/python3
# -*- encoding: utf8 -*-

import optparse
import sys

# prepare options
parser = optparse.OptionParser()
parser.add_option(&#39;-e&#39;, &#39;--end&#39;, 
            action=&amp;quot;store&amp;quot;, 
            dest=&amp;quot;end&amp;quot;, 
            help=&amp;quot;end code&amp;quot;, 
            default=None)
parser.add_option(&#39;-b&#39;, &#39;--begin&#39;, 
            action=&amp;quot;store&amp;quot;, 
            dest=&amp;quot;begin&amp;quot;, 
            help=&amp;quot;begin code&amp;quot;, 
            default=None)

options, expr = parser.parse_args()

# now, save ctx (and get imports fro BEGIN if any)
ctx = globals()

#execute BEGIN block
if options.begin is not None:
    exec(options.begin, ctx)


#joining all commands
expr = &amp;quot;\n&amp;quot;.join(expr)

# for each stdin line, stop if empty
for i, x in enumerate(iter(sys.stdin.readline, &#39;&#39;)):
    ctx[&#39;x&#39;] = x.rstrip(&#39;\n&#39;)
    ctx[&#39;i&#39;] = i
    exec (expr, ctx)

# execute END code
if options.end is not None:
    exec (options.end, ctx)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc, là, pour tout vous expliquer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On propose deux options, -b (&amp;ndash;begin) et -e (&amp;ndash;end) qui permettent
de donner des instructions à exécuter au début et à la fin du
travail (import, ouverture de fichier, etc&amp;hellip;) les variables seront
accessibles dans les block d&amp;rsquo;instructions&lt;/li&gt;
&lt;li&gt;on récupère touts les autres paramètre qui sont les instructions à
traiter. C&amp;rsquo;est une liste, donc je les join avec &amp;ldquo;n&amp;rdquo;.join(&amp;hellip;)&lt;/li&gt;
&lt;li&gt;on intègre dans le contexte deux variables: x =&amp;gt; la ligne capturée
dans STDIN et &amp;ldquo;i&amp;rdquo; qui est le numéro de la ligne à traiter (sympa
pour compter)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Comprennez bien&lt;/strong&gt;, j&amp;rsquo;utilise le contexte &amp;ldquo;ctx&amp;rdquo; dans les 3 &amp;ldquo;exec&amp;rdquo;, du
coup les variables et import créés dans le block begin sont accessible
dans les blocks d&amp;rsquo;instructions et le block &amp;ldquo;end&amp;rdquo;; et par conséquent une
variable créée dans le block principal sera accessible dans le block
&amp;ldquo;end&amp;rdquo;. Le context est clairement l&amp;rsquo;environnement de globales qui se
promène partout.&lt;/p&gt;
&lt;p&gt;Du coup, là, on peut faire:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -1 /etc | ./main.py \
-b &amp;quot;import re; counter = 0&amp;quot; \
-e &amp;quot;print(&#39;Founds: %d&#39; % counter)&amp;quot; \
&amp;quot;if re.findall(&#39;.*.conf&#39;, x):
     print(&#39;found %s at %d&#39; % (x, i))
     counter += 1
&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(les backslash sont là parce que dans mon blog ça déborde, ça permet de
revenir à la ligne sans exécuter l&amp;rsquo;instruction&amp;hellip; si vous le souhaitez,
vous pouvez ne pas les mettre et tout faire en une ligne, d&amp;rsquo;où le nom
&amp;ldquo;one liner&amp;rdquo; hein)&lt;/p&gt;
&lt;p&gt;On charge &amp;ldquo;re&amp;rdquo; au début et on initialise un compteur &amp;ldquo;counter&amp;rdquo;, puis
pour chaque ligne on utilise le module.&lt;/p&gt;
&lt;p&gt;A la fin&amp;hellip; on acffiche le compteur.&lt;/p&gt;
&lt;p&gt;Bref, c&amp;rsquo;est pas idiot comme script, et ça peut être (pour les
Pythonistes) plus agréable que awk ou sed.&lt;/p&gt;
&lt;h1 id=&#34;et-un-zeste-de-cython&#34;&gt;Et un zeste de Cython&lt;/h1&gt;
&lt;p&gt;Allez, on va gagner le temps d&amp;rsquo;interprétation et (dans certains cas)
accélerer un chouillat le tratement de notre script. Bon ok, c&amp;rsquo;est pas
super avantageux mais juste pour l&amp;rsquo;amusement&amp;hellip; on va compiler notre
script pour en faire un binaire exécutable natif:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cython --embed pype.py
$ gcc pype.c -o pype -O9 $(pkg-config --libs --cflags python3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous avez maintenant un binaire &amp;ldquo;pype&amp;rdquo; que vous pouvez copier dans un
répertoire de votre $PATH, par exemple dans &amp;ldquo;~./local/bin&amp;rdquo;. Le binaire
doit faire dans les 40ko.&lt;/p&gt;
&lt;p&gt;Alors oui, on peut améliorer le truc, on peut utiliser le projet Pyped
(su-cité), mais bon sang&amp;hellip; que c&amp;rsquo;est cool de le faire soi-même !&lt;/p&gt;
&lt;h1 id=&#34;un-mot-pour-la-fin&#34;&gt;Un mot pour la fin&lt;/h1&gt;
&lt;p&gt;De mon coté, mon script est à peine différent que celui que je vous
propse; en fait j&amp;rsquo;ai juste ajouté des &amp;ldquo;import&amp;rdquo; pour que le contexte ait
accès à des modules que je trouve indispensable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;import re -&amp;gt; pour les expressions régulières, je m&amp;rsquo;en sers tout le
temps&lt;/li&gt;
&lt;li&gt;import json -&amp;gt; on ne le présente plus&lt;/li&gt;
&lt;li&gt;import os -&amp;gt; parfois utile pour la résolution de path&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tout ce que vous avez à faire, c&amp;rsquo;est de les mettre dans pype.py au début
du script. Comme nous initialisons le contexte avec &amp;ldquo;globales()&amp;rdquo;, ces
modules sont accessible dans les block d&amp;rsquo;instructions. Vous pouvez le
recompiler avec cython, et le tour est joué.&lt;/p&gt;
&lt;p&gt;A vous de vous amuser, de mettre les modules qui vous intéressent,
d&amp;rsquo;adapter mon script (qui est libre), de l&amp;rsquo;améliorer. Il est clair que
je le fais ici en Python, mais les PHPistes pourront le faire en PHP,
les Javaistes ne pourront pas (oui bha une boutade de temps en temps
hein&amp;hellip;), et je suis sûr que c&amp;rsquo;est faisable en NodeJS.&lt;/p&gt;
&lt;p&gt;Allez à vous !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Règles de base iptables</title>
      <link>https://www.metal3d.org/blog/2013/r%C3%A8gles-de-base-iptables/</link>
      <pubDate>Fri, 01 Nov 2013 14:07:10 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/r%C3%A8gles-de-base-iptables/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;J&amp;rsquo;ai dut changé de serveur il y a 2 jours et face à une nouvelle
installation, je me suis retrouvé confronté à une configuration de base
iptables ésotérique. Après plusieurs tentatives de modifications
&amp;ldquo;nouvelle mode&amp;rdquo;, j&amp;rsquo;en suis revenu à ma méthode favorite, l&amp;rsquo;ancienne,
celle qui marche. Ainsi, si vous voulez régler un firewall &amp;ldquo;basique&amp;rdquo;
(assez fiable), vous pouvez suivre ma méthode. Elle n&amp;rsquo;est pas nouvelle
ni révolutionnaire, mais elle est maîtrisable.&lt;/p&gt;
&lt;p&gt;Tout d&amp;rsquo;abord, la fameuse commande &amp;ldquo;iptables -L&amp;rdquo; me donnait des règles
difficilement compréhensibles. Etant donné que je voulais au moins
arriver à ouvrir quelques ports, et sans entrer dans une configuration
compliquée, j&amp;rsquo;ai décidé de faire un &amp;ldquo;flush&amp;rdquo;. Erreur !!! trop pressé,
j&amp;rsquo;ai simplement laissé les règles par défaut en &amp;ldquo;DROP&amp;rdquo;, je n&amp;rsquo;avais plus
qu&amp;rsquo;à relancer le serveur pour m&amp;rsquo;en sortir.&lt;/p&gt;
&lt;p&gt;Bref, pour éviter tout problème, voici les commandes de base à
effectuer:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -P INPUT ACCEPT
iptables -F
iptables -X
iptables -t mangle -F
iptables -t nat -X
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui fait que toutes les règles de bases sont supprimées. Mais ne perd
pas la connexion en cours car nous avons demandé d&amp;rsquo;accepter toutes les
connexions par défaut (le temps de configurer le par-feu).&lt;/p&gt;
&lt;p&gt;Mainentant, on va configurer un peu notre par-feu pour n&amp;rsquo;ouvrir que ce
dont nous avons vraiment besoin, à savoir:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ssh (port 22)&lt;/li&gt;
&lt;li&gt;http, https et http-alt (ports 80, 443 et 8080)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il faut aussi:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;accepter les connexions sur le rebouclage locale (connexions
internes)&lt;/li&gt;
&lt;li&gt;accepter les connexions déjà en cours vers la sortie, si une
connexion entrante à été acceptée, il faut pouvoir lui répondre&lt;/li&gt;
&lt;li&gt;accepter tout en sortie (pour mon cas, vous pouvez ne pas le
vouloir)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On va donc commencer par le plus simple,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -i lo -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donc, sur l&amp;rsquo;interface &amp;ldquo;lo&amp;rdquo;, on accept tout. On continue sur notre
lancée, on accepte les connexions établies&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et enfin, les connexions venant de l&amp;rsquo;extérieure sont acceptées seulement
sur les ports que nous désirons. L&amp;rsquo;interface &amp;ldquo;eth0&amp;rdquo; est, sur mon
serveur, la carte qui est utilisé pour l&amp;rsquo;accès depuis l&amp;rsquo;extérieure,
vérifiez avec &amp;ldquo;ifconfig&amp;rdquo; le nom de votre interface réseau.&lt;/p&gt;
&lt;p&gt;Donc pour mes ports à ouvrir (tcp uniquement):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 8080 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour des raisons pratique, je veux que mon serveur accpete aussi de
répondre au &amp;ldquo;ping&amp;rdquo;, vous pouvez utiliser cette règle:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et enfin, je &amp;ldquo;DROP&amp;rdquo; (laisse tomber, c&amp;rsquo;est-à-dire refuser&amp;hellip;) le reste
par défaut:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -P INPUT DROP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Un coup de &amp;ldquo;iptables -L&amp;rdquo; vous donne la liste effective des règles de
votre firewall:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Chain INPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http-alt
ACCEPT     icmp --  anywhere             anywhere             icmp echo-request

Chain FORWARD (policy DROP)
target     prot opt source               destination         


Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui compte ici, c&amp;rsquo;est de voir que la politique par défaut (policy)
est à DROP pour les entrées (INPUT) et traduction ip (FORWARD, utile
dans mon cas car j&amp;rsquo;installe libvirt pour faire tourner des LXC, à savoir
que libvirt installe des règles iptables au lancement, donc je ne m&amp;rsquo;en
occupe pas pour le moment).&lt;/p&gt;
&lt;p&gt;Pour des raisons pratique, je veux que mon serveur accpete aussi de
répondre au &amp;ldquo;ping&amp;rdquo;, vous pouvez utiliser cette règle:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&amp;rsquo;autre commande utile est &amp;ldquo;iptables-save&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;*filter
:INPUT DROP [7:614]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [414:373999]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 8080 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
COMMIT
# Completed on Fri Nov  1 01:02:47 2013
# Generated by iptables-save v1.4.14 on Fri Nov  1 01:02:47 2013
*mangle
:PREROUTING ACCEPT [720:485103]
:INPUT ACCEPT [716:484539]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [496:383895]
:POSTROUTING ACCEPT [496:383895]
COMMIT
# Completed on Fri Nov  1 01:02:47 2013
# Generated by iptables-save v1.4.14 on Fri Nov  1 01:02:47 2013
*nat
:PREROUTING ACCEPT [17:1894]
:INPUT ACCEPT [7:499]
:OUTPUT ACCEPT [15:900]
:POSTROUTING ACCEPT [15:900]
COMMIT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant, plusieurs solutions&amp;hellip; Vous être sur Debian, vous devez
installer le paquet &amp;ldquo;iptables-persistent&amp;rdquo; puis taper cette commande:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables-save &amp;gt; /etc/iptables/rules.v4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lors du redémarrage serveur, &amp;ldquo;iptables-restore &amp;lt;
/etc/iptables/rules.v4&amp;rdquo; sera effectuée.&lt;/p&gt;
&lt;p&gt;Sinon, sur CentOS et RedHat, utilisez cette commande:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;service iptables save
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela a pour effet de sauver votre configuration dans
&amp;ldquo;/etc/sysconfig/iptables&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Quand tout est fini, essayez de vérifier si tout est ok vue de
l&amp;rsquo;extérieur. La commande nmap peut vous aider:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nmap -v ip_de_votre_server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Evidemment, utilisez cette commande depuis un poste externe au réseau du
serveur. Il faut que vous ne voyez que les ports que vous avez autorisé
et pas un de plus.&lt;/p&gt;
&lt;p&gt;Pour ma part:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nmap -v XXXXXX.org

Starting Nmap 6.40 ( http://nmap.org ) at 2013-11-01 15:02 CET
Initiating Ping Scan at 15:02
Scanning XXXX.org (XXXXX) [4 ports]
Completed Ping Scan at 15:02, 0.04s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 15:02
Completed Parallel DNS resolution of 1 host. at 15:02, 0.00s elapsed
Initiating SYN Stealth Scan at 15:02
Scanning XXXXXX.org (XXXXXX) [1000 ports]
Discovered open port 22/tcp on XXXXXX
Discovered open port 80/tcp on XXXXXX
Discovered open port 443/tcp on XXXXXX
Discovered open port 8080/tcp on XXXXXX
Completed SYN Stealth Scan at 15:02, 4.39s elapsed (1000 total ports)
Nmap scan report for XXXXXX.org (XXXXXXXXX)
Host is up (0.029s latency).
rDNS record for XXXXX: XXXXXX
Not shown: 996 filtered ports
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
443/tcp  open  https
8080/tcp open  http-proxy

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 4.49 seconds
           Raw packets sent: 2002 (88.064KB) | Rcvd: 7 (292B)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pourrez ensuite gérer d&amp;rsquo;autres aspects de sécurité, mais pour
l&amp;rsquo;heure cette première version vous évitera d&amp;rsquo;être mangé par un méchant
pirate amateur. J&amp;rsquo;insiste sur le fait que cet article ne vous permet pas
une sécurité absolue, mais c&amp;rsquo;est un début pour ceux qui veulent
s&amp;rsquo;atteler à iptables.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Les designs patterns une preuve de faiblesse du langage</title>
      <link>https://www.metal3d.org/blog/2013/les-designs-patterns-une-preuve-de-faiblesse-du-langage/</link>
      <pubDate>Thu, 10 Oct 2013 11:26:08 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/les-designs-patterns-une-preuve-de-faiblesse-du-langage/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Le titre peut faire frémir et vous pensez que je vais lancer un troll
bien dégoulinant sur cette page - non ce n&amp;rsquo;est pas le but. Vous allez
voir que je n&amp;rsquo;ai rien contre les designs patterns mais qu&amp;rsquo;une réflexion
sur leur existence peut vous amener à y réfléchir quand vous choisirez
un langage pour implémenter vos projets. Si ce post vous offusque, ne le
lisez pas&amp;hellip;&lt;/p&gt;
&lt;p&gt;Hier, je discute avec mes collègues ingénieurs en développement à propos
des patterns existants nativement dans certains langages, et je ne sais
pas pourquoi, je lance la phrase &amp;ldquo;Rob Pike disait dans une conférence
que les design patterns sont la démonstration de la faiblesse d&amp;rsquo;un
langage&amp;rdquo;. Je ne me souvenais plus exactement quelle conférence, mais je
l&amp;rsquo;ai retrouvé, c&amp;rsquo;est &amp;ldquo;Public Static Void&amp;rdquo; à voir ici
&lt;a href=&#34;http://www.youtube.com/watch?v=5kj5ApnhPAE&#34;&gt;http://www.youtube.com/watch?v=5kj5ApnhPAE&lt;/a&gt; et là
&lt;a href=&#34;http://fr.slideshare.net/120bi/public-staticvoidrobpikeosconjuly2210&#34;&gt;http://fr.slideshare.net/120bi/public-staticvoidrobpikeosconjuly2210&lt;/a&gt; .
En réalité, je me rend compte que cette phrase est d&amp;rsquo;un autre personnage
cité dans la conférence, c&amp;rsquo;est Norvig qui raconté ceci.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Patterns are demonstration of weakness in a language P.Norvig&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Il le démontre en réalité en parlant du pattern Iterator dans son
article &lt;a href=&#34;http://home.pipeline.com/~hbaker1/Iterator.html&#34;&gt;http://home.pipeline.com/~hbaker1/Iterator.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Personnellement je me suis fais cette réflexion il y a bien longtemps.
Lorsque je faisais parti de l&amp;rsquo;équipe de développement de Copix
(&lt;a href=&#34;http://www.copix.org&#34;&gt;http://www.copix.org&lt;/a&gt;) Gérald Croës donnait des conférences sur le
sujet. Et je me disais en l&amp;rsquo;écoutant &amp;ldquo;ouais en gros les designs patterns
ça sert à régler des problèmes récurrents quand on a pas la solution
nativement&amp;rdquo;&amp;hellip; J&amp;rsquo;étais pas loin de ce que raconte R.Pike et P.Norvig.&lt;/p&gt;
&lt;p&gt;Prenons un exemple de pattern, le Thread Pool. L&amp;rsquo;idée est de définir une
pile limitée en taille qui va accepter des threads à lancer. Quand un
thread est terminé, il laisse la place à un autre, pendant que les
autres threads déjà dans la pile continuent de tourner.&lt;/p&gt;
&lt;p&gt;Vous vous dites que c&amp;rsquo;est simple, c&amp;rsquo;est vrai. Aujourd&amp;rsquo;hui Python, Java,
Ruby, et autres langages haut niveau permettent de faire un &amp;ldquo;Pool&amp;rdquo; et
des &amp;ldquo;Threads&amp;rdquo;. Mais le Thread Pool doit être implémenté, et ce pour une
raison simple: ils n&amp;rsquo;existent pas nativement dans le langage.&lt;/p&gt;
&lt;p&gt;Certains langages par contre ont des types permettant de gérer des
tâches concurrentes (ou parallèles, cela dépend du langage et de
l&amp;rsquo;implémentation) nativement sans avoir besoin d&amp;rsquo;une classe ou d&amp;rsquo;un type
spécifique, et de même pour les &amp;ldquo;pool&amp;rdquo; qui sont gérés eux aussi par des
types prévus à cet effet. Je pense à Go, Erlang&amp;hellip;&lt;/p&gt;
&lt;p&gt;Je ne suis pas en train de dire que Go et/ou Erlang sont plus puissant
que Java ou Python, mais que leur force réside dans le fait que vous
pouvez vous abstraire de beaucoup de patterns parce que le langage est
prévu pour cela.&lt;/p&gt;
&lt;p&gt;Je vais un peu plus loin dans mon idée. Les design patterns existent
depuis des lustres, en fait on en entend parler depuis les années 80 en
tant que tel mais des &amp;ldquo;patterns&amp;rdquo; existent depuis les années 50 à
l&amp;rsquo;époque où l&amp;rsquo;on développait en assembleur. Le simple fait de pousser
des valeurs dans un espace mémoire pour laisser la pile propre au
&amp;ldquo;sous-routines&amp;rdquo; était une idée ingénieuse qui règle un souci récurrent
dans les langages &amp;ldquo;Assembleur&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Quand est né le C, ce problème de mémoire à gérer a disparut pour ce
cas. Le langage a supprimé l&amp;rsquo;utilisation d&amp;rsquo;un pattern en le rendant
transparent. On peut en déduire que la méthode de déplacement des
registres en mémoire pour l&amp;rsquo;utilisation de sous-routines (en tant que
pattern) était une faiblesse des langages Assembleur. Et là, je ne
crache pas sur le langage, je ne fais que noter une complexité réduite
par un langage dont le niveau d&amp;rsquo;abstraction est plus haut.&lt;/p&gt;
&lt;p&gt;Aujourd&amp;rsquo;hui, dans la plupart des langages que j&amp;rsquo;utilise (PHP, Python,
C/C++, Java, &amp;hellip;) je vois des implémentations de design patterns quasi
identiques: Iterator, Singleton, Factory&amp;hellip; tous sont des
implémentations &lt;strong&gt;non natives&lt;/strong&gt; alors que le développeur en a besoin
tous les jours.&lt;/p&gt;
&lt;p&gt;Deux options se présentent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;avoir les implémentations existantes&lt;/li&gt;
&lt;li&gt;utiliser un langage qui s&amp;rsquo;en abstrait&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La première solution est celle que l&amp;rsquo;on empreinte finalement presque
tout le temps, car changer de langage dans une infrastructure logiciel
complexe est de l&amp;rsquo;ordre de l&amp;rsquo;impossible.&lt;/p&gt;
&lt;p&gt;La seconde solution fonctionne dans le cas d&amp;rsquo;un nouveau projet et si
vous êtes certain que l&amp;rsquo;impact ne sera pas conséquent dans une
infrastructure. Cela dit, ces langages sont généralement relativement
nouveaux et sont laissés de coté en faveur d&amp;rsquo;un langage plus répandu, et
qui n&amp;rsquo;offre pas ces évolutions.&lt;/p&gt;
&lt;p&gt;Donc, en en revient à mon plaidoyer, si tous les langages demandent une
implémentation non native d&amp;rsquo;un pattern que le langage ne résous pas,
alors que de nouveaux langages s&amp;rsquo;en abstraient, on en revient au pattern
&amp;ldquo;sous-routine&amp;rdquo; réglé par le langage C face à l&amp;rsquo;assembleur. Vous ne
pouvez pas ne pas admettre le fait: le langage que vous utilisez a une
faiblesse, même si la solution existe le problème est bien là.&lt;/p&gt;
&lt;p&gt;Lisez la page: &lt;a href=&#34;http://en.wikipedia.org/wiki/Software_design_pattern&#34;&gt;http://en.wikipedia.org/wiki/Software_design_pattern&lt;/a&gt;,
juste les premières lignes&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;En informatique, et plus particulièrement en développement logiciel,
un patron de conception (en anglais : « design pattern ») est un
arrangement caractéristique de modules, reconnu comme bonne pratique
en réponse à un problème de conception d&amp;rsquo;un logiciel. Il décrit une
solution standard, utilisable dans la conception de différents
logiciels.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Il n&amp;rsquo;y a pas d&amp;rsquo;équivoque, le design pattern est une réponse à un
problème standard, mais il manque l&amp;rsquo;information , je complète la
dernière phrase &amp;ldquo;Il décrit une solution standard, utilisable dans la
conception de différents logiciels &lt;em&gt;si le langage ne propose pas de
solution native&lt;/em&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Et de plus, prenons une phrase dans le préambule de la page wikipedia:
&amp;ldquo;Ils ont une influence sur l&amp;rsquo;architecture logicielle d&amp;rsquo;un système
informatique.&amp;rdquo; C&amp;rsquo;est étonnamment vrai et invraisemblable, car un pattern
&amp;ldquo;standard&amp;rdquo; c&amp;rsquo;est à dire une solution à un problème, va vous forcer d&amp;rsquo;une
part à l&amp;rsquo;implémenter mais aussi va influer sur la manière dont vous
développez votre logiciel.&lt;/p&gt;
&lt;p&gt;Ainsi, je ne peux pas définir un &amp;ldquo;pattern&amp;rdquo; autrement que par &amp;ldquo;une
faiblesse&amp;rdquo; car si le langage ne vous impose pas d&amp;rsquo;implémenter une
batterie d&amp;rsquo;implémentations qui influent sur votre code, alors il vous
affranchi d&amp;rsquo;un problème et rend votre développement plus libre tout en
respectant le standard du langage lui-même; et non pas d&amp;rsquo;une &amp;ldquo;bonne
pratique&amp;rdquo; en &amp;ldquo;add-on&amp;rdquo; de langage&amp;hellip; D&amp;rsquo;ailleurs, je me rend compte
qu&amp;rsquo;encore une fois je suis sur la voie de R.Pike, car dans la même page
où il cite &amp;ldquo;Norvig&amp;rdquo;, je vois qu&amp;rsquo;il parle de &amp;ldquo;add-ons&amp;rdquo; en parlant des
patterns.&lt;/p&gt;
&lt;p&gt;Voilà, c&amp;rsquo;est à peu près le fond de ma pensé. Je continue à utiliser des
patterns dans mes développements quand le langage ne me permet pas de
gérer certains aspects de conceptions, et je ne dis pas non plus que ces
langages sont désuets, vieillots ou à oublier. Simplement, j&amp;rsquo;estime que
les langages évoluent, que certains nouveaux apparaissent et qu&amp;rsquo;il ont
la &lt;strong&gt;force&lt;/strong&gt; de me permettre de me passer de patterns et par conséquent
réduisent mes efforts.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Montrer un message aux utilisateurs AdBlock</title>
      <link>https://www.metal3d.org/blog/2013/montrer-un-message-aux-utilisateurs-adblock/</link>
      <pubDate>Tue, 24 Sep 2013 19:59:22 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/montrer-un-message-aux-utilisateurs-adblock/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;AdBlock&amp;hellip; le cauchemars de ceux qui vivent des bannières de pubs sur
leur site&amp;hellip; On a des polémiques, on a des discussions houleuses, et en
mélangeant un peu le tout, on peut trouver un arrangement. Je vous
propose une solution qui peut plaire à presque tout le monde,
techniquement simple, qui ne gène pas (ou peu) l&amp;rsquo;utilisateur AdBlock ou
Freenaute et qui peut vous permettre de vous faire comprendre; mais
avant cela je tiens à m&amp;rsquo;exprimer une fois pour toute sur le pourquoi du
comment de l&amp;rsquo;utilisation de bannières de pubs sur différents sites que
je maintiens.&lt;/p&gt;
&lt;p&gt;Bon, soyons clairs, moi personnellement j&amp;rsquo;utilise des bannières de pubs
sur mon site. Si j&amp;rsquo;avais un moyen simple de vous proposer des logiciels,
des solutions, des services en ligne sans avoir à me ruiner avec des
serveurs dédiés, je le ferai. Le souci, c&amp;rsquo;est qu&amp;rsquo;un serveur ça coûte
bonbon.&lt;/p&gt;
&lt;p&gt;Pour ma part, entre les noms de domaines, le serveur, et sans compter le
courant à la maison pour alimenter les heures de boulot sur mes travaux,
je tape dans les 100, voir 120 euros par mois. Et le souci, c&amp;rsquo;est que
ces serveurs ne sont pas là pour héberger des solutions
professionnelles. Oui je suis développeur, mais pour un patron qui n&amp;rsquo;a
&lt;strong&gt;rien à voir avec ce que je propose en ligne&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;OK, je ne suis pas du tout un &lt;em&gt;grand blogueur&lt;/em&gt;. Mon audience se limite à
500 visites par jours, et encore je pense que certains se sont juste
perdu et ont atterrit ici pour rien. Toujours est-il que voilà, à un
moment donné je tente de proposer du contenu utile, par forcément grand
publique j&amp;rsquo;en conviens. Mais pour être clair, j&amp;rsquo;ai besoin d&amp;rsquo;aide,
financièrement parlant. Je suis pas riche, je suis juste passionné et je
fais de mon mieux.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc décidé de tenter, il y a déjà un long moment, de mettre des
bannières de pubs sur mon site. Oui là en bas de l&amp;rsquo;article. Avouez
qu&amp;rsquo;elle vous gène en rien là&amp;hellip; si ? ha&amp;hellip; c&amp;rsquo;est dommage. Dommage pour
qui ? bah pour vous&amp;hellip;&lt;/p&gt;
&lt;p&gt;Pourquoi je dis ça ? C&amp;rsquo;est simple, moi elle me permet de financer un peu
mes travaux, c&amp;rsquo;est à dire les logiciels libres que je développe et je
donne sans demander quelque chose en retour, des services que je
maintiens, et j&amp;rsquo;en passe. La totalité des revenus des bannières de pub
que je mets me donne (allez&amp;hellip; en voyant large) 5 euros dans les
meilleurs mois. La faute à qui ? bah à moi là pour le coup! Et oui,
parce que j&amp;rsquo;ai fais le choix idiot de ne pas polluer complètement mon
site avec des bannières partout. Là où certains abusent largement des
bannières de pubs alors qu&amp;rsquo;ils génèrent un trafic conséquent, moi et mes
visites ridicules je ne fais pas le choix de compenser.&lt;/p&gt;
&lt;p&gt;Sauf que justement, moi qui ne compense pas en vous polluant la vue sur
tout l&amp;rsquo;écran, je me vois réduis à être classifié dans le même groupe que
le &amp;ldquo;salop qui profite des visiteurs&amp;rdquo; à cause des abus&amp;hellip; Du coup,
l&amp;rsquo;autre là avec son site chargé comme une mule de pubs, il profite
toujours des revenus de ceux qui n&amp;rsquo;ont pas de bloqueur de pubs, mais
moi, par contre, ça me vautre littéralement alors que je pense être
largement en dessous du seuil d&amp;rsquo;abus.&lt;/p&gt;
&lt;p&gt;Alors que je tente de faire les choses bien, on me prive de plus en plus
de mes revenus. Adblock Plus par exemple, et Free avec son système
anti-pub, me virent près de 40% de ce que je pourrais gagner. Ce chiffre
ne sort pas de nul part, il sort de mes rapports de stats. Je sais
comment connaitre le ratio en regardant simplement sur mes sites le
nombre d&amp;rsquo;impression de pubs par rapport à mes visites réelles.&lt;/p&gt;
&lt;p&gt;Alors quelles sont les solutions ?&lt;/p&gt;
&lt;h1 id=&#34;solution-1-jarrête-tout&#34;&gt;Solution 1, j&amp;rsquo;arrête tout.&lt;/h1&gt;
&lt;p&gt;Bah à un moment donné, à force d&amp;rsquo;en baver j&amp;rsquo;ai littéralement ralenti mon
rythme de travail. J&amp;rsquo;ai arrêté de supporter mon serveur de dépôt Fedora
de près de 40 paquets que je maintenais (qui ne pouvaient pas entrer
dans rpmfusion et dans les dépôts officiels). Passer près de 4h par jour
à développer &lt;a href=&#34;https://github.com/metal3d/kwiscale&#34;&gt;Kwiscale&lt;/a&gt;,
&lt;a href=&#34;https://github.com/metal3d/knotter&#34;&gt;Knotter&lt;/a&gt;,
&lt;a href=&#34;https://github.com/metal3d/baboosh&#34;&gt;Baboosh&lt;/a&gt; ou
&lt;a href=&#34;https://github.com/VerbalExpressions/GoVerbalExpressions&#34;&gt;GoVerbalExpressions&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;10 autres projets&amp;hellip; pour en plus payer tout ça (serveur d&amp;rsquo;appli, noms
de domaines&amp;hellip;) avouez que ça décourage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;ai donc arrêté et je ne maintiens plus que deux ou trois projets&amp;hellip; et
encore&amp;hellip; je vous assure que je pense de plus en plus à tout couper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A ceux qui vont me répondre: t&amp;rsquo;as des services gratuit pour avoir un
blog etc&amp;hellip;&lt;/strong&gt; je répond de suite: non. Enfin oui&amp;hellip; je peux très bien
avoir un site sur BlogSpot par exemple, mais le but est tout de même de
faire tourner mes outils. Là par exemple, c&amp;rsquo;est
&lt;a href=&#34;https://github.com/metal3d/knotter&#34;&gt;Knotter&lt;/a&gt; qui répond. Un outil que
je développe pour le moment dans mon coin mais qui permet de faire des
sites comme ce blog. Le souci c&amp;rsquo;est que tous les serveurs qu&amp;rsquo;on me
propose gratuitement ne me permettent pas de faire tourner mon framework
node. J&amp;rsquo;attends pas la gloire, mais de faire tourner en production un
outil qui, à terme, peut servir à d&amp;rsquo;autres que mon misérable blog. Donc,
je répète, NON je ne peux pas aller sur un service gratuit.&lt;/p&gt;
&lt;p&gt;Et encore, je parle pour le moment très peu de mon autre projet sous Go
(Golang), nommé Kwiscale, pourtant j&amp;rsquo;ai un autre blog qui tourne dessus,
mais comme je ne connais pas encore sa limite en sécurité, je dois le
faire tourner sur mon serveur et vérifier tout ça. Je prends des heures
à développer tout ce petit monde, en espérant qu&amp;rsquo;un jour des gens
l&amp;rsquo;utilisent.&lt;/p&gt;
&lt;h1 id=&#34;solution-2-jarrête-le-libre&#34;&gt;Solution 2, j&amp;rsquo;arrête le libre&lt;/h1&gt;
&lt;p&gt;Bah à un moment donné, la lâcheté me pendait au nez. Retourner sur
Windows et faire un gros projet pour les mecs prêt à payer 15€ par
copie, je me suis posé la question. Même en ne vendant que 10-15
produits par mois, ça arrondissait largement les angles pour mon
banquier. Sauf que voilà, moi j&amp;rsquo;aime pas le fait de fermer la
connaissance. J&amp;rsquo;ai toujours soutenu &lt;em&gt;le libre&lt;/em&gt; et je ne vois pas trop
comment l&amp;rsquo;argent pourrait polluer à ce point cet engouement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ma vrai richesse c&amp;rsquo;est ma passion, et le fait de vouloir la
partager.&lt;/strong&gt; Donc non, hors de question de passer à cette solution.&lt;/p&gt;
&lt;h1 id=&#34;solution-3-je-fais-la-manche&#34;&gt;Solution 3, je fais la manche&lt;/h1&gt;
&lt;p&gt;Pas si con en fait&amp;hellip; Mettre un petit encart avec marqué &amp;ldquo;sivousplééééé&amp;rdquo;
ou un truc du genre. Non, je suis bête&amp;hellip; &lt;strong&gt;les gens ne veulent pas
payer&lt;/strong&gt; et moi le premier. Si je devais faire un don à tous les sites
que je visite et que j&amp;rsquo;adore, je pense que mon banquier me pendrait.
Même à un euro par mois et par site, je serai très mal.&lt;/p&gt;
&lt;p&gt;Par contre j&amp;rsquo;ai fait pas mal de dons, Ardour, Blender,&amp;hellip; je donne quand
j&amp;rsquo;utilise, parce que ces mecs méritent d&amp;rsquo;être aidé. Mais moi, je suis
pas Ton Roosendhal et j&amp;rsquo;ai pas le génie de Thorvalds. Bref, le don ça
marche pas. Donc à la limite, un mec qui a des sous, si il met 1 euro
dans mes projets, je suis preneur.&lt;/p&gt;
&lt;p&gt;1 euro ça parait rien comme ça&amp;hellip; mais c&amp;rsquo;est preque 1% de mes couts de
maintenance&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;solution-4-je-laisse-la-pub-et-je-supplie-les-gens-de-me-whitelister&#34;&gt;Solution 4, je laisse la pub et je supplie les gens de me whitelister&lt;/h1&gt;
&lt;p&gt;Mhhhh pas bête ça ! A la limite, je vais pas interdire un mec de lire
mes articles, de télécharger mes sources de logiciels etc&amp;hellip; sous
prétexte qu&amp;rsquo;il veut pas voir les pub de &amp;ldquo;meetruc&amp;rdquo; ou &amp;ldquo;comment blanchir
ses dents avec de la javel alimentaire&amp;rdquo;&amp;hellip; mais si je lui explique que
ces pubs aussi grotesques soit-elles parfois (souvent) sont très
importantes pour que je puisse continuer à lui fournir des outils, des
articles, etc&amp;hellip; Peut-être qu&amp;rsquo;il acceptera. Ou mieux, je lui propose de
faire un don ou de désactiver le blocage de pub mais je lui laisse aussi
le choix de ne rien faire. Y&amp;rsquo;a aussi des gens qui peuvent pas payer et
qui ne veulent pas voir de pub. J&amp;rsquo;aurais beau tenter de lui expliquer
que &lt;strong&gt;voir de la pub ne lui coute rien, que ça m&amp;rsquo;aide à payer mon
serveur qu&amp;rsquo;il utilise pour lire mes déblatérations, et qu&amp;rsquo;en plus ça lui
permet de pouvoir jouer avec mes outils sans rien payer&lt;/strong&gt;, si il veut
pas comprendre, je vais pas le blâmer&amp;hellip; tant pis pour moi.&lt;/p&gt;
&lt;p&gt;Donc la solution est pas si mal. Mais comment savoir si il a coupé mes
pubs ?&lt;/p&gt;
&lt;h1 id=&#34;afficher-un-popup-pour-celui-qui-coupe-les-pubs&#34;&gt;Afficher un popup pour celui qui coupe les pubs&lt;/h1&gt;
&lt;p&gt;Y&amp;rsquo;a un moyen simple, c&amp;rsquo;est de jouer avec le bloqueur lui même. Que fait
le bloqueur ? il regarde le DNS utilisé, ou simplement le nom du ficher
récupéré (c&amp;rsquo;est ce que fait AdBlock) pour aller chercher le javascript
et/ou l&amp;rsquo;iframe de la pub, et il arrête le chargement &lt;em&gt;avant&lt;/em&gt; affichage.&lt;/p&gt;
&lt;p&gt;On peut alors imaginer simplement que le bloqueur cherche un truc simple
dans le nom du ficher, par exemple &amp;ldquo;ads&amp;rdquo;. Je me suis alors adonné à une
expérience. J&amp;rsquo;ai posé un fichier css nommé &amp;ldquo;myblogads.css&amp;rdquo; et (miracle
?) le bloqueur de pub refuse son chargement&amp;hellip; L&amp;rsquo;idée est là !&lt;/p&gt;
&lt;p&gt;Je pose un div qui contient un message explicatif, du genre &amp;ldquo;hey
monsieur (ou madame) tu bloques mes pubs&amp;hellip; machin&amp;hellip; tout ça&amp;hellip; aide
moi, argent&amp;hellip; bouton de don, bouton pour cacher le div&amp;rdquo;. ID du bloc:
&amp;ldquo;soyezcool&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Ensuite, dans ce fameux CSS bloqué par AdBlock, je mets:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-css&#34;&gt;div#soyezcool { display: none }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui va se passer c&amp;rsquo;est que si le bloqueur refuse mon CSS (à cause de
son nom qui contient &amp;ldquo;ads&amp;rdquo;) la div n&amp;rsquo;est plus cachée. Par contre, pour
ceux qui se foutent de voir un petit encart de pub, rien ne se passe,
car le CSS est chargé. Donc le bloc &amp;ldquo;soyezcool&amp;rdquo; est caché.&lt;/p&gt;
&lt;p&gt;Reste à être sympa &lt;em&gt;aussi&lt;/em&gt; avec celui qui veut pas de pub. J&amp;rsquo;ai
personnellement eut l&amp;rsquo;idée (sur un autre site) de placer un cookie
valable un an qui, s&amp;rsquo;il est présent, cache direct le div.&lt;/p&gt;
&lt;h1 id=&#34;le-script-qui-fait-tout-le-patacaisse&#34;&gt;Le script qui fait tout le patacaisse&lt;/h1&gt;
&lt;p&gt;Dans un JS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function createCookie(name,value,days) {
    if (days) {
        var date = new Date();
        date.setTime(date.getTime()+(days*24*60*60*1000));
        var expires = &amp;quot;; expires=&amp;quot;+date.toGMTString();
    }
    else var expires = &amp;quot;&amp;quot;;
    document.cookie = name+&amp;quot;=&amp;quot;+value+expires+&amp;quot;; path=/&amp;quot;;
}

function readCookie(name) {
    var nameEQ = name + &amp;quot;=&amp;quot;;
    var ca = document.cookie.split(&#39;;&#39;);
    for(var i=0;i &amp;amp;lt; ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==&#39; &#39;) c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}

function eraseCookie(name) {
    createCookie(name,&amp;quot;&amp;quot;,-1);
}

// cache le message d&#39;information et place un cookie
function hide(cookietime) {
    // on retient qu&#39;on ne montera pas le message pendant X jours 
    createCookie(&amp;quot;cool&amp;quot;, &amp;quot;true&amp;quot;, cookietime); 
    // on cache le message
    document.getElementById(&amp;quot;soyezcool&amp;quot;).style.display = &amp;quot;none&amp;quot;;
}

// cas d&#39;un retour de paiement, à paramétrer chez Paypal si vous mettez un bouton de don
// place le cookie, le mec a fait un don
if (location.hash === &amp;quot;#payed&amp;quot; &amp;amp;&amp;amp; readCookie(cookiename) !== &amp;quot;true&amp;quot;) {
    hide(356); // on laisse le visiteur 1 an tranquile
}

// Bon le visiteur là, il a déjà payé ou demandé de cacher le message, on 
// cache le message par défaut
if (readCookie(cookiename) === &amp;quot;true&amp;quot;) {
    document.getElementById(divid).style.display = &amp;quot;none&amp;quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, c&amp;rsquo;est tout. Reste à faire une div dans votre HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;div id=&amp;quot;soyezcool&amp;quot;&amp;gt;
&amp;lt;button onclick=&amp;quot;hide(1)&amp;quot;&amp;gt;Fermer&amp;lt;/button&amp;gt;
&amp;lt;p&amp;gt;
    Soyez cool, coupez votre bloqueur de pub ou faire un don (mettre le bouton de don)... etc... 
    avec des explications claires et gentilles. 
    Si vous voulez pas vous pouvez continuer quand même... et bla bla bla
&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est tout&amp;hellip; Si votre visiteur a déjà pris connaissance de votre quête
ou qu&amp;rsquo;il a fait un petit don et qu&amp;rsquo;il a bien été redirigé sur votre page
suivi d&amp;rsquo;un &amp;ldquo;#payed&amp;rdquo; dans l&amp;rsquo;URL, il ne verra ni la pub, ni votre div
d&amp;rsquo;information.&lt;/p&gt;
&lt;p&gt;Dans le cas d&amp;rsquo;un don, le message disparait 1 an, pour une personne qui
cache juste le message, on le fait pour 1 jour. &lt;strong&gt;Oui là je le fais à la
mode cookie&lt;/strong&gt; ce qui veut dire que:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le payeur change de navigateur ou supprime les cookies, il verra de
nouveau le message&lt;/li&gt;
&lt;li&gt;en lisant le code, on peut facilement être tranquile 1 an sans
payer&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je sais tout ça&amp;hellip; mais je suis pas là pour fliquer tout le monde&amp;hellip; et
puis encore une fois je suis désolé de me défausser mais si AdBlock
n&amp;rsquo;était pas là il ne verrait qu&amp;rsquo;une pub en bas de page et pas un gros
popup une fois par jours en haut de page&amp;hellip; et je continue sur ma
lancée, si les foutus sites remplis de bannières de pub n&amp;rsquo;avaient pas
été aussi avide de pognon, vous ne seriez pas un utilsateur de AdBlock,
et par conséquent moi j&amp;rsquo;aurai pas à ruser pour vous montrer un message.
&lt;strong&gt;La faute est aux profiteurs, pas à vous chers lecteurs qui utilisez
AdBlock ou qui êtes chez Free.&lt;/strong&gt; Je tiens à être clair sur ce point !&lt;/p&gt;
&lt;p&gt;Personnellement j&amp;rsquo;ai déjà utilisé cette méthode sur ma page
&lt;a href=&#34;http://asm.metal3d.org&#34;&gt;http://asm.metal3d.org&lt;/a&gt; étant donné le temps que m&amp;rsquo;a pris ce petit
émulateur d&amp;rsquo;assembleur&amp;hellip;&lt;/p&gt;
&lt;p&gt;Comprenez moi bien, je comprends largement le fait que la pub vous
ennui, mais pensez à ceux qui posent des pubs avec parcimonie (tel que
moi) et qui ne cherchent pas la richesse, mais la subsistance&amp;hellip; je suis
pas un Robin des bois, mais je donne pas mal de mon temps à des projets
qui ne me rapportent rien, mis à part l&amp;rsquo;immense joie (et c&amp;rsquo;est énorme,
croyez moi) d&amp;rsquo;avoir de temps à autre des commentaires par mail sur
différents projets que je donne de bon cœur. Pensez juste au fait que
tout cela me prend du temps, beaucoup de temps, et que ça me coute
vraiment cher par mois pour maintenir le matériel en marche. Si ce que
je donne vous aide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;faites un don sur la page de projet qui vous intéresse&lt;/li&gt;
&lt;li&gt;ou whiltelistez mes pages de votre bloqueur de pub (si c&amp;rsquo;est Free je
sais pas comment vous devez faire&amp;hellip;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En espérant que vous ne preniez pas comme une agression ce billet de
site. Bonne soirée à tous !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Une ligne pour avoir un serveur HTTP de développement</title>
      <link>https://www.metal3d.org/blog/2013/une-ligne-pour-avoir-un-serveur-http-de-d%C3%A9veloppement/</link>
      <pubDate>Sun, 22 Sep 2013 09:37:05 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/une-ligne-pour-avoir-un-serveur-http-de-d%C3%A9veloppement/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;En parcourant des pages de ci de là, j&amp;rsquo;ai découvert deux commandes qui
permettent de développer rapidement des pages nécessitant des appels
HTTPRequest,&lt;/p&gt;
&lt;p&gt;Alors que je m&amp;rsquo;amusais à développer un &lt;a href=&#34;http://asm.metal3d.org&#34;&gt;émulateur
d&amp;rsquo;Assembleur&lt;/a&gt; avec en prime quelques binding de
AngularJS, il m&amp;rsquo;a fallut utiliser un serveur Web pour que les appels
HTTPRequest (ce que beaucoup appellent à tort Ajax). De prime abord, on
aurait le réflexe de se dire &amp;ldquo;ok, on a deux possibilités, je me
configure mon serveur Web pour qu&amp;rsquo;il puisse servir mon dossier de
travail, ou sinon je déplace le tout dans /var/www&amp;hellip;&amp;rdquo; avec tout les
soucis de droits qu&amp;rsquo;il faut gérer.&lt;/p&gt;
&lt;p&gt;Arrêtez vous ! y&amp;rsquo;a plus simple ! Vous avez certainement Python installé
sur votre poste ? Alors faites simplement cette opération :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /répertoire/à/utiliser
python -m SimpleHTTPServer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et hop ! un serveur web qui tourne. Il écoute sur le port 8000 par
défaut. Si ça vous plait pas, utilisez :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python -m SimpleHTTPServer 8082
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir de maintenant il écoute le port 8082. C&amp;rsquo;est bête comme choux.
Il écoute, de plus, sur toutes les interfaces. Du coup votre instance
est accessible depuis le réseau.&lt;/p&gt;
&lt;p&gt;Vous préférez PHP ? Pas de souci:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /répertoire/à/utiliser
php -S 0.0.0.0:8000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pareil que plus haut, mais en prime un &amp;ldquo;log en couleur&amp;rdquo; qui vous
permettra de mieux voir les erreurs 404 (not found).&lt;/p&gt;
&lt;p&gt;Attention hein, ces lignes ne doivent servir que pour les développements
locaux. &lt;strong&gt;Ne les utilisez pas en production&lt;/strong&gt;, c&amp;rsquo;est juste pas fait pour
ça, ce serait dangereux et pas efficace.&lt;/p&gt;
&lt;p&gt;Petit edit de dernière minute, merci à Frédéric Thouin (sur Google+):
Ces commandes permettent aussi de faire un partage réseau ponctuel.
C&amp;rsquo;est super pratique, vous vous placez dans le répertoire, vous tapez la
commande, et si aucun fichier &amp;ldquo;index.html&amp;rdquo; n&amp;rsquo;est présent, vous pouvez
naviguer (via un navigateur internet) dans les répertoires et fichiers.
C&amp;rsquo;est super pratique, et bien plus rapide que de créer un partage samba
(ou sur Windows, partager un dossier en spécifiant que le partage est
public).&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Technique de masque binaire</title>
      <link>https://www.metal3d.org/blog/2013/technique-de-masque-binaire/</link>
      <pubDate>Fri, 09 Aug 2013 13:55:16 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/technique-de-masque-binaire/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Une fois n&amp;rsquo;est pas coutume, j&amp;rsquo;ai décidé d&amp;rsquo;expliquer une technique
courante mais souvent oubliée par les développeurs, cela sans aucune
forme de critique. C&amp;rsquo;est le sytème de masque binaire pour créer des
drapeaux (ou flag) d&amp;rsquo;option.&lt;/p&gt;
&lt;p&gt;Ce post s&amp;rsquo;adresse surtout au développeurs qui débutent ou encore ceux
qui ont perdu l&amp;rsquo;habitude de travailler avec des types primitifs. Si vous
connaissez cette méthode je ne vous apprendrai rien.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée d&amp;rsquo;écrire un post de blog sur le sujet me vient du fait que moi
même j&amp;rsquo;ai commis l&amp;rsquo;erreur d&amp;rsquo;oublier d&amp;rsquo;utiliser cette méthode très propre
lors de mon écriture d&amp;rsquo;implémentation de
&lt;a href=&#34;https://github.com/VerbalExpressions/GoVerbalExpressions&#34;&gt;&amp;ldquo;VerbalExpression&amp;rdquo;&lt;/a&gt;
en Go. C&amp;rsquo;est un certain surnommé &amp;ldquo;maurodec&amp;rdquo; qui m&amp;rsquo;a mis sur la voie avec
son implémentation que je tente de fusionner à la mienne.&lt;/p&gt;
&lt;p&gt;Le principe est connu, assez simple mais rarement utilisé si comme moi
vous travaillez &amp;ldquo;trop vite&amp;rdquo; pour implémenter une librairie&amp;hellip;&lt;/p&gt;
&lt;p&gt;Je vais partir de mon implémentation de VerbalExpressions pour vous
expliquer le principe.&lt;/p&gt;
&lt;p&gt;VerbalExpression permet de construire des expressions régulières
simplement. Elle permet d&amp;rsquo;utiliser des méthodes qui active ou non des
options telles que:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ignorer la casse&lt;/li&gt;
&lt;li&gt;capturer en multiligne&lt;/li&gt;
&lt;li&gt;le caractère &amp;ldquo;.&amp;rdquo; prend aussi les n&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On ne va prendre que ces cas pour le moment.&lt;/p&gt;
&lt;p&gt;Si on utilise la méthode &amp;lsquo;SearchOneLine(true)&amp;rsquo; il faut ajouter &amp;ldquo;m&amp;rdquo; au
modificateur d&amp;rsquo;expression régulière, si &amp;ldquo;WithCaseSensitive(true)&amp;rsquo; on
ajoute &amp;ldquo;i&amp;rdquo; au modificateur. Si on passe &amp;ldquo;false&amp;rdquo; à ces méthodes, on doit
supprimer le modificateur associé. Pas d&amp;rsquo;inquiétude, je vais éclaircir
tout ça.&lt;/p&gt;
&lt;p&gt;Ma première idée était simple, avoir une &amp;ldquo;chaine&amp;rdquo; qui se concaténait
avec les modificateurs&amp;hellip; et supprimer la lettre correspondante via
strings.Replace si on demandait la suppressions. &lt;em&gt;C&amp;rsquo;est tout simplement
pas du tout efficace&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Seconde idée, avoir un &amp;ldquo;slice&amp;rdquo; (genre de hash) qui avec pour chaque
lettre &amp;ldquo;m&amp;rdquo; et &amp;ldquo;i&amp;rdquo; un booléen associé, puis concaténer selon le cas&amp;hellip;
encore une fois mauvais idée.&lt;/p&gt;
&lt;p&gt;La troisième fut la bonne (merci à maurodec): utiliser les masques !&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée est simple: on crée des constante qui ont une valeur
particulière. Ensuite je garde une variable qui opère de manière
binaire, et enfin quand je veux vérifier les flags, je fais des masques.&lt;/p&gt;
&lt;p&gt;Houlla je vous perds&amp;hellip; on va y aller doucement.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord, revenons à la base de nos vieux cours d&amp;rsquo;info.&lt;/p&gt;
&lt;p&gt;Une variables est une valeur binaire, une représentation de 0 et de 1.
Selon le type, le langage interprétera cette série pour l&amp;rsquo;utiliser de
différente manière.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On lit de droite à gauche !&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prenons le nombre entier &amp;ldquo;1&amp;rdquo;, en binaire 4 bits c&amp;rsquo;est &amp;ldquo;0001&amp;rdquo;. Ok&amp;hellip;&lt;/li&gt;
&lt;li&gt;Le nombre entier 2 est &amp;ldquo;0010&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Pour le &amp;ldquo;3&amp;rdquo; : &amp;ldquo;0011&amp;rdquo;&lt;/li&gt;
&lt;li&gt;etc&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si l&amp;rsquo;on regarde ces nombres, plus on avance dans les nombres entiers,
plus on assigne des &amp;ldquo;1&amp;rdquo; vers la gauche. Ainsi &amp;ldquo;8&amp;rdquo; est &amp;ldquo;1000&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Bon, maintenant, regadons ces 4 représentations binaires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0001 = 1&lt;/li&gt;
&lt;li&gt;0010 = 2&lt;/li&gt;
&lt;li&gt;0100 = 4&lt;/li&gt;
&lt;li&gt;1000 = 8&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Que des &amp;ldquo;0&amp;rdquo; + un &amp;ldquo;1&amp;rdquo; qui se décalle à gauche.&lt;/p&gt;
&lt;p&gt;Le principe va être &lt;strong&gt;très simple&lt;/strong&gt;, on regarde chaque bit du mot
binaire, on imagine simplement que &lt;strong&gt;chaque bit est une colonne, ou une
option&lt;/strong&gt;, qui a une valeur 1 pour active, 0 pour inactive.&lt;/p&gt;
&lt;p&gt;Les nombres 1, 2, 4, 8, 16&amp;hellip; sont interssants car ils n&amp;rsquo;ont qu&amp;rsquo;un seul
&amp;ldquo;1&amp;rdquo; dans leur représentation binaire. Du coup en les additionnant on a
juste un &amp;ldquo;ou&amp;rdquo; binaire&amp;hellip; et oui ! regardez 2+4 = 6&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=4&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;OR&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=6&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Bien&amp;hellip; on continue.&lt;/p&gt;
&lt;p&gt;Prenons ce fameux nombre &amp;ldquo;6&amp;rdquo; issue de notre opération. Il a deux &amp;ldquo;1&amp;rdquo;&amp;hellip;
un sur la colonne 2 (en partant de la droite) et un à la troisième&amp;hellip;&lt;/p&gt;
&lt;p&gt;Si on prend un à un ces nombres &amp;ldquo;1&amp;rdquo;, et qu&amp;rsquo;on élimine les autres, on
trouve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0010&lt;/li&gt;
&lt;li&gt;0100&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tien donc ! c&amp;rsquo;est bien 2 et 4&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et oui, on sait donc à partir d&amp;rsquo;un résultat retrouver les nombres qui
ont servis.&lt;/p&gt;
&lt;p&gt;Et bien maintenant, tout le problème est de savoir décomposer les
nombres qui ont composé le résultat. Et pour cela, on va faire un
masque. on y arrive.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée est de voir si on arrive à retrouver la composition binaire.&lt;/p&gt;
&lt;p&gt;On a une première méthode, faire un &amp;ldquo;&amp;amp;&amp;rdquo; (&amp;ldquo;et binaire&amp;rdquo;) avec 1, 2, 4,
8&amp;hellip;&lt;/p&gt;
&lt;p&gt;Avec 1&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=6&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;=1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;AND&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=0&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Avec 2&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=6&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;AND&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=2&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Avec 4&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=6&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=4&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;AND&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=4&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Et avec 8&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=6&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=8&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;AND&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;=0&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Donc, ça fonctionne: l&amp;rsquo;opération binaire a retourné une valeur supérieure à 0 seulement pour les nombres binaires composés d&amp;rsquo;un seul &amp;ldquo;1&amp;rdquo; qui partagent un bit en commun. C&amp;rsquo;est à dire 4 et 2.&lt;/p&gt;
&lt;p&gt;Donc, voilà une première idée, on teste avec &amp;ldquo;&amp;amp;&amp;rdquo; chaque valeur:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const (
    MULTILINE  = 1
    IGNORECASE = 2
    DOTALL     = 4
)

// ... plus loin

flags := MULTILINE | DOTALL // ça donne un 5
if ( flags &amp;amp; MULTILINE != 0 ){
    // oui, on a MULTILINE
}
if ( flags &amp;amp; IGNORECASE != 0 ){
    // oui, on a IGNORECASE
}
if ( flags &amp;amp; DOTALL != 0 ){
    // oui, on a DOTALL
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bon ça marche, mais on peut aller encore plus loin.&lt;/p&gt;
&lt;p&gt;On reviens à VerbalExpression et le fameux &amp;ldquo;modificateur&amp;rdquo; à créer.
Chaque option correspond à une lettre:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MULTILINE : m&lt;/li&gt;
&lt;li&gt;DOTALL : s&lt;/li&gt;
&lt;li&gt;IGNORECASE : i&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Si je fais &amp;ldquo;MULTILINE | DOTALL&amp;rdquo; je dois générer &amp;ldquo;ms&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Si je fais &amp;ldquo;IGNORECASE | DOTALL&amp;rdquo; je dois générer &amp;ldquo;is&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ok, maintenant j&amp;rsquo;ai besoin de regarder les options enregistrées tour à
tour&amp;hellip; mais je dois compter non pas de 0 à 2 en décimale, mais de 1 à 4 de manière
binaire (1,2,4,8&amp;hellip;)&lt;/p&gt;
&lt;p&gt;Pour compter de cette manière on va utiliser le décallage de bit.&lt;/p&gt;
&lt;p&gt;Comme vous l&amp;rsquo;avez vu, les nombres 1,2,4,8,16&amp;hellip; sont des représentation
d&amp;rsquo;un seul 1 binaire qui se décalle à gauche. L&amp;rsquo;opérateur &amp;ldquo;&amp;lt;&amp;lt;&amp;rdquo; décale
les bits vers la gauche (en assignant des 0 à droite)&lt;/p&gt;
&lt;p&gt;Ainsi:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0001 = 1&amp;lt;&amp;lt;0 (1 décallé de 0 bit vers la gauche)&lt;/li&gt;
&lt;li&gt;0010 = 1&amp;lt;&amp;lt;1 (1 décallé de 1 bit vers la gauche)&lt;/li&gt;
&lt;li&gt;0100 = 1&amp;lt;&amp;lt;2 (1 décallé de 2 bits vers la gauche)&lt;/li&gt;
&lt;li&gt;1000 = 1&amp;lt;&amp;lt;3 (1 décallé de 3 bits vers la gauche)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;0, 1, 2, 3&amp;hellip; donne 0, 1, 2, 4&amp;hellip; c&amp;rsquo;est pas génial ça ?&lt;/p&gt;
&lt;p&gt;Donc l&amp;rsquo;idée c&amp;rsquo;est que chaque itération, je décalle &amp;ldquo;1&amp;rdquo; de &amp;ldquo;i&amp;rdquo; vers la
gauche, et j&amp;rsquo;ai bien 1 puis 2 puis 4&amp;hellip;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flags := MULTILINE | DOTALL
for i:=0; i&amp;lt;4; i++ {
    if flags &amp;amp; (1&amp;lt;&amp;lt; uint(i) ) != 0 {
        // tour à tour je teste MULTILINE 
        // (quand i=1 =&amp;gt; 1&amp;lt;&amp;lt;1 = 0001 = 1)
        // puis IGNORECASE 
        // (quand i=2 =&amp;gt; 1&amp;lt;&amp;lt;2 = 0010 = 2)
        // puis DOTALL 
        // (quand i=3 =&amp;gt; 1&amp;lt;&amp;lt;3 = 0100 = 4)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors voilà comment on peut procéder:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const (
    MULTILINE  = 1
    IGNORECASE = 2
    DOTALL     = 4
)

// ... plus loin

flags := MULTILINE | DOTALL //on demande à avoir &amp;quot;m&amp;quot; et &amp;quot;s&amp;quot;

// rune correspond à un caractère, c&#39;est plus simple
// en Go pour indexer les lettres, on transforme en string à la fin
totests := []rune(&amp;quot;mis&amp;quot;) // dans l&#39;ordre exact de mes constantes

modifiers := []rune{} // on stoquera là dedans les caractères m, i, s...
for i, flag := range totests {
    // on le fait pour chaque lettre de totests
    if flags &amp;amp; (1 &amp;lt;&amp;lt; uint(i) ) != 0 {
        modifiers = append(modifiers, flag) // on ajoute la lettre associée            
    }
}

fmt.Println(string(modifiers)) // affiche &amp;quot;ms&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez aller voir l&amp;rsquo;exemple &lt;a href=&#34;http://play.golang.org/p/8J_tYrhUgq&#34;&gt;http://play.golang.org/p/8J_tYrhUgq&lt;/a&gt;
et changez la valeur de &amp;ldquo;flags&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Et pour les connaisseurs de Go, on peut déclarer nos constante via
&amp;ldquo;iota&amp;rdquo;, c&amp;rsquo;est moins ennuyeux que de compter en binaire, surtout quand on
commence à avoir un paquet de constantes&amp;hellip;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const {
    MULTILINE  = 1&amp;lt;&amp;lt;iota
    IGNORECASE = 1&amp;lt;&amp;lt;iota
    DOTALL     = 1&amp;lt;&amp;lt;iota
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reste un dernier point&amp;hellip; il faut des méthodes pour ajouter les
constantes ou les retirer. Rappelez vous: ajouter = faire un &amp;ldquo;ou&amp;rdquo;,
retirer c&amp;rsquo;est faire un &amp;ldquo;bitclear&amp;rdquo; (l&amp;rsquo;un et pas l&amp;rsquo;autre) soit &amp;ldquo;&amp;amp;^&amp;rdquo;, cela
ne perd pas les autres bits&amp;hellip;&lt;/p&gt;
&lt;p&gt;Exemple, j&amp;rsquo;ai DOTALL et j&amp;rsquo;ajoute IGNORECASE&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0100 =&amp;gt; DOTALL, 4&lt;/li&gt;
&lt;li&gt;0010 =&amp;gt; ajout de IGNORECASE, 2&lt;/li&gt;
&lt;li&gt;0110 =&amp;gt; donne 6 (4+2) par opération &amp;ldquo;ou&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On retire IGNORECASE&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0110 =&amp;gt; DOTALL | IGNORECASE = 4+2 = 6&lt;/li&gt;
&lt;li&gt;0010 =&amp;gt; on retire IGNORECASE&lt;/li&gt;
&lt;li&gt;0100 =&amp;gt; bitclear, il faut un seul 1 dans la colonne, on revient à
&amp;ldquo;4&amp;rdquo; soit DOTALL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc voilà une méthode d&amp;rsquo;exemple :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var flags unint = 0
func IgnoreCase(stat bool) {
        if stat {
            flags = flags | MULTILINE
        } else {
            flags = flags &amp;amp;^ MULTILINE
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Selon si on active ou non un mode, on ajoute ou retranche la valeur&amp;hellip;
on aurait put le faire avec &amp;ldquo;+&amp;rdquo; et &amp;ldquo;-&amp;rdquo; mais bien souvent on préfère
retyper les constantes&amp;hellip; du coup prenez l&amp;rsquo;habitude de la faire de
manière binaire.&lt;/p&gt;
&lt;p&gt;Voici un exemple avec les méthodes appliquées :
&lt;a href=&#34;http://play.golang.org/p/OICDEDceWN&#34;&gt;http://play.golang.org/p/OICDEDceWN&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Voilà, j&amp;rsquo;ai peut-être pas été super clair, mais les exemples peuvent
vous donner de la lumière.&lt;/p&gt;
&lt;p&gt;En espérant que je vous ai donné des billes&amp;hellip;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Réactiver le mass storage sur Android</title>
      <link>https://www.metal3d.org/blog/2013/r%C3%A9activer-le-mass-storage-sur-android/</link>
      <pubDate>Mon, 29 Jul 2013 11:30:03 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/r%C3%A9activer-le-mass-storage-sur-android/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Vous l&amp;rsquo;avez certainement remarqué. Android a supprimé le fameux mass
storage depuis les versions 4.x. Au lieu de cela, nous avons droit à un
protocole soit disant pratique, le MTP, mais qui engendre un certain
nombre de frustrations. Cela dit il existe un moyen de réactiver
rapidement le mode &amp;ldquo;par défaut&amp;rdquo; qu&amp;rsquo;on aimait tant.&lt;/p&gt;
&lt;p&gt;Depuis les versions 4.x, et y compris sur Cyanogen, brancher un
téléphone ou une tablette sur PC active un protocole &amp;ldquo;mtp&amp;rdquo; (multimedia
transfert protocol) et non plus un simple support USB de stockage appelé
communément &amp;ldquo;mass storage&amp;rdquo;. Outre les soucis de protocoles, il arrive
que certains bugs vous gênent, par exemple dans mon cas &lt;strong&gt;un répertoire
de 17 éléments sur mon téléphone n&amp;rsquo;affiche que 14 éléments sur le PC.&lt;/strong&gt;
Moi personnellement ça m’énerve.&lt;/p&gt;
&lt;p&gt;Rage, énervements&amp;hellip; je me suis mis à utiliser SSHDroid pour communiquer
avec mon téléphone.&lt;/p&gt;
&lt;p&gt;Mais en réalité il existe un moyen simple pour réactiver le mass
storage: &lt;em&gt;activer le mode développeur&lt;/em&gt;. Et comment on fait ? Simple ! ou
du moins&amp;hellip; quand on sait comment faire.&lt;/p&gt;
&lt;p&gt;Allez dans les paramètres (touche menu depuis l&amp;rsquo;accueil ou &amp;ldquo;Paramètres&amp;rdquo;
dans la liste des applications)&lt;/p&gt;
&lt;p&gt;En bas de l&amp;rsquo;écran vous devez voir &amp;ldquo;A propos du téléphone&amp;rdquo;, cliquez
dessus&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//statics/captures/androidms/androisms1.png%0A%20:class:%20clear&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;Au prochain écran, en bas, il doit y avoir une case &amp;ldquo;Numéro de build&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//statics/captures/androidms/androisms2.png%0A%20:class:%20clear&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;Pressez 7 fois cette case, théoriquement un &amp;ldquo;popup&amp;rdquo; apparait et décompte
le nombre de fois que vous devez encore presser la case pour activer le
mode &amp;ldquo;développeur&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//statics/captures/androidms/androisms3.png%0A%20:class:%20clear&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;Ok, après ces 7 touches, vous devez avoir dans les paramètres (écran
précédent) une case d&amp;rsquo;options pour le mode développeur&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//statics/captures/androidms/androisms4.png%0A%20:class:%20clear&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;En pressant cette entrée, vous allez avoir des options:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//statics/captures/androidms/androisms5.png%0A%20:class:%20clear&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;Activez le mode &amp;ldquo;Débogage USB&amp;rdquo; va réactiver le &amp;ldquo;mass storage&amp;rdquo;. De ce
fait, en connectant votre téléphone sur la prise USB de votre
ordinateur, vous aurez monté le téléphone comme une &amp;ldquo;clef usb&amp;rdquo; ou un
&amp;ldquo;disque dur amovible&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;En haut de l&amp;rsquo;écran se trouve un bouton pour désactiver le mode
&amp;ldquo;Développeur&amp;rdquo; au cas où vous ne voudriez plus avoir cette option.&lt;/p&gt;
&lt;p&gt;Voilà&amp;hellip; Pour ma part, plus besoin de SSHDroid et plus d&amp;rsquo;utilisation du
mode &amp;ldquo;mtp&amp;rdquo;&amp;hellip; On se demande bien pourquoi un mode aussi simple et
efficace que le mass storage a été désactivé, mais surtout pourquoi
cacher ce mode dans un menu caché&amp;hellip;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Reading some lines from a file in terminal</title>
      <link>https://www.metal3d.org/blog/2013/reading-some-lines-from-a-file-in-terminal/</link>
      <pubDate>Mon, 22 Jul 2013 09:42:32 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/reading-some-lines-from-a-file-in-terminal/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;At work, I am often asked to find commands that meet specific needs. One
of them recurs: how to make selections of lines in a file?&lt;/p&gt;
&lt;p&gt;The question is very simple, some of you have got the answer. But when
you&amp;rsquo;re not a &amp;ldquo;frequent terminal user&amp;rdquo; you can realize that it&amp;rsquo;s not as
simple.&lt;/p&gt;
&lt;p&gt;There are 4 commands to use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;head: get firsts lines of a file&lt;/li&gt;
&lt;li&gt;tail: get last lines of a file&lt;/li&gt;
&lt;li&gt;cat: get the whole content of a file&lt;/li&gt;
&lt;li&gt;sed: to manipulate specific lines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ldquo;head&amp;rdquo; and &amp;ldquo;tail&amp;rdquo; commands have got an option to get a specific number
of lines: &amp;ldquo;-n&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s create a file to test:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my line 1 for foo
bar line 2
baz line 3
and another line 4
one more for 5
and the last 6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the content in &amp;ldquo;/tmp/example.txt&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Ok now let&amp;rsquo;s test:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat /tmp/example.txt
my line 1 for foo
bar line 2
baz line 3
and another line 4
one more for 5
and the last 6

$ head -n2 /tmp/example.txt
my line 1 for foo
bar line 2

$ tail -n3 /tmp/example.txt
and another line 4
one more for 5
and the last 6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, but how to get line 4 of a file ? You can do it with head and
tail&amp;hellip; But the problem is that you will run 2 commands + an output
redirection:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ head -n 4 /tmp/example.txt | tail -n1
and another line 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, the &amp;ldquo;good&amp;rdquo; answer can be &amp;ldquo;use sed&amp;rdquo;, it will open file only one time:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sed -n &amp;quot;4p&amp;quot; /tmp/example.txt
and another line 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How to read the command ?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sed -n =&amp;gt; &amp;ldquo;-n&amp;rdquo; means &amp;ldquo;do not print anything&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;4p&amp;rdquo; =&amp;gt; when line is number 4, &amp;ldquo;print&amp;rdquo; (command &amp;ldquo;p&amp;rdquo;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ldquo;sed&amp;rdquo; can return a lineset, for example from line 3 to 5:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sed -n &amp;quot;3,5p&amp;quot; /tmp/example.txt
baz line 3
and another line 4
one more for 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;ldquo;head tail&amp;rdquo; (bad answer) equivalent, that runs 2 commands:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;head -n 5 /tmp/example.txt | tail -n 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can get several lines, for example the line 2 and 4:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sed -n &amp;quot;2p; 4p&amp;quot; /tmp/example.txt
bar line 2
and another line 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And for this example, there is no &amp;ldquo;head tail&amp;rdquo; equivalence that is
easilly usable ;)&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Vim for Golang</title>
      <link>https://www.metal3d.org/blog/2013/vim-for-golang/</link>
      <pubDate>Sun, 07 Jul 2013 13:02:27 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/vim-for-golang/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Developping with Go is easy, a simple text editor and you&amp;rsquo;re ready to
work. But it&amp;rsquo;s very confortable to have syntax highlight, checker and
outline view. There are lot of possible configurations for a lot of
editors. My preference is Vim + some plugins. Let me show you how I
configured my &amp;ldquo;Go vim IDE&amp;rdquo;&lt;/p&gt;
&lt;p&gt;At first, be sure you&amp;rsquo;ve installed a recent Go implementation. My
configuration works with Go version &amp;gt; 1.x.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m working on Fedora (18) with a vim version 7.3.&lt;/p&gt;
&lt;p&gt;What we will do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;install pathogen (or Vundle)&lt;/li&gt;
&lt;li&gt;install syntastic&lt;/li&gt;
&lt;li&gt;install gocode&lt;/li&gt;
&lt;li&gt;install tagbar + configuration&lt;/li&gt;
&lt;li&gt;install nerdtree (optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;pathogen&#34;&gt;Pathogen&lt;/h1&gt;
&lt;p&gt;Pathogen is like Vundle, it allows you to install plugins inside a
deported directory that is easier to manage. To install pathogen:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p ~/.vim/autoload ~/.vim/bundle; \
curl -Sso ~/.vim/autoload/pathogen.vim \
https://raw.github.com/tpope/vim-pathogen/master/autoload/pathogen.vim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in your ~/.vimrc, append this line &lt;strong&gt;before&lt;/strong&gt; any &amp;ldquo;filetype&amp;rdquo;
directive or &amp;ldquo;syntax&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;execute pathogen#infect()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&amp;rsquo;ll see my own vimrc plugin at the end of post.&lt;/p&gt;
&lt;p&gt;Ok, you&amp;rsquo;re done, now to install plugins, you just have to put it in
~/.vim/bundle - this is what we will do.&lt;/p&gt;
&lt;h1 id=&#34;syntastic&#34;&gt;Syntastic&lt;/h1&gt;
&lt;p&gt;I will be honest, I don&amp;rsquo;t know if syntastic is required, but if you
develop with other langages (as Python, C, Java&amp;hellip;) with vim, you will
love this plugin.&lt;/p&gt;
&lt;p&gt;Syntastic is a vim plugin that is very powerfull. It will show you
errors and warnings for a large variety of langage when you save your
vim buffer. TO install this plugin, now you&amp;rsquo;ve got pathogen installed,
is easy:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd ~/.vim/bundle
git clone https://github.com/scrooloose/syntastic.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;rsquo;s all&lt;/p&gt;
&lt;h1 id=&#34;vim-for-go&#34;&gt;Vim for go&lt;/h1&gt;
&lt;p&gt;Before installing Gocode, let&amp;rsquo;s install the given vim configuration from
your Go installation.&lt;/p&gt;
&lt;p&gt;Be sure that your environment variable contains somthing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ echo $GOROOT 
/home/patachou/src/go
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ln -s $GOROOT/misc/vim ~/.vim/bundle/go-vim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That link configuration to the &amp;ldquo;bundle&amp;rdquo; directory for vim.&lt;/p&gt;
&lt;p&gt;Now, the entire vim configuration is ready to use (thanks again to
pathogen)&lt;/p&gt;
&lt;h1 id=&#34;gocode&#34;&gt;Gocode&lt;/h1&gt;
&lt;p&gt;Now, let&amp;rsquo;s install the Go part. Gocode is a &amp;ldquo;service&amp;rdquo; that will be
connected to your vim instance. It will add lot of very interessing
functionnalities as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;code check&lt;/li&gt;
&lt;li&gt;code completion&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In your terminal, execute this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go get -u github.com/nsf/gocode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This add the &amp;ldquo;gocode&amp;rdquo; command. Vim will use if needed. The deamon will
start at time.&lt;/p&gt;
&lt;h1 id=&#34;some-vim-configuration&#34;&gt;Some vim configuration&lt;/h1&gt;
&lt;p&gt;We will need some additional vim configuration. Open your ~/.vimrc and
append:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set omnifunc=syntaxcomplete#Complete
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This activate &amp;ldquo;omnifunc&amp;rdquo; for the whole possible languages that implement
completion.&lt;/p&gt;
&lt;p&gt;So to conclude, this is my (cleaned for the article) vimrc file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;execute pathogen#infect()
set modeline
set number
syntax on
set omnifunc=syntaxcomplete#Complete
set mouse=a
set autoindent
set ts=4 sts=4 sw=4 expandtab
filetype plugin indent on     &amp;quot; required! 
filetype plugin on
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this lines, I&amp;rsquo;ve got:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pathogen that checks my ~/.vim/bundle directory&lt;/li&gt;
&lt;li&gt;modeline that can read some &amp;ldquo;vim directives in comments&amp;rdquo; (optionnal)&lt;/li&gt;
&lt;li&gt;line number&lt;/li&gt;
&lt;li&gt;syntax is on&lt;/li&gt;
&lt;li&gt;completion ok&lt;/li&gt;
&lt;li&gt;mouse that can be used to navigate in NerdTree, and Tagbar (after
the test, be patient ;) )&lt;/li&gt;
&lt;li&gt;indentation auto&lt;/li&gt;
&lt;li&gt;tab makes 4 spaces&lt;/li&gt;
&lt;li&gt;plugin for indentation + autocommand (required by gocode)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ok, make a test now&lt;/p&gt;
&lt;h1 id=&#34;testing-installation&#34;&gt;Testing installation&lt;/h1&gt;
&lt;p&gt;Before to try, keep the shortcuts in mind: CTRL+x - o ==&amp;gt;
autocompletion from &amp;ldquo;omnifunc&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Now, go to a temporary directory, for example /tmp and make a test
directory:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;cd /tmp; mkdir testgo; cd testgo&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And try:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;vim main.go&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;You will have vim opened, append this lines:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import &amp;quot;fmt&amp;quot;

func main() {
     // place your cursor after the &amp;quot;.&amp;quot;
     fmt.
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After dot (.) press &amp;ldquo;CTRL+x&amp;rdquo; then &amp;ldquo;o&amp;rdquo;, and you will see a menu that lets
you choose the method to use.&lt;/p&gt;
&lt;p&gt;This works with any variables, package, builtins&amp;hellip;&lt;/p&gt;
&lt;h1 id=&#34;append-2-plugins-that-will-change-your-life&#34;&gt;Append 2 plugins that will change your life&lt;/h1&gt;
&lt;p&gt;There are 2 plugins that can be very cool to use: Nerdtree and tagbar.&lt;/p&gt;
&lt;p&gt;Nerdtree is a directory viewer that shows you your project directory.
There are a lot of options, but you can use &amp;ldquo;as is&amp;rdquo;. NERDTree is made by
the same author that syntastic.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s install:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd ~/.vim/bundle
git clone https://github.com/scrooloose/nerdtree.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now install the second plugin, tagbar that is an outline viewer that
works with a lot of languages (excpeting go&amp;hellip; buuuuuuut):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd ~/.vim/bundle
git clone git://github.com/majutsushi/tagbar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To let tagbar useable with Go, we need to install &amp;ldquo;gotags&amp;rdquo; and append a
vim configuration&amp;hellip;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go get -u github.com/jstemmer/gotags
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And append this to the file ~/.vim/ftplugin/go.vim (theorically, gocode
has created this file and append a omnifunc method on top, don&amp;rsquo;t remove
this line, append this after):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot; gotags
let g:tagbar_type_go = {
    \ &#39;ctagstype&#39; : &#39;go&#39;,
    \ &#39;kinds&#39;     : [
        \ &#39;p:package&#39;,
        \ &#39;i:imports:1&#39;,
        \ &#39;c:constants&#39;,
        \ &#39;v:variables&#39;,
        \ &#39;t:types&#39;,
        \ &#39;n:interfaces&#39;,
        \ &#39;w:fields&#39;,
        \ &#39;e:embedded&#39;,
        \ &#39;m:methods&#39;,
        \ &#39;r:constructor&#39;,
        \ &#39;f:functions&#39;
    \ ],
    \ &#39;sro&#39; : &#39;.&#39;,
    \ &#39;kind2scope&#39; : {
        \ &#39;t&#39; : &#39;ctype&#39;,
        \ &#39;n&#39; : &#39;ntype&#39;
    \ },
    \ &#39;scope2kind&#39; : {
        \ &#39;ctype&#39; : &#39;t&#39;,
        \ &#39;ntype&#39; : &#39;n&#39;
    \ },
    \ &#39;ctagsbin&#39;  : &#39;gotags&#39;,
    \ &#39;ctagsargs&#39; : &#39;-sort -silent&#39;
\ }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right ok !&lt;/p&gt;
&lt;h1 id=&#34;test-tagbar-and-nerdtree&#34;&gt;Test tagbar and NERDTree&lt;/h1&gt;
&lt;p&gt;You can do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;type :NERDTree in vim&lt;/li&gt;
&lt;li&gt;open vim with +NERDTree argument&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To open Tagbar, type :TagbarOpen&lt;/p&gt;
&lt;p&gt;This is what I&amp;rsquo;ve got:&lt;/p&gt;
&lt;img src=&#34;https://www.metal3d.org//statics/go-vim.png&#34; style=&#34;width:40%; margin: auto&#34; /&gt;
&lt;h1 id=&#34;one-shot&#34;&gt;One shot&lt;/h1&gt;
&lt;p&gt;Let&amp;rsquo;s resume what we&amp;rsquo;ve made:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p ~/.vim/autoload ~/.vim/bundle; \
curl -Sso ~/.vim/autoload/pathogen.vim \
https://raw.github.com/tpope/vim-pathogen/master/autoload/pathogen.vim

ln -s $GOROOT/misc/vim ~/.vim/bundle/go-vim

cd ~/.vim/bundle
git clone https://github.com/scrooloose/syntastic.git
git clone https://github.com/scrooloose/nerdtree.git
git clone git://github.com/majutsushi/tagbar

go get -u github.com/nsf/gocode
go get -u github.com/jstemmer/gotags
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In ~/.vimrc:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;execute pathogen#infect()
set modeline
set number
syntax on
set omnifunc=syntaxcomplete#Complete
set mouse=a
set autoindent
set ts=4 sts=4 sw=4 expandtab
filetype plugin indent on     &amp;quot; required! 
filetype plugin on
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open one time a gocode, use completion, that creates
~/.vim/ftplugin/go.vim&lt;/p&gt;
&lt;p&gt;Then append to ~/.vim/ftplugin/go.vim:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot; gotags
let g:tagbar_type_go = {
    \ &#39;ctagstype&#39; : &#39;go&#39;,
    \ &#39;kinds&#39;     : [
        \ &#39;p:package&#39;,
        \ &#39;i:imports:1&#39;,
        \ &#39;c:constants&#39;,
        \ &#39;v:variables&#39;,
        \ &#39;t:types&#39;,
        \ &#39;n:interfaces&#39;,
        \ &#39;w:fields&#39;,
        \ &#39;e:embedded&#39;,
        \ &#39;m:methods&#39;,
        \ &#39;r:constructor&#39;,
        \ &#39;f:functions&#39;
    \ ],
    \ &#39;sro&#39; : &#39;.&#39;,
    \ &#39;kind2scope&#39; : {
        \ &#39;t&#39; : &#39;ctype&#39;,
        \ &#39;n&#39; : &#39;ntype&#39;
    \ },
    \ &#39;scope2kind&#39; : {
        \ &#39;ctype&#39; : &#39;t&#39;,
        \ &#39;ntype&#39; : &#39;n&#39;
    \ },
    \ &#39;ctagsbin&#39;  : &#39;gotags&#39;,
    \ &#39;ctagsargs&#39; : &#39;-sort -silent&#39;
\ }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;rsquo;s all folks&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Utiliser la reconnaissance vocale de Google</title>
      <link>https://www.metal3d.org/blog/2013/utiliser-la-reconnaissance-vocale-de-google/</link>
      <pubDate>Sat, 18 May 2013 13:27:49 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/utiliser-la-reconnaissance-vocale-de-google/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Et si on se faisait une petit application de reconnaissance vocale
maison qui marche sur le desktop ? Je recherche depuis des mois, voir
des années, une solution sous Linux qui me permettrait de faire
correctement de la reconnaissance vocale proprement. Mais mes recherches
n&amp;rsquo;ont jamais été fructueuses, que ce soit du coté de CMU Sphinx ou je ne
sais quel &amp;ldquo;Perlbox&amp;rdquo;&amp;hellip; Mais voilà, Google propose sur Android, et depuis
peu (quelques mois en fait) une api sur Chromium/Chrome qui fonctionne
vraiment bien.&lt;/p&gt;
&lt;p&gt;Alors avant toutes choses: &lt;strong&gt;très important, utiliser l&amp;rsquo;api de Google
hors Android et hors navigateur semble ne pas être autorisé. Si vous
utilisez mes exemples, vous êtes responsables de vos actes en cas de
souci avec la loi. Je ne vous propose cet article qu&amp;rsquo;à des fins de
recherche.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Relisez bien ce que je viens de taper&amp;hellip; qu&amp;rsquo;on soit bien clair, ce que
je vous propose est utile à des fins de tests jusqu&amp;rsquo;à ce que Google nous
autorise un jour (on y croit&amp;hellip;) à faire mumuse sans restriction avec
leur API&amp;hellip; ou (rêvons un peu) qu&amp;rsquo;il rende tout ça publique.&lt;/p&gt;
&lt;p&gt;On passe à la suite. Comment ça fonctionne ?&lt;/p&gt;
&lt;p&gt;Si vous visitez &lt;a href=&#34;http://translate.google.com&#34;&gt;http://translate.google.com&lt;/a&gt; vous remarquerez une icone
de micro en bas du textarea de texte à traduire. C&amp;rsquo;est une nouvelle
implémentation qui permet de proposer de dicter le texte via le micro
plutôt que de taper à la main le texte. Ceci est possible depuis
l&amp;rsquo;introduction d&amp;rsquo;un attribut &amp;ldquo;x-speech&amp;rdquo; dans les inputs et textareas.
Une très bonne explication se trouve sur la page:
&lt;a href=&#34;http://tilap.net/champs-de-saisie-vocale-en-html5-x-webkit-speech/&#34;&gt;http://tilap.net/champs-de-saisie-vocale-en-html5-x-webkit-speech/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Lorsque vous pressez le bouton, le navigateur enregistre votre voix.
L&amp;rsquo;encodage est FLAC (voilà une idée géniale) à 16kHz. Puis il le poste
sur la page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://www.google.com/speech-api/v1/recognize
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ne cherchez pas à ouvrir la page depuis votre navigateur, cette adresse
n&amp;rsquo;accèpte pas les requêtes GET. Bref, cette adresse prend aussi des
arguments, comme la langue source, le nombre de résultats attendus,
etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Pour préparer notre petit programme de reconnaissance, on va utiliser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gstreamer pour capturer votre voix&lt;/li&gt;
&lt;li&gt;curl pour poster tout ça à Google&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Gstreamer n&amp;rsquo;est pas plus simple, mais il a le mérite de ne pas me
prendre la tête pour le choix de la source d&amp;rsquo;enregistrement.&lt;/p&gt;
&lt;p&gt;Pour le pipe Gstreamer, on va utiliser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;autoaudiosrc pour laisser pulse nous donner la source (donc vérifiez
vos paramètres pour vérifier que votre micro fonctionne)&lt;/li&gt;
&lt;li&gt;audioresample pour rééchantillonner le fichier proprement&lt;/li&gt;
&lt;li&gt;utiliser la capacité audio/x-raw-int pour demander un &amp;ldquo;rate&amp;rdquo; de
16kHz (16000 hz)&lt;/li&gt;
&lt;li&gt;pour le moment: envoyer ça dans un fichier .flac&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est parti, dans un terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gst-launch autoaudiosrc ! audioresample ! audio/x-raw-int,rate=16000 ! \
flacenc ! filesink location=/tmp/voice.flac
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dites quelques mots, pas trop long, puis presssez CTRL+C. Vérifiez votre
fichier en l&amp;rsquo;écoutant avec mplayer, vlc, ou la commande &amp;ldquo;play&amp;rdquo; pour vous
assurer que vous êtes audible et qu&amp;rsquo;il n&amp;rsquo;y a pas trop de bruit.&lt;/p&gt;
&lt;p&gt;Maintenant, on va l&amp;rsquo;envoyer à Google pour voir si il reconnait
correctement la voix.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée c&amp;rsquo;est qu&amp;rsquo;il faut envoyer les informations nécessaires pour
construire une requête POST bien propre. A savoir donc:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le Content-Type qui est un audio/x-flac; rate=16000&lt;/li&gt;
&lt;li&gt;les données binaires (le fichier de son)&lt;/li&gt;
&lt;li&gt;un argument pour lui dire qui est le client, je vais mettre chromium&lt;/li&gt;
&lt;li&gt;un argument pour lui dire que je veux au pire 10 résultats de
reconnaissance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Donc la commande &amp;ldquo;curl&amp;rdquo; est la suivante:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;curl -X POST --data-binary @/tmp/voice.flac \
--header &#39;Content-type: audio/x-flac; rate=16000&#39; \
&#39;https://www.google.com/speech-api/v1/recognize?client=chromium&amp;amp;lang=fr-FR&amp;amp;maxresults=10&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela me retourne (j&amp;rsquo;ai reformaté pour plus de lisibilité):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
&amp;quot;status&amp;quot;: 0,
&amp;quot;id&amp;quot;: &amp;quot;cf3c79ce01920853aedc0825f33ac0ea-1&amp;quot;,
&amp;quot;hypotheses&amp;quot;: [
    {
        &amp;quot;utterance&amp;quot;: &amp;quot;bonjour et bienvenue&amp;quot;,
        &amp;quot;confidence&amp;quot;: 0.7474387
    },
    {
        &amp;quot;utterance&amp;quot;: &amp;quot;bonjour et bienvenu&amp;quot;
    },
    {
        &amp;quot;utterance&amp;quot;: &amp;quot;bonjour bienvenue&amp;quot;
    },
    {
        &amp;quot;utterance&amp;quot;: &amp;quot;bonjour et bienvenue à&amp;quot;
    },
    {
        &amp;quot;utterance&amp;quot;: &amp;quot;bonjour et bienvenue chez&amp;quot;
    },
    {
        &amp;quot;utterance&amp;quot;: &amp;quot;bonjour et bienvenue au&amp;quot;
    }
    ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est donc au format JSON que la réponse est faite (plutôt pratique).
Vous remarquez que ce sont les &amp;ldquo;hypothèses&amp;rdquo; de reconnaissance qui vont
nous intéresser et que nous avons quelques possibilité avec des points
(confidence) qui oscillent de 0.0 à 1.0. Si vous tentiez sans le
&amp;ldquo;maxresults&amp;rdquo;, vous n&amp;rsquo;auriez en réponse que l&amp;rsquo;hypothèse la plus haute:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -X POST --data-binary @/tmp/voice.flac \
--header &#39;Content-Type: audio/x-flac; rate=16000;&#39; \
&#39;https://www.google.com/speech-api/v1/recognize?client=chromium&amp;amp;lang=fr-FR&#39;

{
&amp;quot;status&amp;quot;: 0,
&amp;quot;id&amp;quot;: &amp;quot;06eee2607513b6f23081891d051736bc-1&amp;quot;,
&amp;quot;hypotheses&amp;quot;: [
    {
        &amp;quot;utterance&amp;quot;: &amp;quot;bonjour et bienvenue&amp;quot;,
        &amp;quot;confidence&amp;quot;: 0.7474387
    }
    ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bon me reste une ou deux choses à faire, notamment faire en sorte que
l&amp;rsquo;enregistrement de ma voix s&amp;rsquo;arrête quand je ne parle plus&amp;hellip; ha ça
c&amp;rsquo;est un truc que j&amp;rsquo;ai gratté un moment, mais j&amp;rsquo;ai fini par trouver !&lt;/p&gt;
&lt;p&gt;Il existe un pad gstreamer nommé &amp;ldquo;vader&amp;rdquo;. Ce pad est magique, il
déclenche la lecture du pipeline gstreamer quand le niveau
d&amp;rsquo;enregistrement monte, puis il met en pause quand vous ne parlez plus.
Pour tester, voici ce que vous pouvez faire:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gst-launch autoaudiosrc ! vader auto_threshold=true ! audioconvert ! \
audioresample ! audio/x-raw-int,rate=16000 ! \
flacenc ! filesink location=/tmp/tmpaudio.flac
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors, attendez&amp;hellip; pour couper le pipeline vous allez devoir presser
CTRL+C&amp;hellip; sauf que vader va peut-être l&amp;rsquo;entendre donc faites doucement.&lt;/p&gt;
&lt;p&gt;Commencez à parler, dites &amp;ldquo;un deux trois&amp;rdquo; par exemple. Attendez une
dizaine de secondes puis dites &amp;ldquo;quatre cinq&amp;rdquo;. Pesses calmement CTRL+C et
écoutez le fichier /tmp/tmpaudio.flac. Vous allez vous rendre compte que
la dizaine de secondes de pause a disparue. C&amp;rsquo;est tout le but,
l&amp;rsquo;encodage ne s&amp;rsquo;est fait que lorque vous parliez.&lt;/p&gt;
&lt;p&gt;Voilà, on approche du but:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on sait enregistrer des phrases au moment où on parle&lt;/li&gt;
&lt;li&gt;on sait envoyer un fichier à Google qui peut être utilisé pour de la
reconnaissance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hé, dites&amp;hellip; ça vous dirait de faire un petit programme qui écrit ce que
vous dites ? Python est un petit bijou pour ça.&lt;/p&gt;
&lt;p&gt;Je vous explique pas à pas.&lt;/p&gt;
&lt;p&gt;On va utiliser le module &amp;ldquo;gst&amp;rdquo; qui est le module gstreamer de python. Ce
module a une méthode bien pratique qui permet de donner le pipeline à la
manière de gst-launch plutôt que de s&amp;rsquo;entêter à faire les pad un à un et
de les raccorder.&lt;/p&gt;
&lt;p&gt;Petit rappel, les pads, ainsi que le pipeline gstreamer, envoit des
évènements. On regarde ce que donne le pad &amp;ldquo;vader&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;gst-inspect vader
...
Element Signals:
   &amp;quot;vader-start&amp;quot; :  void user_function (GstElement* object,
                                   guint64 arg0,
                                   gpointer user_data);
   &amp;quot;vader-stop&amp;quot; :  void user_function (GstElement* object,
                                  guint64 arg0,
                                  gpointer user_data);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deux évènements à retenir: vader-start et vader-stop&amp;hellip; inutile de vous
dire à quoi il servent.&lt;/p&gt;
&lt;p&gt;Ensuite, en ce qui concerne python et gst, on peut mettre en pause ou en
état de démarrage le pipeline via la méthode set_state() en lui donnant
en argument gst.STATE_PAUSED ou gst.STATE_PLAYING&lt;/p&gt;
&lt;p&gt;On va utiliser urllib2 pour envoyer les données à Google.&lt;/p&gt;
&lt;p&gt;Et enfin, pour que le programme écoute en continue sans couper le
script, on va utiliser le module gtk pour utiliser la boucle itérative
&amp;ldquo;gtk.main()&amp;rdquo;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;algo est le suivant:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;on crée le pipeline en nommant le pad vader &amp;quot;vad&amp;quot; (afin de le récupérer)
on écoute les évènement du pad vader
quand vader stop son écoute:
    on met en pause tout le pipeline pour ne pas relancer l&#39;écoute tout de suite
    on copie le fichier flac en mémoire
    on vide le fichier flac pour ne pas cumuler nos phrases
    on remet le pipeline en écoute
    on envoit la requête à Google
    si ça a marché, on affiche la meilleures hypothèse de reconnaissance
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà&amp;hellip; et bien lisez un peu le source:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import gst
import gtk
import urllib2
import json
import logging
import os

# file where we record our voice (removed at end)
FLACFILE=&#39;/tmp/tmpvoice.flac&#39;

#to be clean on logs
logging.getLogger().setLevel(logging.DEBUG)

def on_vader_start(ob, message):
    &amp;quot;&amp;quot;&amp;quot; Just to be sure that vader has reconnized that you&#39;re speaking
    we set a trace &amp;quot;&amp;quot;&amp;quot;
    logging.debug(&amp;quot;Listening...&amp;quot;)

def on_vader_stop(ob, message):
    &amp;quot;&amp;quot;&amp;quot; This function is launched when vader stopped to listen
    That happend when you stop to talk &amp;quot;&amp;quot;&amp;quot;

    logging.debug(&amp;quot;Processing...&amp;quot;)

    # pause pipeline to not break our file
    pipe.set_state(gst.STATE_PAUSED)

    # get content of the file
    flacfile = open(FLACFILE, &#39;r&#39;).read()

    # empty file, to not reprocess
    # next time
    open(FLACFILE, &#39;w&#39;).write(&#39;&#39;)

    #file is empty, continue to listen
    pipe.set_state(gst.STATE_PLAYING)

    try:
        # hey, Google ! what did I said ?
        req = urllib2.Request(&#39;https://www.google.com/speech-api/v1/&#39;
                              &#39;recognize?client=chromium&amp;amp;lang=fr-FR&amp;amp;maxresults=10&#39;,
                flacfile, {&#39;Content-Type&#39;: &#39;audio/x-flac; rate=16000&#39;})
        res = urllib2.urlopen(req)
        # thanks google...
        resp = res.read()
        resp = json.loads(resp)
        print resp[&#39;hypotheses&#39;][0][&#39;utterance&#39;]
    except:
        logging.error(&amp;quot;An error occured...&amp;quot;)



#the main pipeline
pipe = gst.parse_launch(&#39;autoaudiosrc ! vader auto_threshold=true name=vad &#39;
                        &#39;! audioconvert ! audioresample ! &#39;
                        &#39;audio/x-raw-int,rate=16000 ! flacenc ! &#39;
                        &#39;filesink location=%s&#39; % FLACFILE)

bus = pipe.get_bus()
bus.add_signal_watch()

vader = pipe.get_by_name(&#39;vad&#39;)
vader.connect(&#39;vader-start&#39;, on_vader_start)
vader.connect(&#39;vader-stop&#39;, on_vader_stop)

try:
    # start the pipeline now
    pipe.set_state(gst.STATE_PLAYING)
    logging.info(&amp;quot;Press CTRL+C to stop&amp;quot;)
    gtk.main()

except KeyboardInterrupt:
    # stop pipeline 
    pipe.set_state(gst.STATE_NULL)
    # remove our flac file
    os.remove(FLACFILE)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voici un exemple de choses que j&amp;rsquo;ai put voir en lançant mon script
python:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python voice.py 
** Message: pygobject_register_sinkfunc is deprecated (GstObject)
INFO:root:Press CTRL+C to stop
DEBUG:root:Listening...
DEBUG:root:Processing...
ceci est un exemple de reconnaissance vocale
DEBUG:root:Listening...
DEBUG:root:Processing...
comme vous pouvez voir cela fonctionne plutôt pas mal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bon il est clair qu&amp;rsquo;il faudra certainement régler le niveau du micro
pour pas tout enregistrer tout le temps, voir créer un raccourcis qui
lance cette reconnaissance one shot&amp;hellip; et puis pourquoi pas se faire des
jeux de commandes qui peuvent executer des actions&amp;hellip;&lt;/p&gt;
&lt;p&gt;En gros, tout est possible, mais encore une fois: Google n&amp;rsquo;a pas donné
explicitement son accord pour utiliser leur outil de reconnaissance
vocale comme ça&amp;hellip; alors allez-y molo avec ce jouet - c&amp;rsquo;est purement un
exemple à titre de recherche !&lt;/p&gt;
&lt;p&gt;Petite parenthèse: j&amp;rsquo;ai pas mal testé pocketsphinx, y&amp;rsquo;a bien un moment
où je vais réussir à en tirer un truc plus propre&amp;hellip; mais rien n&amp;rsquo;a égalé
ce que j&amp;rsquo;ai réussi à faire via Google&amp;hellip;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Les closures javascript et la notion de classe</title>
      <link>https://www.metal3d.org/blog/2013/les-closures-javascript-et-la-notion-de-classe/</link>
      <pubDate>Fri, 17 May 2013 11:50:04 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/les-closures-javascript-et-la-notion-de-classe/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;&lt;a href=&#34;http://nodejs.org&#34;&gt;NodeJS&lt;/a&gt;, &lt;a href=&#34;http://jquery.com&#34;&gt;JQuery&lt;/a&gt;,
&lt;a href=&#34;http://mootools.net&#34;&gt;Mootools&lt;/a&gt;, &lt;a href=&#34;http://www.html5rocks.com&#34;&gt;HTML5&lt;/a&gt;&amp;hellip;
le javascript s&amp;rsquo;est imposé. Mais un sujet encore mal compris par
beaucoup de développeurs JS me saute aux yeux. Depuis que je développe
&lt;a href=&#34;https://github.com/metal3d/knotter&#34;&gt;Knotter&lt;/a&gt; et que j&amp;rsquo;ai l&amp;rsquo;occasion
d&amp;rsquo;en discuter, la tendance se creuse: beaucoup n&amp;rsquo;ont pas bien assimilé
le concept de closure. Alors faisons un point sur les closures JS.&lt;/p&gt;
&lt;p&gt;Les closures ne sont pas nouvelles et Javascript n&amp;rsquo;est pas le seul à les
implémenter. &lt;a href=&#34;http://golang.org&#34;&gt;Go&lt;/a&gt; par exemple implémente le concept
de &amp;ldquo;full closure&amp;rdquo;, et PHP entre dans la danse. Mais il y a aussi C#,
Perl, Python&amp;hellip; tous utilisent le principe de closure.&lt;/p&gt;
&lt;p&gt;Si il y a bien un chose qui est mal écrit dans Javascript, c&amp;rsquo;est ce
fichu mot clef &amp;ldquo;function&amp;rdquo;. Car en réalité, une fonction Javascript n&amp;rsquo;est
pas réellement une fonction.&lt;/p&gt;
&lt;p&gt;Alors c&amp;rsquo;est quoi une closure ? Et bien simplement, c&amp;rsquo;est une notion de
fonction qui peut garder son contexte actif et faire référence à des
variables libres.&lt;/p&gt;
&lt;p&gt;Bon, prenons un exemple de &amp;ldquo;fonction&amp;rdquo;, elle peut prendre en argument des
valeurs (ou pas) et retourner une valeur (ou pas&amp;hellip;)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function Add(a, b) {
    var c= a+b;
    return c;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est simple, c&amp;rsquo;est basique, c&amp;rsquo;est une fonction ! Sauf que je peux très
bien écrire la même chose de cette manière:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;var Add = function (a, b){
    var c = a+b;
    return c;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Etonnant ? je peux assigner une fonction à une variable. La variable
&amp;ldquo;Add&amp;rdquo; contient la fonction. &amp;ldquo;Contient&amp;rdquo; ? et oui, car une fonction est un
genre d&amp;rsquo;objet qu&amp;rsquo;on peut assigner quelque part, ce genre d&amp;rsquo;objet
s&amp;rsquo;appelle une &lt;em&gt;closure&lt;/em&gt; (le terme &amp;ldquo;fermeture&amp;rdquo; en français existe)&lt;/p&gt;
&lt;p&gt;Alors si je peux l&amp;rsquo;assigner, au même titre que la variable &amp;ldquo;c&amp;rdquo; dans mon
exemple, je peux faire une fonction qui retourne une fonction ! On
tente:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function createAddFunction(){
    var add = function (a, b){
        var c = a+b;
        return c;
    };

    return add;
}

var myadd = createAddFunction();

console.log( myadd(4, 5) ); //=&amp;gt; retourne 9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour le moment l&amp;rsquo;utilité n&amp;rsquo;est pas probante, mais vous comprennez
aisément ce qu&amp;rsquo;il se passe. La variable &amp;ldquo;myadd&amp;rdquo; se voit assignée de la
fonction retournée par createAddFunction. Par conséquent elle contient
la fonction &amp;ldquo;add&amp;rdquo; qui va être créée seulement au moment de l&amp;rsquo;appel
&amp;ldquo;createAddFunction&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Si c&amp;rsquo;est pas génial ça ?&lt;/p&gt;
&lt;p&gt;Mais là où la closure prend tout son sens et son intérêt, c&amp;rsquo;est qu&amp;rsquo;elle
capture le contexte dans lequelle elle est créée.&lt;/p&gt;
&lt;p&gt;Je vais être plus clair: la fonction &amp;ldquo;add&amp;rdquo; retournée est créée dans une
fonction nommé &amp;ldquo;createAddFunction&amp;rdquo;. Mais cette seconde fonction, c&amp;rsquo;est
aussi une closure ! Si je créee une closure à l&amp;rsquo;intérieur d&amp;rsquo;un autre,
les variables de la closure parente sont accessibles dans la descendante
tant qu&amp;rsquo;elle s&amp;rsquo;en sert.&lt;/p&gt;
&lt;p&gt;Voici un exemple simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function getCounter() {
    var c = 0;
    return function (){
        return c++;
    }
}

var counter1 = getCounter();
var counter2 = getCounter();

// on utilise le premier compteur
console.log( counter1() ); // 1
console.log( counter1() ); // 2

// on passe au seconde compteur
console.log( counter2() ); // 1
console.log( counter2() ); // 2

// on revient au premier compteur
console.log( counter1() ); // 3
console.log( counter1() ); // 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà ce qu&amp;rsquo;est une closure que l&amp;rsquo;on appelle &amp;ldquo;full closure&amp;rdquo;. Le contexte
parent est assigné à ses descendants. Ainsi, &amp;ldquo;counter1&amp;rdquo; contient la
fonction qui retourne &amp;ldquo;c++&amp;rdquo;. Mais comme cette variable &amp;ldquo;c&amp;rdquo; est
initialisée dans la closure &amp;ldquo;getCounter&amp;rdquo; elle a accès à la variable &amp;ldquo;c&amp;rdquo;
qui a été créée au moment de l&amp;rsquo;appel à getCounter. Je vous rappelle au
passage que chaque appel à getCounter est séparé, on parle de contexte,
et par conséquent la variable &amp;ldquo;c&amp;rdquo; n&amp;rsquo;est pas partagé à tous les
compteurs. Ce ne sont pas des singletons et c&amp;rsquo;est tant mieux.&lt;/p&gt;
&lt;p&gt;Ce concept est très utile car il permet de ne pas garder globalement des
variables, et surtout il permet de controller la mémoire aisément.&lt;/p&gt;
&lt;p&gt;Mais ce qui est fabuleux en JS, c&amp;rsquo;est qu&amp;rsquo;en plus d&amp;rsquo;être &amp;ldquo;full closure&amp;rdquo;,
les fonctions peuvent avoir un prototype. Du coup, on peut instancier
une fonction dont on aura accès à des méthodes, des propriétés&amp;hellip; ha
mais oui ! on dirait bien une classe ! &lt;strong&gt;Sauf qu&amp;rsquo;il faut garder à
l&amp;rsquo;esprit que ce ne sont pas des classes !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Tout comme en Go qui utilise un principe de structures et d&amp;rsquo;interfaces
permettant de &amp;ldquo;simuler&amp;rdquo; des classes, gardez à l&amp;rsquo;esprit que JS n&amp;rsquo;a pas de
classe réelle mais des closures avec un prototype.&lt;/p&gt;
&lt;p&gt;Voici une classe simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function Animal() {
    console.log(&amp;quot;Naissance de l&#39;animal&amp;quot;);
}
Animal.prototype.name = null ;

Animal.prototype.getName = function (){
    return this.name;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce mot clef &amp;ldquo;prototype&amp;rdquo; est une quelque sorte un &amp;ldquo;objet&amp;rdquo; qui contient
des propriétés et fonctions.&lt;/p&gt;
&lt;p&gt;Vous avez remarqué le mot &amp;ldquo;this&amp;rdquo; dans la méthode &amp;ldquo;getName&amp;rdquo;. Elle fera
référence à l&amp;rsquo;animal en cours. Sauf que voilà&amp;hellip; pour que &amp;ldquo;this&amp;rdquo; soit
une référence à un animal (et pas tous les animaux qu&amp;rsquo;on créera) il faut
&amp;ldquo;instancier&amp;rdquo; la closure. C&amp;rsquo;est le rôle du mot clef &amp;ldquo;new&amp;rdquo;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;var tom = new Animal()
tom.name = &amp;quot;Tom le chat&amp;quot;
console.log( tom.getName() ); //=&amp;gt; Tom le chat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà, on a l&amp;rsquo;impression d&amp;rsquo;utiliser une classe qui s&amp;rsquo;instancie dans
un objet nommé &amp;ldquo;tom&amp;rdquo;. En réalité, on a instancié le prototpye
initialisé. En passant, &lt;em&gt;l&amp;rsquo;appel à &amp;ldquo;new Animal()&amp;rdquo; a appelé la fonction
&amp;ldquo;Animal&amp;rdquo; que l&amp;rsquo;ont peut voir comme un &amp;ldquo;constructeur&amp;rdquo;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Et donc, si un prototype est un objet&amp;hellip; rien ne nous empêche de
l&amp;rsquo;initialiser avec un autre objet. Par exemple, un chat :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function Chat() {

}

Chat.prototype = new Animal();

Chat.prototype.classification = &amp;quot;chat&amp;quot;;

var hercule = new Chat();
hercule.name = &amp;quot;Hercule le copain de Pif&amp;quot;;
console.log( hercule.getName() ); // appelle getName de &amp;quot;Animal&amp;quot;
console.log( hercule.classification ); // =&amp;gt; chat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela fonctionne pour une simple et bonne raison. Le prototype de &amp;ldquo;Chat&amp;rdquo;
est un objet, auquel on a assigné une instance de &amp;ldquo;Animal&amp;rdquo;. Ainsi,
Chat.prototype.getName existe tout comme Chat.prototype.name&lt;/p&gt;
&lt;p&gt;Ensuite on a ajouté au prototype une nouvelle propriété, nommée
&amp;ldquo;classification&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Notez au passage, il fait bien construire le prorotype &lt;strong&gt;après&lt;/strong&gt; avoir
assigné le prototype parent. Sinon vous allez écraser ce que vous avez
assigné.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est typiquement une façon de faire de l&amp;rsquo;héritage. Même si au final,
c&amp;rsquo;est de la composition&amp;hellip;&lt;/p&gt;
&lt;p&gt;Par contre, vous avez remarqué que &amp;ldquo;new Chat()&amp;rdquo; n&amp;rsquo;a pas appelé le
constructeur parent. Encore une fois, comprennez bien que nous ne
faisons pas ici de classes, mais des closures !&lt;/p&gt;
&lt;p&gt;La méthode pour forcer l&amp;rsquo;appel au parent est la suivante, appeler une
fonction qui existe dans toutes les closures: &amp;ldquo;call()&amp;rdquo;. Cette méthode
prend en argument un objet et va appeler la méthode en prenant un
contexte donné. En clar, modifiez la closure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function Chat() {
    Animal.call(this); // on envoit le chat dans Animal
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela va appeler le constructeur &amp;ldquo;Animal&amp;rdquo; avec le contexte du &amp;ldquo;Chat&amp;rdquo;.
Pour aller plus loin, regardez cet exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function Animal(name) {
    this.name = name;
    console.log(&amp;quot;Nom assigné !&amp;quot;);
}

Animal.prototype.name = null;
Animal.prorotype.getName = function (){
    return this.name;
}

function Chat(name){
    Animal.call(this, name); // on appelle le constructeur
}

// on hérite... mais le souci c&#39;est que là on 
// ne donne pas &amp;quot;name&amp;quot; en paramètre...
// Dans tous les cas, Chat() sera appelé, donc on aura
// quand même un appel à Animal() avec le nom...
Chat.prototype = new Animal();
Chat.prototype.classification = &amp;quot;chat&amp;quot;;

var grosminet = new Chat(&amp;quot;Sylvestre&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Là on a bien appelé la construction parente&amp;hellip; 2 fois ??? et oui&amp;hellip; le
prorotype est initialisé avec &amp;ldquo;new Animal()&amp;rdquo; puis on appelle
&amp;ldquo;Animal.call(this, name)&amp;rdquo; depuis le constructeur de Chat&amp;hellip; Ralalala
c&amp;rsquo;est gênant !&lt;/p&gt;
&lt;p&gt;Il existe heureusement d&amp;rsquo;autres méthodes pour ça. Mais avant de lire la
suite, assurez vous d&amp;rsquo;avoir tout compris à ce que je raconte au dessus.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est bon ? on continue. Le but du jeu c&amp;rsquo;est d&amp;rsquo;initialiser le prorotype
de la closure &amp;ldquo;Chat&amp;rdquo; avec celui de &amp;ldquo;Animal&amp;rdquo;, c&amp;rsquo;est tout simple. Alors
pourquoi ne pas copier le prototype de &amp;ldquo;Animal&amp;rdquo; ?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function Chat(name) {
    console.log(&amp;quot;Constructeur de chat&amp;quot;);
    Animal.call(this, name);
}

Chat.prototype = Animal.proyotype;
// etc...

var c = new Chat(&amp;quot;Choupette&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là&amp;hellip; désespoir&amp;hellip; vous n&amp;rsquo;avez pas vu passer &amp;ldquo;Constructeur de chat&amp;rdquo;.
Car en réalité, vous avez assigné &lt;strong&gt;tout&lt;/strong&gt; le prototype de Animal à
Chat. Y compris le constructeur !&lt;/p&gt;
&lt;p&gt;La solution est de remettre le constructeur à sa place:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;//...
Chat.prototype = Animal.prototype;
Chat.prorotype.constructor = Chat;

//...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et là, ça marche !&lt;/p&gt;
&lt;p&gt;Bon on va récapituler:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une closure c&amp;rsquo;est une fonction qui a un prototype, on peut
l&amp;rsquo;assigner à une variable et par conséquent la retourner depuis une
fonction&lt;/li&gt;
&lt;li&gt;une classe en JS n&amp;rsquo;existe pas, c&amp;rsquo;est une closure (fonction) dont on
a initialisé un prototype&lt;/li&gt;
&lt;li&gt;on peut copier le prototype d&amp;rsquo;une closure dans une autre closure, du
coup on compose la closure avec une autre =&amp;gt; simili d&amp;rsquo;héritage de
classe&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Du coup c&amp;rsquo;est relativement simple de créer des factories. Voici un
exemple simplifié de ce que j&amp;rsquo;utilise dans mes helpers de
&lt;a href=&#34;https://github.com/metal3d/knotter&#34;&gt;Knotter&lt;/a&gt; :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function createClass(proto, parentClass) {

    var f = null;

    if (parentClass) {
        // le constructeur appellera le constructeur parent
        f = function (){
            parentClass.call(this);
        };

        // on copie le prototype parent
        f.prototype = parentClass.prototype;

        //on remet le constructeur enfant
        f.prototype.constructor = f;

    }
    else {
        // pas d&#39;héritage, donc une simple closure
        // suffit
        f = function (){};
    }

    //assigne le prototype sans écraser
    //le prototype parent (on garde le constructeur, etc...)
    for (var p in proto) {
        f.prototype[p] = proto[p];
    }

    return f;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce qui permet de faire joliement:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;var MaClass = createClass({
    name : &amp;quot;&amp;quot;,
    //fonction parente, retourne le nom tel quel
    getName : function (){
        return this.name;
    }
});

var ChildClass = createClass({
    // on fait pareil que le le parent
    // mais en lowercase
    getName : function () {
        return this.name.toLowerCase()
    }
}, MaClass);


var c = new ChildClass();
c.name = &amp;quot;Foo Bar&amp;quot;;

//affiche &amp;quot;foo bar&amp;quot;
console.log(c.getName());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est assez propre, joli, facile à utiliser&amp;hellip;&lt;/p&gt;
&lt;p&gt;Bref, j&amp;rsquo;espère voir avoir éclairé un peu plus sur ce qu&amp;rsquo;est une closure
JS, et sur le principe même des classes Javascript.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Le futur du web m&#39;inquiète</title>
      <link>https://www.metal3d.org/blog/2013/le-futur-du-web-minqui%C3%A8te/</link>
      <pubDate>Fri, 03 May 2013 06:17:28 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/le-futur-du-web-minqui%C3%A8te/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Il fut un temps pas si lointain où créer un site web entrait dans une
logique saine de construction de pages. Chaque contenu était bien défini
et les applications étaient un jeu de question-réponses entre le client
et le serveur. Je me souviens que le plus dur était de passer la
validation xhtml et de ne pas passer par des tableaux pour toute la mise
en page. HTML5 est arrivé et tout à coup, c&amp;rsquo;est comme si tout le monde
se foutait de l&amp;rsquo;accessibilité&amp;hellip;&lt;/p&gt;
&lt;p&gt;Avant que vous me pourrissiez en me disant que je suis anti &amp;ldquo;nouvelles
technologies&amp;rdquo;, sachez que j&amp;rsquo;utilise avec plaisirs les dernières
technologies du web, que ce soit les websockets, les canvas, les tags
video et audio, le CSS3, et le HTML5. Mais mon analyse qui suit est une
interrogation suite à quelques années de développement dans le Web. Je
ne cherche en aucun cas à faire boycotter les technologies dont je vais
parler. Je cherche simplement à donner matière à réflexion.&lt;/p&gt;
&lt;p&gt;Revenons quelques années en arrière, voir même seulement quelques mois.
Quand l&amp;rsquo;annonce de Adobe fait mouche auprès de la communauté des
développeurs de sites en spécifiant que leur technologie &amp;ldquo;Flash&amp;rdquo;
n&amp;rsquo;allait plus être supportée sur Android (alors que Apple ne l&amp;rsquo;a jamais
supporté), cela donnait du sourire à pas mal d&amp;rsquo;entre nous. Car si les
deux monstres du web mobile ne supportent pas Flash, alors il est clair
que cette technologie n&amp;rsquo;allait pas suivre longtemps sur les navigateurs
de bureau. Pourquoi ? tout simplement parce qu&amp;rsquo;aujourd&amp;rsquo;hui les mobiles
affichent le même site que celui que vous voyez sur votre ordinateur.
Cela pose donc un problème si vous utilisez Flash sur votre site. Car un
utilisateur visionnant votre site sur ton téléphone ou sa tablette
allait avoir un sacré problème.&lt;/p&gt;
&lt;p&gt;Flash, c&amp;rsquo;est la techno anti-accessibilité au possible. De vrais soucis
pour le référencment&amp;hellip; et j&amp;rsquo;en passe. Les derniers bastions de cette
technologie reste la possibilité d&amp;rsquo;utiliser aisément la webcam et le
micro. Seulement voilà&amp;hellip; même sur ce terrain les navigateur modernes
(donc tout sauf Internet Explorer) peuvent commencer à utiliser ce
matériel via
&amp;ldquo;&lt;a href=&#34;http://www.html5rocks.com/en/tutorials/getusermedia/intro/?redirect_from_locale=fr&#34;&gt;getUserMedia&lt;/a&gt;&amp;rdquo;.
Ce n&amp;rsquo;est donc pratiquement plus un problème.&lt;/p&gt;
&lt;p&gt;Il y a donc quelques mois encore, l&amp;rsquo;un des problèmes majeurs du corps de
métier du web était de faire un site viable, lisible, accessible.&lt;/p&gt;
&lt;p&gt;Mais HTML5 approchait. Petit à petit j&amp;rsquo;ai vu des dizaines de sites
utiliser cette technologie pour ne plus être un site, mais une
application Web. Il y a une grande différence entre un site de contenu
et une application web. L&amp;rsquo;un donne du contenu, l&amp;rsquo;autre vous fait
interagir avec du contenu qui apparaît et disparaît selon vos actions.&lt;/p&gt;
&lt;p&gt;La complexité de créer ce genre de site, le rendre accessible et surtout
qu&amp;rsquo;il puisse être correctement référencé par les moteurs de recherche
fait doubler voir tripler le temps de développement. Car si vous faites
les choses correctement, il faut pour chaque actions de l&amp;rsquo;utilisateur
qui donne du contenu via des requêtes HTTP javascript, permettre un
affichage statique&amp;hellip; imaginez un peu si vous n&amp;rsquo;y avez pas pensé dés le
départ&amp;hellip;&lt;/p&gt;
&lt;p&gt;Il existe une méthodologie que je vous conseille d&amp;rsquo;utiliser: le
&amp;ldquo;progressive enhancement&amp;rdquo; ou en français &amp;ldquo;&lt;a href=&#34;http://fr.wikipedia.org/wiki/Am%C3%A9lioration_progressive&#34;&gt;Amélioration
Progressive&lt;/a&gt;&amp;rdquo;.
Cette technique consiste à créer votre page en éliminant un maximum de
fioritures (Javascript, canvas&amp;hellip;) puis quand votre page est
correctement affichée, que les liens répondent de manière
traditionnelle, vous ajoutez la couche interactive: les liens peuvent
utiliser des requêtes XHR etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ce qui m&amp;rsquo;inquiète, c&amp;rsquo;est que de plus en plus de site web se fichent de
cela. Google Plus, que j&amp;rsquo;utilise à foison, ne fonctionne pas du tout
avec les navigateurs type &lt;a href=&#34;http://fr.wikipedia.org/wiki/Uzbl&#34;&gt;Uzbl&lt;/a&gt;. Et
je sais que beaucoup de non voyant n&amp;rsquo;on pas accès à ce genre de site de
part le fait que les navigateur Braille n&amp;rsquo;arrive pas à afficher les
contenus.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mettre le Web et ses services à la disposition de tous les individus,
quel que soit leur matériel ou logiciel, leur infrastructure réseau,
leur langue maternelle, leur culture, leur localisation géographique
ou leurs aptitudes physiques ou mentales.&lt;/p&gt;
&lt;p&gt;&amp;ndash;Tim Berners-Lee, Inventeur de l&amp;rsquo;internet&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Une chose est vraie, je pense aussi que nous ne devons pas limiter la
technologie pour qu&amp;rsquo;elle soit accessible, mais plutôt faire évoluer
l&amp;rsquo;accessibilité (ses outils et ses intégrations) pour que tout le monde
puisse en profiter.&lt;/p&gt;
&lt;p&gt;Alors quel avenir pour le Web ? Allons nous perdre ces règles que nous
chérissions il y a peu de temps ? N&amp;rsquo;allons nous pas trop loin avec nos
navigateurs ? Nous sommes en train de passer sur du &amp;ldquo;tout web&amp;rdquo;,
pratiquement toutes les applications que nous utilisions en &amp;ldquo;client
lourd&amp;rdquo; deviennent des application web&amp;hellip; nos mails, édition de document,
visio conférence, édition de vidéo&amp;hellip; notre navigateur internet devient
notre bureau. C&amp;rsquo;était d&amp;rsquo;ailleurs l&amp;rsquo;un des paris de Google avec
&lt;a href=&#34;http://fr.wikipedia.org/wiki/Google_Chrome_OS&#34;&gt;ChomeOS&lt;/a&gt;. Un webstore
propose des jeux, des outils, des applications bureautique qui tournent
toutes dans le navigateur.&lt;/p&gt;
&lt;p&gt;Le WebGL apporte en plus la partie accélération 3D, ce qui fait
qu&amp;rsquo;aujourd&amp;rsquo;hui le navigateur est capable de faire absolument tout ce
qu&amp;rsquo;un gestionnaire de fenêtre peut faire. Et même si je suis franchement
impressionné par la puissance de Chrome, de Firefox ou de Opera, je n&amp;rsquo;en
reste pas moins inquiet pour nos technologies de bureau&amp;hellip; et je vous
avoues que j&amp;rsquo;ai peur de voir disparaître beaucoup d&amp;rsquo;applications client
lourd et les voir se transformer en applications Web, ouvrant ainsi la
porte à des failles de sécurité et des inaccessibilités grandissante. Il
suffira que le site qui propose l&amp;rsquo;application soit indisponible, et vous
voilà dans le brouillard.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Sortie de Knotter</title>
      <link>https://www.metal3d.org/blog/2013/sortie-de-knotter/</link>
      <pubDate>Mon, 29 Apr 2013 23:46:11 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/sortie-de-knotter/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Et bien on y est. Je me suis lancé un défis il y a quelques semaines et
il est réalisé en quelques temps. Knotter, mon microframework NodeJS
passe en version 0.1.1. Et comme je pense que personne ne le connais, je
vais vous le présenter avec des exemples à l&amp;rsquo;appui. Knotter, c&amp;rsquo;est
simple un framework NodeJS basé sur un principe prototypé simple, rapide
et malléable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.metal3d.org//statics/knotter-logo.png%0A%20:class:%20four%20columns&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://github.com/metal3d/knotter&#34;&gt;Knotter&lt;/a&gt; est né lors d&amp;rsquo;une étude
comparative que je devais faire entre une solution Python WSGI (webapp2)
et NodeJS. Comme je devais réaliser à peu de chose près la même
structure de code, j&amp;rsquo;ai créé un tout petit middleware en quelques lignes
pour simuler le même fonctionnement. J&amp;rsquo;aurais put utiliser
&lt;a href=&#34;http://www.senchalabs.org/connect/&#34;&gt;Connect&lt;/a&gt; mais il ne collait pas
exactement à mon attention. En quelques minutes j&amp;rsquo;avais la base, et j&amp;rsquo;ai
réalisé mon étude. Et puis&amp;hellip; l&amp;rsquo;idée à pris son chemin&amp;hellip;&lt;/p&gt;
&lt;p&gt;Les jours passaient, et dans ma tête se peaufinais l&amp;rsquo;idée. Oui j&amp;rsquo;adore
Connect. Mais j&amp;rsquo;aime le pattern composite des closures pour créer des
pseudo classes. Le mode Objet de Javascript n&amp;rsquo;est pas réputé, mais il
faut dire que la plupart des développeurs oubli que Javascript n&amp;rsquo;a pas
de classes, ni même de fonctions&amp;hellip; Il a des &amp;ldquo;closures&amp;rdquo;. Du moment où
vous acceptez cette idée, que vous la comprenez, alors le langage
devient limpide.&lt;/p&gt;
&lt;p&gt;Je ferai un billet de blog à ce sujet, passons au coeur de l&amp;rsquo;idée.&lt;/p&gt;
&lt;p&gt;Mon blog tombait, je ne le maintenait plus et après plusieurs années de
bons et loyaux services, Copix 3 sous PHP 5.2 n&amp;rsquo;y arrivait plus. Depuis
des mois je n&amp;rsquo;ai pas touché à PHP, je code en Python et JS à longueur de
journée&amp;hellip; et voilà alors le projet qui nait. Je me décide, je fais mon
framework sur mesure pour gérer mon blog.&lt;/p&gt;
&lt;p&gt;Plus de MySQL, on passe à MongoDB et je me lance dans le code.&lt;/p&gt;
&lt;p&gt;Knotter commence à naitre, au départ avec des erreurs monumentales de
conception: les &amp;ldquo;handlers&amp;rdquo; sont des objets non prototypés, les cookies
sont mal gérés, et surtout pas moyen de faire passer des sessions d&amp;rsquo;un
worker à l&amp;rsquo;autre. Mais outre cela, le modèle était pas mauvais, il
fallait juste une refonte, repenser le core.&lt;/p&gt;
&lt;p&gt;La version 0.1.1 devait faire sauter le bouchon, et voici comment vous
pouvez créer un petit projet avec Knotter, Petit car, mis à part mon
site, je ne sais pas à quel point cela est viable. Mais mes benchmarks
sont assez encourageant.&lt;/p&gt;
&lt;h1 id=&#34;installation&#34;&gt;Installation&lt;/h1&gt;
&lt;p&gt;On va commencer par créer notre dossier de travail. Nous allons le créer
dans &amp;ldquo;~/Dev/knotter-example&amp;rdquo;. Au final, vous aurez cette structure de
répertoire:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~/Dev/knotter-example/
                     |_ index.js
                     |_ templates/
                     |      |_example.html
                     |
                     |_ handlers/
                     |      |_page.js
                     |
                     |_ statics/
                               |_ css/
                               |     |_base.css
                               |_ images/
                                     |_test.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas compliqué, tapez dans un terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p ~/Dev/knotter-example/{handlers,statics,statics/css,static/images,templates}
cd ~/Dev/knotter-example
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Installez knotter via &amp;ldquo;npm&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install knotter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inutile de l&amp;rsquo;installer globalement, knotter s&amp;rsquo;installe localement. Du
coup, vous allez avoir un répertoire &amp;ldquo;node_modules&amp;rdquo; qui va apparaitre,
avec à l&amp;rsquo;intérieur une installation toute prête de knotter.&lt;/p&gt;
&lt;p&gt;Puisqu&amp;rsquo;on va faire les choses bien, on va utiliser un moteur de
template. Knotter est agnostique au moteur de templates car il utilise
le génial module &amp;ldquo;consolidate&amp;rdquo;. Mon moteur de templates préféré étant
&amp;ldquo;&lt;a href=&#34;http://paularmstrong.github.io/swig/&#34;&gt;swig&lt;/a&gt;&amp;rdquo;, on passe par la
commande:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install swig
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swig a l&amp;rsquo;avantage de ressembler fortement à Twig ou Jinja et me permet
de ne pas me mélanger les pinceaux. Bref, passons au code.&lt;/p&gt;
&lt;h1 id=&#34;premier-handler&#34;&gt;Premier handler&lt;/h1&gt;
&lt;p&gt;Un handler est une closure prototypée (oui, une classe allez&amp;hellip;) qui
hérite (se compose en réalité) de knotter.Handler. Afin d&amp;rsquo;assurer
l&amp;rsquo;héritage, il faut respecter une syntaxe simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;// fichier ~/Dev/knotter-example/handlers/page.js

var knotter = require(&#39;knotter&#39;),
    util    = require(&#39;util&#39;);

//on crée notre classe PageHandler, le nom est arbitraire
PageHandler(){
    // on appelle le constructeur parent
    knotter.Handler.call(this);
}
//on fait hériter la classe
util.inherits(PageHandler, knotter.Handler)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et c&amp;rsquo;est tout&amp;hellip; enfin presque. Là nous avons un handler, mais il doit
répondre à une url et surtout à une méthode HTTP précise, continuez:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;// l&#39;expression régulière pour répondre
// la route *doit* faire parti du prototype
PageHandler.prototype.route = &amp;quot;^/page/example&amp;quot;;

// le handler &amp;quot;get&amp;quot; 
PageHandler.prototype.get = function () {
    this.end(&amp;quot;Coucou&amp;quot;)
};

// on exporte le handler
module.exports = PageHandler;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà pour le handler, on passe au serveur&lt;/p&gt;
&lt;h1 id=&#34;le-serveur&#34;&gt;Le serveur&lt;/h1&gt;
&lt;p&gt;Comme la plupart des nouvelles technologies de service Web, c&amp;rsquo;est
l&amp;rsquo;outil qui sert les réponses depuis sont propre mécanisme de serveur.
Ainsi, votre application Web répond elle-même aux clients. On aime
passer par un proxy frontal type nginx pour simplifier une partie du
travail, mais dans l&amp;rsquo;ensemble le serveur HTTP intégré de Node s&amp;rsquo;en sort
bien.&lt;/p&gt;
&lt;p&gt;Mais knotter fourni une classe qui va permettre de définir la
configuration, savoir quels handlers utiliser, etc&amp;hellip;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;// fichier ~/Dev/knotter-example/index.js

var knotter = require(&#39;knotter&#39;),
    PageHandler = require(&#39;./handlers/page.js&#39;);

var server = new knotter.Server({
    handlers : [ PageHandler ]
})
// on lance le serveur
server.start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, rien de bien compliqué ici. Pour le moment notez simplement que
nous n&amp;rsquo;avons qu&amp;rsquo;un handler dans la liste (n&amp;rsquo;oubliez pas que &amp;ldquo;handlers&amp;rdquo;
est un tableau). Il suffit alors de lancer:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et de se rendre sur &lt;a href=&#34;http://127.0.0.1:8000/page/example&#34;&gt;http://127.0.0.1:8000/page/example&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Un fabuleux &amp;ldquo;Coucou&amp;rdquo; apparait, c&amp;rsquo;est gagné :)&lt;/p&gt;
&lt;h1 id=&#34;gérer-les-arguments-et-utiliser-un-moteur-de-template&#34;&gt;Gérer les arguments et utiliser un moteur de template&lt;/h1&gt;
&lt;p&gt;Nous avons installé un moteur de template, alors utilison le ! Mais pour
rendre l&amp;rsquo;exemple plus sexy, on va utiliser les arguments passés à la
route.&lt;/p&gt;
&lt;p&gt;Reprenons handler/page.js et modifions la route et le handler &amp;ldquo;get&amp;rdquo;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;// la regexp qui prend deux arguments
// le &amp;quot;?&amp;quot; permet de ne pas capturer de &amp;quot;/&amp;quot; dans la première
// capture.
PageHandler.prototype.route = &amp;quot;^/page/example/(.*?)/(.*)&amp;quot;;

// on va utiliser un template
PageHandler.prototype.get = function () {

    var firstname = this.params.args[1];
    var lastname = this.params.args[2];


    // on appelle le template example.html à la racine de &amp;quot;templates&amp;quot;
    // La racine sera déclaré dans la configuration serveur, lisez la suite...
    // Le second argument est le contexte envoyé au template, les 
    // propriétés seront des variables de template
    this.render(&#39;/example.html&#39;, {
        lastname: lastname,
        firstname : firstname
    });

};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans le fichier &amp;ldquo;templates/example.html&amp;rdquo; on va juste faire simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;Bonjour {{firstname}} {{lastname}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On passe à &amp;ldquo;index.js&amp;rdquo;, on y configure l&amp;rsquo;utilisation de swig&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;var server = new knotter.Server({
    handlers : [ PageHandler ],
    engine   : &amp;quot;swig&amp;quot;,
    templates : __dirname + &amp;quot;/templates&amp;quot;,
    engineOptions: {
        root: __dirname +&amp;quot;/templates&amp;quot;,
        allowErrors : true,
        autoescape : false,
        cache : false
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il faut malheureusement bel et bien répéter les deux options de
&amp;ldquo;templates&amp;rdquo; et &amp;ldquo;root&amp;rdquo;. Je n&amp;rsquo;ai pas encore trouvé de palliatif&amp;hellip;&lt;/p&gt;
&lt;p&gt;Bref, coupez le serveur si ce n&amp;rsquo;est pas déjà fait, et relancez le. Puis
allez vois les pages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://127.0.0.1:8000/page/example/Pierre/Dupont&#34;&gt;http://127.0.0.1:8000/page/example/Pierre/Dupont&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://127.0.0.1:8000/page/example/Jane/Doe&#34;&gt;http://127.0.0.1:8000/page/example/Jane/Doe&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les arguments passés à l&amp;rsquo;url apparaissent bien, c&amp;rsquo;est encore gagné !&lt;/p&gt;
&lt;h1 id=&#34;sessions&#34;&gt;Sessions&lt;/h1&gt;
&lt;p&gt;Les sessions fonctionnent correctement depuis la version 0.0.5 mais la
version 0.1.1 permet désormais de les partager entre clusters (voir
section suivante).&lt;/p&gt;
&lt;p&gt;Pour utiliser des variables de sessions, il suffit, dans un handler,
d&amp;rsquo;utiliser l&amp;rsquo;objet &amp;ldquo;this.sessions&amp;rdquo;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;this.sessions.get(&#39;key&#39;); // retourne la valeur de session utilisateur pour la clef &amp;quot;key&amp;quot;
this.sessions.set(&#39;key&#39;, &#39;une valeur&#39;); // assigne la valeur à une clef
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Les valeurs sont sérialisée via JSON, ce qui induit que vous ne pouvez
pas garder une classe complexe en session, mais un objet simple sera
correctement récupéré.&lt;/p&gt;
&lt;p&gt;Les sessions restent en mémoire, elles ont un TTL de 15 minutes
d&amp;rsquo;inactivité. Je vais régler d&amp;rsquo;ici la version 0.1.2 le fait de pouvoir
changer ce TTL.&lt;/p&gt;
&lt;h1 id=&#34;cluster-et-sessions&#34;&gt;Cluster, et sessions&lt;/h1&gt;
&lt;p&gt;L&amp;rsquo;un des mes soucis majeurs était de réussir à faire tourner plusieurs
&amp;ldquo;workers&amp;rdquo; en parallèle tout en pouvant gérer des variables de sessions.
Car vous le savez certainement, Node est &amp;ldquo;monoprocess&amp;rdquo;. Cela signifie
que pour pouvoir répondre en même temps à plusieurs clients, il faut
jongler avec le module &amp;ldquo;cluster&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Mais le passage de valeurs a été une bataille sans nom&amp;hellip; Knotter fourni
une classe pour simplifier le travail. Voici comment vous pouvez
procéder.&lt;/p&gt;
&lt;p&gt;Coupez les serveur (CTRL+C) et modifiez les fichier index.js:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;var cluster = require(&#39;cluster&#39;),
    numCPUs = require(&#39;os&#39;).cpus().length,
    knotter = require(&#39;knotter&#39;);

if (cluster.isMaster) {
    var n = cluster.fork();
    knotter.Cluster.addWorker(n);

} else {
    // dans le worker, on lance le serveur

    var server = new knotter.Server({
        handlers : [ PageHandler ],
        engine   : &amp;quot;swig&amp;quot;,
        templates : __dirname + &amp;quot;/templates&amp;quot;,
        engineOptions: {
            root: __dirname +&amp;quot;/templates&amp;quot;,
            allowErrors : true,
            autoescape : false,
            cache : false
        }
    });

    server.start();

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le sous-module &amp;ldquo;Cluster&amp;rdquo; de Knotter permet d&amp;rsquo;envoyer les sessions lors
des modifications de valeurs. Tout est fait automatiquement.&lt;/p&gt;
&lt;p&gt;A terme, le système de sessions sera paramétrable pour pouvoir utiliser
un memcache ou redis&amp;hellip; pour le moment c&amp;rsquo;est amplement suffisant pour
mon cas personnel.&lt;/p&gt;
&lt;h1 id=&#34;fichiers-statiques&#34;&gt;Fichiers statiques&lt;/h1&gt;
&lt;p&gt;Vous allez avoir besoin de servir des fichiers statiques, ne serait-ce
que des images ou des feuilles de styles. Ici nous utilisons le
répertoire &amp;ldquo;statics&amp;rdquo;. Pour décorèler un peu et que vous comprenniez bien
comment cela fonctionne, je fais en sorte de créer deux routes
spécifiques pour les images et le css. Voici la configuration à donner
au serveur:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;var server = new knotter.Server({
    handlers : [ PageHandler ],
    engine   : &amp;quot;swig&amp;quot;,
    templates : __dirname + &amp;quot;/templates&amp;quot;,
    engineOptions: {
        root: __dirname +&amp;quot;/templates&amp;quot;,
        allowErrors : true,
        autoescape : false,
        cache : false
    },
    // deux points pour les fichiers statiques
    statics: {
        &#39;styles&#39;: __dirname + &#39;/statics/css/&#39;,
        &#39;img&#39; : __diranme + &#39;/statics/images/&#39;
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ainsi:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://127.0.0.1:8000/styles/base.css&#34;&gt;http://127.0.0.1:8000/styles/base.css&lt;/a&gt; sert statics/css/base.css&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://127.0.0.1:8000/img/test.png&#34;&gt;http://127.0.0.1:8000/img/test.png&lt;/a&gt; sert statics/images/test.png&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;concrètement&#34;&gt;Concrètement&lt;/h1&gt;
&lt;p&gt;Concrètement mon blog tourne sur Knotter. J&amp;rsquo;ai créé deux ou trois
modules pour gérer les tickets, le flux RSS ou les sitemaps. Cela ne m&amp;rsquo;a
pas pris longtemps, mais je maitrise mon outil&amp;hellip; Je vais vraiment
devoir documenter Knotter sérieusement si vous êtes intéressés par mon
projet. Dans tous les cas, celui-ci ne mourra pas. Le simple fait de
l&amp;rsquo;utiliser ici pour mon blog va me pousser à le maintenir. Mais je vous
avoues qu&amp;rsquo;un peu d&amp;rsquo;aide ou ne serait-ce que quelques personnes me
faisant un retour serait un bon carburant.&lt;/p&gt;
&lt;h1 id=&#34;pour-aller-plus-loin&#34;&gt;Pour aller plus loin&lt;/h1&gt;
&lt;p&gt;Lisez la documentation de swig, notamment en ce qui concerne &amp;ldquo;extends&amp;rdquo;
dans l&amp;rsquo;API. Cela va vous permettre de faire des templates &amp;ldquo;globaux&amp;rdquo; dans
lesquels vous injecterez des blocks.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;utilisation d&amp;rsquo;une base de données sera vraiment un plus, MongoDB est
en quelques sorte la panacée puisque la syntaxe est aussi en
Javascript&amp;hellip; mais n&amp;rsquo;importe quelle base de données fera l&amp;rsquo;affaire.&lt;/p&gt;
&lt;p&gt;Je pense avoir fait un tour assez clair&amp;hellip; à moins que vous ayez des
questions ?&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Reprise du blog avec NodeJS</title>
      <link>https://www.metal3d.org/blog/2013/reprise-du-blog-avec-nodejs/</link>
      <pubDate>Sun, 21 Apr 2013 16:26:28 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/reprise-du-blog-avec-nodejs/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Vous avez remarqué ? nouveau blog, nouveau design, et surtout nouveau
langage: NodeJS (javascript) avec une base mongo. Le tout avec mon
middleware nommé Knotter. Je me permet donc de vous présenter le
renouveau et certainement un peu plus de contenu puisque de désormais je
pense qu&amp;rsquo;il va bien mieux marcher. Laissez moi vous expliquer&amp;hellip;&lt;/p&gt;
&lt;p&gt;Cela faisait des mois, que je ne touchais plus mon blog. Il faut dire
que j&amp;rsquo;ai pas eut le temps, d&amp;rsquo;une part, mais aussi que ce vieux coucou
tourne depuis presque 6 ans sur un Copix 3 (vous vous souvenez ?) en
PHP. J&amp;rsquo;avais beau avoir fait le maximum à l&amp;rsquo;époque, les méchants
spammeurs ont pris le dessus et m&amp;rsquo;ont pollué mon site de commentaires
bien dégueulasses&amp;hellip; En plus de ça, le conteneur qui était là pour tenir
le site en éveil s&amp;rsquo;en prend plein la poire (d&amp;rsquo;ailleurs ça j&amp;rsquo;ai pas
encore tout réglé). Mais depuis quelques jours, je me suis relancé sur
le projet. D&amp;rsquo;abord parce que je développe depuis quelques temps un
middleware (un genre de microframework) nommé
&lt;a href=&#34;https://github.com/metal3d/knotter&#34;&gt;Knotter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;. But de la manœuvre: avoir un truc qui fasse un peu comme le WSGI via
Python, mais en Javascript.&lt;/p&gt;
&lt;p&gt;Le projet lancé, je me suis attelé à reprendre mon blog&amp;hellip; pas mal
d&amp;rsquo;étapes ont dut être effectuées pour remettre au propre le site.&lt;/p&gt;
&lt;p&gt;En premier lieu, virer le spam ! Tenez vous bien: 97% des commentaires
sur le site était du spam. Dingue non ? Alors j&amp;rsquo;ai commencé par:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;passer un export de la base MySQL dans MongoDB (avec un script
python qui sérialisait mes tickets + commentaires en un document
JSON)&lt;/li&gt;
&lt;li&gt;faire un module de calcul &amp;ldquo;Bayesien&amp;rdquo;, un coup d&amp;rsquo;apprentissage et il
a pas mal classé le bordel&lt;/li&gt;
&lt;li&gt;nettoyer un peu mes billets qui étaient écrit en &amp;ldquo;wiki&amp;rdquo; =&amp;gt; or
voilà, node n&amp;rsquo;a rien pour ça, je me suis donc tapé un module à la
main qui fait tout ça&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ensuite, j&amp;rsquo;ai recherché les modules qui pouvaient me rendre service:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;feed&amp;rdquo; pour créer un fil RSS propre&lt;/li&gt;
&lt;li&gt;&amp;ldquo;swig&amp;rdquo; pour le moteur de template (vraiment génial, proche de Twig
pour PHP ou Jinja2 pour Python)&lt;/li&gt;
&lt;li&gt;&amp;ldquo;emailjs&amp;rdquo; pour ajouter (enfin !) un système de notification email en
cas de réponse à un commentaire&lt;/li&gt;
&lt;li&gt;&amp;ldquo;sitemap&amp;rdquo; pour créer un bon sitemap rapidement&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;et j&amp;rsquo;en passe&amp;hellip;&lt;/p&gt;
&lt;p&gt;Je me suis dit aussi: &amp;ldquo;rendons le site responsive&amp;rdquo;, et j&amp;rsquo;ai beau aimer
le bon vieux Twitter Bootstrap, j&amp;rsquo;ai préféré épurer mon travail en
utilisant &amp;ldquo;Skeleton&amp;rdquo;, un outil généralissime qui est &amp;ldquo;style agnostique&amp;rdquo;,
ce qui veut dire qu&amp;rsquo;il ne me donne que la partie intéressante: les
layouts.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai calculé, pour créer le site, à savoir:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;3h pour les modules (ticket, page statique, rss, sitemap, connexion
à la base mongo&amp;hellip;)&lt;/li&gt;
&lt;li&gt;1h pour le montage HTML et styles (oui je sais ça se voit&amp;hellip;)&lt;/li&gt;
&lt;li&gt;2h pour le classement des spams&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reste que je vois encore des spammeurs me faire chier&amp;hellip; je vais
vérifier si mon filtre auto fonctionne. Et au fur et à mesure je vais
améliorer tout ça. J&amp;rsquo;espère que cette fois-ci le site arrêtera de se
planter misérablement tous les jours (parfois pendant plusieurs heures)
et que je pourrais vous délivrer rapidement tous mes modules.&lt;/p&gt;
&lt;p&gt;A savoir que désormais, le site tourne entièrement en Javascript (ou
simili):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nodejs coté serveur&lt;/li&gt;
&lt;li&gt;mongodb pour les datas&lt;/li&gt;
&lt;li&gt;un peu de js brut pour le site&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Du coup, un seul langage pour un site, je vous assure que ça le fait.
Niveau perf je suis pas mal non plus&amp;hellip; Voilà pour l&amp;rsquo;info.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Vendre de la musique libre</title>
      <link>https://www.metal3d.org/blog/2013/vendre-de-la-musique-libre/</link>
      <pubDate>Sat, 12 Jan 2013 14:38:42 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2013/vendre-de-la-musique-libre/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Voilà des années que je propose ma musique sous une licence libre. En
l’occurrence, sous Creative Commons. Vous pouvez la télécharger, la
distribuer, l&amp;rsquo;utiliser&amp;hellip; aucun souci. La liberté d&amp;rsquo;écouter et partager
est et reste au centre de ma philosophie. Alors quand je vous dis que
mon album est en vente sur Google Play, vous pouvez avoir un sentiment
étrange.&lt;/p&gt;
&lt;p&gt;Effectivement, l&amp;rsquo;album &amp;ldquo;Ayen Bowen No&amp;rdquo; est en vente sur la page &lt;a href=&#34;http://play.google.com/store/music/album/Arkyne_Ayen_Bowen_No?id=Bxwwy7qkccu4excuradrzl42uoy&#34;&gt;Arkyne
sur Google Play pour
0.99€&lt;/a&gt;
, le prix n&amp;rsquo;est pas élevé, mais en même ce n&amp;rsquo;est ni la gloire ni
l&amp;rsquo;enrichissement que je cherche. Non&amp;hellip; vraiment, vous pouvez me croire.
La musique, pour moi, est une forme d&amp;rsquo;échange, un message, un exutoire
dont j&amp;rsquo;ai besoin. Partager mes compositions avec les gens qui veulent
m&amp;rsquo;entendre n&amp;rsquo;est pas une envie de reconnaissance. C&amp;rsquo;est une chance pour
moi, simplement. Car vous me faites des retours, des critiques, et je
les entends.&lt;/p&gt;
&lt;p&gt;Mais le problème dans ce principe d&amp;rsquo;échange, c&amp;rsquo;est que l&amp;rsquo;on veut en
avoir encore plus. Et pour en vouloir plus, il faut proposer plus. Et
dans notre monde, même la création coûte cher; car voilà, proposer un
album, même libre, même si je le produit moi même depuis mon petit
bureau, j&amp;rsquo;ai besoin de matériel, de payer un serveur, d&amp;rsquo;avoir une carte
son digne de ce nom, de nouvelles cordes de violon, de réparer quelques
instruments.&lt;/p&gt;
&lt;p&gt;Et qui plus est, certains m&amp;rsquo;ont demandé une version vinyle, et en ce qui
concerne le son, j&amp;rsquo;aimerai qu&amp;rsquo;un ingénieur me fasse un mastering en
studio&amp;hellip; Bref, tout cela n&amp;rsquo;est pas gratuit.&lt;/p&gt;
&lt;p&gt;Oui, j&amp;rsquo;aimerai avoir un peu plus de fans, non pas par reconnaissance (je
l&amp;rsquo;ai déjà dit) mais pour proposer plus rapidement et de meilleur qualité
de nouveaux morceaux.&lt;/p&gt;
&lt;p&gt;Voilà pourquoi je propose une version payante de l&amp;rsquo;album. Tout comme sur
Jamendo (qui va pas aimer que je propose mon album ailleurs, mais en
même temps&amp;hellip; ils m&amp;rsquo;ont pas beaucoup aidé jusque là), les fonds que je
pourrais engendrer seront gage de nouvelle création.&lt;/p&gt;
&lt;p&gt;Vivre de ma musique est hors de propos. Je n&amp;rsquo;ai ni la capacité
financière, ni le talent de certain pour avoir assez de fans pour vivre
de mes créations. Mais si je peux toucher ne serait-ce que quelques
personnes, que ces personnes en veulent davantage, et que je peux enfin
leur proposer d&amp;rsquo;autres albums sans passer 3 ans à corriger avec peu de
matériel&amp;hellip; alors je serai comblé.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est pourquoi il faut voir cette version comme une version de &amp;ldquo;don&amp;rdquo;. Si
vous voulez m&amp;rsquo;aider à promouvoir un peu ce que je fais, n&amp;rsquo;hésitez pas à
partager le lien de l&amp;rsquo;album, en expliquant bien que la musique proposée
est libre, et que tout le monde peut l&amp;rsquo;écouter et la récupérer
gratuitement ailleurs. Mais que la version payante est une aide
précieuse pour continuer à faire vivre le projet.&lt;/p&gt;
&lt;p&gt;Merci à vous.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Google vs la presse qui veut taxer</title>
      <link>https://www.metal3d.org/blog/2012/google-vs-la-presse-qui-veut-taxer/</link>
      <pubDate>Wed, 24 Oct 2012 11:58:43 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2012/google-vs-la-presse-qui-veut-taxer/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;La menace de voir Google bannir les médias français se fait sentir, même
si ce ne sont que des paroles visant à appeurer le monde internet. Que
Google le fasse ou non, le problème est bien levé. Les éditeurs de
presses demandent à ce que la &amp;ldquo;Lex Google&amp;rdquo; (loi Google en allemand) soit
appliquée aussi en France. Grosso-modo, on demande à Google de reverser
des &amp;ldquo;taxes&amp;rdquo; aux éditeurs de presse français car il gagne de l&amp;rsquo;argent sur
la pub en affichant du contenu des sites de presse. Dans le fond, c&amp;rsquo;est
pas idiot, dans la forme il y a un souci&amp;hellip;&lt;/p&gt;
&lt;p&gt;Soyons pragmatique, à quoi sert Google (en tant que moteur de recherche)
? Il référence les sites qui lui permettent de le faire sur sa base de
données, permettant aux internautes qui utilisent ce moteur de recherche
à trouver des sites en relation avec leur recherche. Comme l&amp;rsquo;une des
grandes sources d&amp;rsquo;information se base sur la presse, il est évident
qu&amp;rsquo;une recherche sur un évènement d&amp;rsquo;actualité vous retourne un résultat
qui se situe sur un site de presse (le monde, le point, figaro&amp;hellip;)&lt;/p&gt;
&lt;p&gt;Bref, en soit, Google est un service de référencement. Dans la forme,
Google génère son contenu à partir du contenu des sites qu&amp;rsquo;il référence.
Mais il reste un service qui permet aux sites référencés de voir leur
trafic augmenter. Car si Google ne retourne pas de résultat pointant sur
votre site, vous pouvez être sûr que vous n&amp;rsquo;allez pas avoir un trafic
phénoménal.&lt;/p&gt;
&lt;p&gt;Google fait apparaitre de la publicité sur sa page de résultat. Car oui,
Google est une entreprise&amp;hellip; et par ailleurs, les sites de presse eux
même ont en général des espaces de publicité qui leur rapport un peu de
sous.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;hopital se fout de la charité, et voilà que &amp;ldquo;comme la bête est grasse&amp;rdquo;
on va pas se gêner. En plus de fournir des visites aux site de presse,
Google devrait reverser un pourcentage de ses révenus publicitaires aux
dits sites de presse.&lt;/p&gt;
&lt;p&gt;Bha voyons&amp;hellip;&lt;/p&gt;
&lt;p&gt;Donc, réaction de Google, faire une menace impossible à tenir: on va
donc bannir les sites de presse du moteur de recherche. Google ne peut
évidamment pas se le permettre, car effectivement les sites de presses
fournissent un contenu à Google. Mais en contre parti, la visibilité du
site de presse est largement accrus.&lt;/p&gt;
&lt;p&gt;Alors deux questions me tarodent. D&amp;rsquo;abord: est-ce que **tous les
moteurs de recherches** vont être visés ? et secondo, si les éditeurs
de sites de presse sont pas content que Google gagne de l&amp;rsquo;argent en
référençant leur site, je leur rappelle qu&amp;rsquo;il peuvent se faire
déréférencer hein, un vieux fichier &amp;ldquo;robots.txt&amp;rdquo; avec un Disallow *&amp;quot; et
on en parle plus. Alors la seconde question: si les éditeurs se croient
si indispensables à Google et qu&amp;rsquo;ils sont pas content que Google gange
des pépètes, pourquoi laissent-ils leur site se faire référencer ?&lt;/p&gt;
&lt;p&gt;Oui&amp;hellip; je connais les réponses à ces questions, mais j&amp;rsquo;ai l&amp;rsquo;impression
que les acteurs de ce mouvement n&amp;rsquo;ont pas conscience qu&amp;rsquo;on est pas
dupes&amp;hellip;&lt;/p&gt;
&lt;p&gt;Alors messieurs les éditeurs de presse qui demande encore de la tune,
vous pouvez nous donner honnêtement une réponse claire ?&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>JQuery toujours pas pour moi</title>
      <link>https://www.metal3d.org/blog/2012/jquery-toujours-pas-pour-moi/</link>
      <pubDate>Wed, 05 Sep 2012 20:15:22 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2012/jquery-toujours-pas-pour-moi/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Suite à un vieux post qui s&amp;rsquo;est terminé par une horde de pas mal de fans
de jQuery insultants, j&amp;rsquo;ai évité de continuer à commenter et je décide
de refaire un article. Deux ans se sont écoulés, et ma vision de jQuery
se déteriore de plus en plus&amp;hellip; J&amp;rsquo;ai admis certaines critiques, mes
exemples n&amp;rsquo;étaient pas forcément les plus adéquats et mon entrain à
défendre un point de vue n&amp;rsquo;a pas été suivi par une explication claire.
Du coup, voici un point de vue plus &amp;ldquo;technique&amp;rdquo; et plus poussé sur le
&amp;ldquo;pourquoi je n&amp;rsquo;aime pas du tout jQuery&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Pour appuyer mes remarques et mon avis, je vais me baser sur des
discussions que j&amp;rsquo;ai eut depuis 2 ans. Que ce soit après ma conférence
donnée sur JS ou avec des collègues, j&amp;rsquo;ai noté les remarques, et je vous
redonne texto ce que j&amp;rsquo;ai répondu ou ce que j&amp;rsquo;ai tenté d&amp;rsquo;expliquer.&lt;/p&gt;
&lt;p&gt;En plus de ces remarques, je vais vous montrer aussi les points
principaux qui me font vraiment une poussée allergique à ce &amp;ldquo;framework&amp;rdquo;
Javascript.&lt;/p&gt;
&lt;h3 id=&#34;sémantique&#34;&gt;Sémantique&lt;/h3&gt;
&lt;p&gt;Commençons par ce qui m&amp;rsquo;orifie le plus, à savoir cette sémantique qui me
fait palir.&lt;/p&gt;
&lt;p&gt;Prenons un exemple tout bête: &lt;code&gt;$(&#39;#element&#39;).load(handler)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Non mais vous plaisantez ? quand je lis ça je me dis que &amp;ldquo;load&amp;rdquo; ça
charge un truc. Bha non ! là c&amp;rsquo;est l&amp;rsquo;évènement &amp;ldquo;onload&amp;rdquo; qui appelle le
handler. Une sémantique pareille me choque vraiment, surtout que:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;//pris de la doc... $(&#39;#result&#39;).load(&#39;ajax/test.html&#39;);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Ce coup-ci load charge bien un truc&amp;hellip; la page &amp;ldquo;ajax/test.html&amp;rdquo; est
récupéré via un XHR (XHttpRequest, et pas &amp;ldquo;Ajax&amp;rdquo;, on va y venir&amp;hellip;) et
le contenu est inséré dans l&amp;rsquo;élement&amp;hellip;&lt;/p&gt;
&lt;p&gt;Vous pouvez me dire ce que vous voulez, utiliser un nom de méthode pour
deux opérations qui n&amp;rsquo;ont rien à voir, c&amp;rsquo;est absurde et pas propre. Vous
en voulez un autre, bougez pas.&lt;/p&gt;
&lt;p&gt;La méthode &amp;ldquo;click&amp;rdquo; par exemple. En premier lieu, moi quand je lis ce
nom, je me dis &amp;ldquo;tiens c&amp;rsquo;est cool, une fonction qui simule un click&amp;rdquo;. Et
j&amp;rsquo;ai pas tort en fait. Car si je fais:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$(&#39;#toto&#39;).click()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;alors je simule un click. Je ne fais que répéter la doc:&lt;/p&gt;
&lt;p&gt;&amp;ldquo;In the third variation, when .click() is called without arguments, it
is a shortcut for .trigger(&amp;ldquo;click&amp;rdquo;).&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Mais si je mets un handler en paramètre, alors tout le comportement est
différent, car dans ce cas la fonction &amp;ldquo;click&amp;rdquo; enregistre un &amp;ldquo;event
listener&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Deux comportements purement éloignés, qui sont bien séparés dans tous
les autres framework JS. On définis plutôt une méthode &amp;ldquo;onclick&amp;rdquo; et
&amp;ldquo;click&amp;rdquo; qui font deux choses bien distinctes.&lt;/p&gt;
&lt;h3 id=&#34;terminologie-hasardeuse&#34;&gt;Terminologie hasardeuse&lt;/h3&gt;
&lt;p&gt;jQuery a vraiment un souci avec les termes. Quand je lis le code qu&amp;rsquo;on
pond, j&amp;rsquo;ai l&amp;rsquo;impression de parler comme un petit débutant qui connait
rien au HTTP, HTML, CSS et JS&amp;hellip; je vous explique.&lt;/p&gt;
&lt;p&gt;Savez-vous ce qu&amp;rsquo;est un CSS ? Cascading StyleSheet, ce qui signifie
&amp;ldquo;Feuille de style en cascade&amp;rdquo;. Autrement dit, une CSS est une
arborescence qui définit des styles pour des éléments dissociés par des
attributs (class, type&amp;hellip;), des identifiants (id), des états (active,
hover, visited&amp;hellip;) et en les repérant par leur hiérarchie dans le DOM.
C&amp;rsquo;est un standard clair, qui évolue.&lt;/p&gt;
&lt;p&gt;Chaque propriété d&amp;rsquo;un élément repéré est un **style**. On est
d&amp;rsquo;accord ? je l&amp;rsquo;espère&amp;hellip; parce que les auteurs de jQuery ont une autre
définition. Pour eux, un CSS est un style&amp;hellip; Si si&amp;hellip;&lt;/p&gt;
&lt;p&gt;Je vous montre:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$(&#39;.class-element&#39;).css(&#39;color&#39;,&#39;red&#39;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Vous me direz &amp;ldquo;ouais tu pousses, en fait on change la CSS pour les
éléments de classe classe-element &amp;ldquo;. Non ! car:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$(this).css(&#39;border&#39;,&#39;1px solid black&#39;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;ce code change le **style d&amp;rsquo;un élément unique**, et si ça suffit
pas, regardez avec Firebug ou la console de Chrome/Chromium pour vous
rendre compte que l&amp;rsquo;attribut touché est &amp;ldquo;style&amp;rdquo; et **non pas la
feuille de style**. Je chipotte ? si aujourd&amp;rsquo;hui vous voyez un
framework dans un autre langage, pour une autre technologie que le web,
je vous assure que vous seriez choqué.&lt;/p&gt;
&lt;p&gt;Ici, on dit que &amp;ldquo;CSS&amp;rdquo; permet de modifier le style de l&amp;rsquo;élément. C&amp;rsquo;est
juste une erreur me diriez-vous. Et bien non. Les auteurs de jQuery ont
vraiment du mal avec les terminologies, car pour eux on peut faire du
&amp;ldquo;Ajax synchrone&amp;rdquo; avec du JSON.&lt;/p&gt;
&lt;p&gt;Rappel: Ajax = Asynchroneous Javascript And Xml&lt;/p&gt;
&lt;p&gt;Dire que vous faite de l&amp;rsquo;Ajax synchrone, c&amp;rsquo;est comme dire que vous
faites de la photo avec une toile, des peinceaux et de la peinture&amp;hellip;
c&amp;rsquo;est vrai quoi, la photo et la peinture c&amp;rsquo;est exactement pareil, on ne
va pas chipoter hein !&lt;/p&gt;
&lt;p&gt;Alors pour reprendre les choses clairement, si vous faite un appel HTTP
en javascript, que ce soit synchrone ou non, avec du JSON ou non&amp;hellip;
c&amp;rsquo;est ce que nous appelons une &amp;ldquo;requête HTTP&amp;rdquo;, ou &amp;ldquo;Http Request&amp;rdquo;. C&amp;rsquo;est
d&amp;rsquo;ailleurs pour cela que l&amp;rsquo;objet standard JS se nomme XHttpRequest (on
dit aussi XHR), et que les autres frameworks JS l&amp;rsquo;appellent de la même
manière&amp;hellip; Mais apparement, jQuery veut se démarquer avec des termes
erronnés. Ou alors, ce que je pense fortement, c&amp;rsquo;est qu&amp;rsquo;à la base jQuery
faisait du HTTPRequest en mode asynchrone par défaut, et qu&amp;rsquo;un jour les
développeurs se sont rendu compte qu&amp;rsquo;on pouvait faire des appels
synchrones. Comme pour &amp;ldquo;css&amp;rdquo;, ils ne se sont pas posé la question et ont
ajouté l&amp;rsquo;option &amp;ldquo;synchrone&amp;rdquo; à une terminologie qui veut dire
&amp;ldquo;asynchrone&amp;rdquo;: Ajax.&lt;/p&gt;
&lt;p&gt;Ce qui me choque, c&amp;rsquo;est que jQuery est arrivé après Prototype et
Mootools, et eux ont fait cette distinction. Sauf qu&amp;rsquo;au lieu de
respecter un standard de dénomination, les auteurs de JQuery ont décidé
de prendre des termes &amp;ldquo;bateaux&amp;rdquo; pour monsieur Michu qui décide de
développer sans connaître Javascript à la base. On y reviendra après.&lt;/p&gt;
&lt;p&gt;Vous me trouvez sarcastique, je ne m&amp;rsquo;en cache pas. C&amp;rsquo;est pas aggressif,
c&amp;rsquo;est juste que je tente de défendre ces idées depuis des années et que
je reste dépité à entendre des gens défendre des erreurs pareils. Bref,
continuons.&lt;/p&gt;
&lt;h3 id=&#34;sélecteurs-illogiques&#34;&gt;Sélecteurs illogiques&lt;/h3&gt;
&lt;p&gt;J&amp;rsquo;adore les selecteurs. Non là je ne suis pas ironique ! Que ce soit sur
Prototype, jQuery, Mootools, etc. l&amp;rsquo;utilisation des sélecteurs me plait.&lt;/p&gt;
&lt;p&gt;Mais&amp;hellip; là où jQuery m&amp;rsquo;enrage, c&amp;rsquo;est cette utilisation de &amp;ldquo;$&amp;rdquo; pour tout
et n&amp;rsquo;importe quoi. Et surtout qu&amp;rsquo;il fait une chose infâme quand on bosse
sur du DOM, laissez-moi vous expliquer.&lt;/p&gt;
&lt;p&gt;On m&amp;rsquo;a par ailleurs sorti: &amp;ldquo;attend mais Xpath tu n&amp;rsquo;aimes pas ?&amp;rdquo; Mais
bien sûr que si ! Je me répète, j&amp;rsquo;adore les selecteurs, mais encore
faut-il que le framework sache les utiliser dans les règles. Je vous
donne un exemple tout bête. Un élément peut avoir un &amp;ldquo;id&amp;rdquo; (identifiant)
qui **doit être unique**. En JS pure, on utilise alors la fonction
&amp;ldquo;getElementById&amp;rdquo; qui récupère **un et un seul élément**,
c&amp;rsquo;est-à-dire l&amp;rsquo;élement qui a cet ID.&lt;/p&gt;
&lt;p&gt;Avant de passer à la suite: oui merci je connais querySelector et
querySelectorAll qui utilisent la même syntaxe que jQuery pour les
sélecteurs, et je n&amp;rsquo;aime pas&amp;hellip;&lt;/p&gt;
&lt;p&gt;Revenons à mon souci: Si deux éléments ont le même ID, alors c&amp;rsquo;est une
erreur de votre part. Vous devez corriger le DOM.&lt;/p&gt;
&lt;p&gt;Sauf que voilà&amp;hellip; JQuery ne sait pas ce que c&amp;rsquo;est que de retourner un
seul élément. Il retourne toujours une collection, que l&amp;rsquo;élément soit
unique (via un id) ou pas.&lt;/p&gt;
&lt;p&gt;Alors comparer cela au comportement de Xpath, excusez-moi mais ça me
fait mal ! Car Xpath retourne un élement unique et non une collection si
vous sélectionnez un élément qui doit l&amp;rsquo;être. Point barre. Et
querySelectorAll fait pas mieux là dessus&amp;hellip; Cherchez pas à me vendre
que le code suivant est normal:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;var uid = $(&#39;#id-unique&#39;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Ici, &amp;ldquo;uid&amp;rdquo; est une collection, avec un seul élément à l&amp;rsquo;intérieur.
Inutile de me retourner une collection.&lt;/p&gt;
&lt;p&gt;Tiens je reviens sur ce que j&amp;rsquo;ai dit, finalement querySelector est pas
bête, car lui il me retourne un seul élément, et querySelectorAll me
retourne une collection, je sais donc choisir entre deux cas. Ho mais
attendez&amp;hellip;&lt;/p&gt;
&lt;p&gt;Prototype et Mootools (pour ne citer que ceux ci) font une distinction
importante entre une collection et un élément, ils ont utilisé une
fonction séparée pour cela:&lt;/p&gt;
&lt;p&gt;-$ pour un élément identifié par un id -$$ pour une collection,
sélectionnée via des selecteurs CSS&lt;/p&gt;
&lt;p&gt;Marrant hein, 7 ans plus tard les moteurs JS récents utilisent aussi
deux méthodes de sélections: querySelector et querySelectorAll.&lt;/p&gt;
&lt;p&gt;Mais attendez la suite, vous allez voir que pour jQuery, un élément
ayant un id n&amp;rsquo;est pas forcément unique, selon le cas. C&amp;rsquo;est juste la
section suivante.&lt;/p&gt;
&lt;h3 id=&#34;4-méthodes-2-comportements-aucune-logique-dom&#34;&gt;4 méthodes, 2 comportements, aucune logique DOM&lt;/h3&gt;
&lt;p&gt;Qu&amp;rsquo;est ce qui est passé par la tête des développeurs de JQuery quand ils
ont développé les méthodes &amp;ldquo;find&amp;rdquo;, &amp;ldquo;children&amp;rdquo;, &amp;ldquo;parent&amp;rdquo; et &amp;ldquo;parents&amp;rdquo; ?&lt;/p&gt;
&lt;p&gt;Tout d&amp;rsquo;abord, le truc qui me choque: find et children. Le premier trouve
tous les déscendants d&amp;rsquo;un élément selon un sélecteur, alors que children
fait pareil mais pour **seulement le premier niveau**. Admettons, ça
ne me choque pas trop jusque-là. Mais par conséquence, pourquoi
&amp;ldquo;parents&amp;rdquo; peut remonter de plusieurs niveaux ? On a deux méthodes
inversement liées (parents, enfants) qui n&amp;rsquo;ont pas la même logique de
sélection.&lt;/p&gt;
&lt;p&gt;Et toujours avec cette fichue collection par défant, parent() vous
retourne un seul élément (normal hein) mais dans une collection&amp;hellip;&lt;/p&gt;
&lt;p&gt;Si ça n&amp;rsquo;était que cela, je dirai &amp;ldquo;ok, pourquoi pas, après tout c&amp;rsquo;est une
dénomination&amp;rdquo;. Mais là où ça marche plus, c&amp;rsquo;est quand je vois ça:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- on fait n&#39;importe quoi --&amp;gt;
&amp;lt;span id=&amp;quot;foo&amp;quot;&amp;gt;
    &amp;lt;div id=&amp;quot;toto&amp;quot;&amp;gt;toto 1&amp;lt;/div&amp;gt;
    &amp;lt;div id=&amp;quot;toto&amp;quot;&amp;gt;toto 2&amp;lt;/div&amp;gt;
    &amp;lt;div id=&amp;quot;toto&amp;quot;&amp;gt;toto 3&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
         &amp;lt;span id=&amp;quot;toto&amp;quot;&amp;gt;un span toto 4&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On est d&amp;rsquo;accord que c&amp;rsquo;est une erreur, on a 3 fois le même &amp;ldquo;id&amp;rdquo;. JQuery
est pas trop débile et sait que &amp;ldquo;$(&amp;rsquo;#toto&amp;rsquo;)&amp;rdquo; retournera un seul
élément, en l&amp;rsquo;occurence le premier, et à la limite ça me convient.&lt;/p&gt;
&lt;p&gt;Mais par contre, avec children, on en a 3 !&lt;/p&gt;
&lt;p&gt;Testez: &lt;code&gt;alert($(&amp;quot;#toto&amp;quot;).length) alert($(&amp;quot;#foo&amp;quot;).children(&amp;quot;#toto&amp;quot;).length) alert($(&amp;quot;#foo&amp;quot;).find(&amp;quot;#toto&amp;quot;).length)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Vous pouvez voir ici: &lt;a href=&#34;http://jsfiddle.net/metal3d/9KDA4/1/&#34;&gt;http://jsfiddle.net/metal3d/9KDA4/1/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est quoi cette implémentation ? trois cas possibles ?&lt;/p&gt;
&lt;p&gt;On se retrouve avec des comportements illogiques.&lt;/p&gt;
&lt;h3 id=&#34;tout-passe-par--merci-la-poo&#34;&gt;Tout passe par $, merci la POO&lt;/h3&gt;
&lt;p&gt;Et pour revenir au cas du fameux &amp;ldquo;$&amp;rdquo;, on va prendre un exemple de
handler de click. Dans cette méthode, &amp;ldquo;this&amp;rdquo; correspond bien à l&amp;rsquo;élément
cliqué, mais pas &amp;ldquo;$(this)&amp;rdquo; qui lui est une collection contenant un seul
élément (celui qui est cliqué)&amp;hellip; Cela a une impacte sur la logique:
puisque le handler est appelé par jQuery, il y a peu de raison (perso
j&amp;rsquo;en vois pas) pour que &amp;ldquo;this&amp;rdquo; ne soit pas au moins l&amp;rsquo;objet jQuery
contenant l&amp;rsquo;élément. Parce que forcer l&amp;rsquo;utilisateur à taper &amp;ldquo;$(this)&amp;rdquo;
pour avoir les méthodes spécifiques au framework&amp;hellip; je ne vois pas
l&amp;rsquo;intérêt.&lt;/p&gt;
&lt;p&gt;Et en en vient à ce qui me dépite le plus&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ce fichu &amp;ldquo;$&amp;rdquo; sert à tout dans jQuery: sélecteur, espace de nom, bind
d&amp;rsquo;évent du document&amp;hellip; tout y passe. Du coup, quoique vous fassiez,
créer un XHR, une collection, une collection, c&amp;rsquo;est un objet &amp;ldquo;jQuery&amp;rdquo;
(mapé en $).&lt;/p&gt;
&lt;p&gt;Impossible de faire des classes, et ne venez pas me parler de $.extend
parce que tout développeur qui a bossé avec des langages objets ne peut
honnêtement me dire que cette méthode est digne de ce nom.&lt;/p&gt;
&lt;p&gt;Alors que Prototype, Mootools, Dojo, etc, vous permettent de bosser avec
des classes, jQuery vous demande de faire du code pas beau pour tenter
de faire des héritages, des proxy et on oublie l&amp;rsquo;implementation.
Impossible de classifier des objets par classe. Et c&amp;rsquo;est là que le &amp;ldquo;code
less, do more&amp;rdquo; me fait sourire, car pour faire du code un peu sérieux,
on est obligé de coder beaucoup plus pour contrôler tout le code, alors
que JS est un langage Objet et qu&amp;rsquo;on pourrait faire du code plus propre,
plus pro, et maintenable.&lt;/p&gt;
&lt;p&gt;Franchement, regardez du code dans d&amp;rsquo;autres framework, par exemple en
Mootools (que j&amp;rsquo;aime beaucoup):&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$(&amp;quot;element&amp;quot;).addEvent(&#39;click&#39;, function (){ this.setStyle(&#39;color&#39;,&#39;red&#39;)     });&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Comprennez bien que dans Mootools, Prototype, etc. la fonction &amp;ldquo;$&amp;rdquo; ne
sert que de sélecteur ! l&amp;rsquo;espace de nom est un élément du document. Cela
permet de faire des classes, des implémentations, sans avoir des &amp;ldquo;$&amp;rdquo;
partout.&lt;/p&gt;
&lt;h3 id=&#34;masquage-du-js&#34;&gt;Masquage du JS&lt;/h3&gt;
&lt;p&gt;Je reviens à M. Michu qui code en JQuery sans connaitre le javascript.
D&amp;rsquo;une, **non je ne critique pas tous les utilisateurs de jQuery en
pensant que ce sont des débiles**. Que ce soit bien clair, jamais je
n&amp;rsquo;ai pensé cela ! On me l&amp;rsquo;a ressorti ce matin, ça commence sérieusement
à m&amp;rsquo;aggacer que &amp;ldquo;du moment où on a une vue critique d&amp;rsquo;un framework&amp;rdquo;
(surtout que, désolé, mais je connais très bien JS et d&amp;rsquo;autres
frameworks) alors ses utilisateurs se voient automatiquement pris pour
des cons.&lt;/p&gt;
&lt;p&gt;Ce que je critique, ce n&amp;rsquo;est pas le fait de ne pas connaître JS. C&amp;rsquo;est
le fait que le framework masque fortement le langage. C&amp;rsquo;est le cas pour
beaucoup de frameworks de pas mal de langages. Je vise particulièrement
PHP et eZ Publish qui ont vraiment tendance à sucrer le langage et
forcer les intégrateurs à coder dans les templates.&lt;/p&gt;
&lt;p&gt;Le vrai souci ici, c&amp;rsquo;est que la puissance du langage est littéralement
court-circuitée par le framework. C&amp;rsquo;est beaucoup moins le cas pour
Prototype par exemple qui a gardé (justement) les notions de prototypage
de classe. Mootools a une syntaxe très claire de création de classe et
garde ses méthodes proches du langage d&amp;rsquo;origine.&lt;/p&gt;
&lt;p&gt;Or, avec jQuery, vous ne faites pas de JS, mais du jQuery. Et c&amp;rsquo;est
selon moi très dommage.&lt;/p&gt;
&lt;h3 id=&#34;une-note-positive-quand-même-&#34;&gt;Une note positive quand même ?&lt;/h3&gt;
&lt;p&gt;Mais évidemment ! Si je suis aussi &amp;ldquo;agressif&amp;rdquo; envers jQuery c&amp;rsquo;est
surtout parce que beaucoup de projets pro utilisent ce framework sans
avoir de réflexion a priori. jQuery est un excelent framework pour des
interfacages rapides, monter un ou deux caroussels et utiliser des
systèmes &amp;ldquo;basiques&amp;rdquo;. C&amp;rsquo;est parfait pour un blog, un site non complexe et
ne demandant pas beaucoup d&amp;rsquo;interactions. Parfait parce que
**simple** et ne demandant pas beaucoup de code pour une utilisation
standard.&lt;/p&gt;
&lt;p&gt;Mais si votre interface a besoin d&amp;rsquo;évolutivité, de maintenance, demande
beaucoup de code pour interfacer dynamiquement le site, alors utilisez
un framework plus orienté POO, plus professionnel et moins gadget.&lt;/p&gt;
&lt;p&gt;jQuery a eut le mérite de proposer une implémentation vraiment facile à
utiliser, rapide à mettre en oeuvre et ne demandant pas de lire de la
doc constamment. Mais avant de choisir par défaut jQuery parce que &amp;ldquo;vous
le connaissez bien&amp;rdquo;, testez d&amp;rsquo;autres frameworks et faites-vous une idée.&lt;/p&gt;
&lt;p&gt;Le bilan de 7 ans à utiliser 5 frameworks m&amp;rsquo;a donné cette vision:&lt;/p&gt;
&lt;p&gt;-jquery != js, = gadget super simple et efficace en mode standard
-prototype, mootools = POO, fiable, propre et modulaire -dojo =
implémentation supra modulaire, proche de JS standard (oui, innerHTML ça
marche hein) -extJS = on se tape des UI pour faire une application, pas
un site&lt;/p&gt;
&lt;p&gt;Mais:&lt;/p&gt;
&lt;p&gt;-tous les frameworks ont tendance à faire oublier la puissance de JS et
du DOM -trop de développeurs oublient que la plupart des UI qu&amp;rsquo;ils font
sont faisables en CSS + un JS basique simple -un framework JS ne doit
être utilisé que si, et seulement si vous perdez du temps à le faire en
JS simple&lt;/p&gt;
&lt;p&gt;Maintenant, vous pouvez penser ce que vous voulez, j&amp;rsquo;en ai discuté lors
des conférences que j&amp;rsquo;ai données, et je sais que je ne suis pas du tout
le seul à penser que jQuery devrait franchement être utilisé à moindre
mesure&amp;hellip; et que trop de développeurs ne s&amp;rsquo;intéressent pas assez aux
autres technologies. C&amp;rsquo;est mon opinion, voilà tout.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Apple vs Samsung ou comment tromper le monde</title>
      <link>https://www.metal3d.org/blog/2012/apple-vs-samsung-ou-comment-tromper-le-monde/</link>
      <pubDate>Sat, 25 Aug 2012 11:39:59 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2012/apple-vs-samsung-ou-comment-tromper-le-monde/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Apple a gagné&amp;hellip; ils ont tous les droits ! le droit de se proclamer
maitre de technologies qu&amp;rsquo;ils n&amp;rsquo;ont pas inventé, que des standards
soient leur propriété, qu&amp;rsquo;il puissent vous demander de payer pour ça.&lt;/p&gt;
&lt;p&gt;Vous ne me croyez pas ? Regardez un peu cette image&amp;hellip; elle parle d&amp;rsquo;elle
même. Et non, ce n&amp;rsquo;est pas une blague, un fake ou quoique ce soit, mais
une pièce à conviction présentée au procés.&lt;/p&gt;
&lt;p&gt;Non mais rien ne vous choque là ? Des brevets sur des &amp;ldquo;icones au coins
arrondis&amp;rdquo; ??? je rêve pas ? Le &amp;ldquo;multitouch&amp;rdquo; ? non toujours pas ?&lt;/p&gt;
&lt;p&gt;Samsung se défend avec les même armes, et c&amp;rsquo;est une erreur
malheureusement. Et oui, on est au pays du MacDo là. La boite américaine
a forcément gain de cause face à un géant asiatique. Mais que ce soit au
dépit de tout bon sens me rend fou&amp;hellip;&lt;/p&gt;
&lt;p&gt;Comment un jury a put permettre une firme milliardaire s&amp;rsquo;approprier des
droits sur des technologies qu&amp;rsquo;elle n&amp;rsquo;a pas inventé, et pire: sur des
idées qui n&amp;rsquo;ont rien d&amp;rsquo;innovantes !!!&lt;/p&gt;
&lt;p&gt;Comment un tel enjeu liberticide a put être voté ? mais comment des
humains peuvent avoir l&amp;rsquo;audace de délibérer un jugement qui prône
l&amp;rsquo;absence total d&amp;rsquo;innovation !&lt;/p&gt;
&lt;p&gt;Apple peut donc acheter un brevet, même à son créateur, et interdire à
tout le monde de l&amp;rsquo;utiliser. Voilà ce que veut dire ce procès, voilà ce
que veut dire le jugement donné chez nos copains les ricains.&lt;/p&gt;
&lt;p&gt;Bravo&amp;hellip; car un pays qui se veut tellement ouvert et exemplaire en terme
de liberté vient de faire un procès inversant tout fondement de leur
belle Amérique.&lt;/p&gt;
&lt;p&gt;Non messieurs les fansboys de Apple, on ne vous appréciera pas davantage
si vous défendez un tel acte d&amp;rsquo;injustice. Non, Apple n&amp;rsquo;a jamais inventé
le multitouch, les icones au bord arrondis, ou encore de pouvoir écouter
de la musique en utilisant une autre application&amp;hellip; Non, Apple n&amp;rsquo;a pas
le droit de se prétendre garante de cela, surtout pour l&amp;rsquo;interdire. Non,
ce n&amp;rsquo;était pas du vol !!! l&amp;rsquo;innovation passe par le fait qu&amp;rsquo;une
technologie soit utilisée en de multiple situation. Enfermer une techno
(qui en plus, je le répète, n&amp;rsquo;a pas été inventée par APPLE !!!) à un
usage strict ne fait qu&amp;rsquo;une chose: enrichir le dirigeant. Vous, vous n&amp;rsquo;y
gagnez rien&amp;hellip; au contraire.&lt;/p&gt;
&lt;p&gt;Vous, utilisateur, vous allez devoir attendre des années avant d&amp;rsquo;avoir
mieux, tout simplement parce que la boite qui a désormais le droit
d’interdire une utilisation va pouvoir prendre tout son temps pour
élaborer un produit encore plus cher, dans un temps qu&amp;rsquo;elle va
maîtriser, en étant sûr que personne ne puisse faire mieux, plus
rapidement, moins cher. Donc vous, Apple Addict, vous acceptez cela sans
broncher, vous payez, vous être content d&amp;rsquo;avoir un produit &amp;ldquo;hype&amp;rdquo; mais
dénué de qualité techniques avancées. Ça vous fait plaisirs de savoir
que votre bonne boite Apple vous a vendu un appareil 5 fois plus cher
que ce que ça lui coûte, que la techno qu&amp;rsquo;elle vous vend est dépassée
mais que personne n&amp;rsquo;ait le droit de vous proposer **mieux et moins
cher** ?&lt;/p&gt;
&lt;p&gt;Donc si demain votre boulanger à tous les droits sur le pain, qu&amp;rsquo;il a
réussit à interdire la vente de baguette dans tout le pays, et qu&amp;rsquo;il en
profite pour monter le prix tout en prenant de la farine moisie, ça vous
dérange pas ?&lt;/p&gt;
&lt;p&gt;Bravo&amp;hellip; je ne peux qu&amp;rsquo;applaudir ce comportement, parce que je pensais
qu&amp;rsquo;il avait disparut au début du 19ième siècle&amp;hellip;&lt;/p&gt;
&lt;p&gt;Amis Apple Addicts: Vous vous rendez compte au moins que défendre un tel
acte est grave ? Vous avez au moins écouté les autres qui vous
expliquait le danger ? Vous avez essayé de comprendre ?&lt;/p&gt;
&lt;p&gt;La suite c&amp;rsquo;est quoi ? on interdit les téléphones portables parce que
Apple aura un brevet sur &amp;ldquo;les appareils sans fil permettant une
télécommunication&amp;rdquo; ? Accorchez vous bien parce que ça va arriver là&amp;hellip;
on est à deux doigts de ça !&lt;/p&gt;
&lt;p&gt;Cette politique me dégoute sérieusement. Là vraiment, plus rien ne me
fera acheter un produit Apple. J&amp;rsquo;avais déjà pas envie question techno&amp;hellip;
alors là&amp;hellip;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Gstreamer la suite</title>
      <link>https://www.metal3d.org/blog/2012/gstreamer-la-suite/</link>
      <pubDate>Tue, 14 Aug 2012 21:29:48 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2012/gstreamer-la-suite/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Toujours envie de faire du gstreamer ? Et si on s&amp;rsquo;amusait un petit peu
avec quelques plugins ? Genre des visualisations graphiques de sons&amp;hellip;
et appréhender un souci simple de gstreamer qui ne peut pas raccorder
une sortie sur deux entrées. Et de trouver la solution.&lt;/p&gt;
&lt;p&gt;Je vous fais un petit rappel vite fait. Gstreamer est un framework
utilisable en bash, python, C, Vala, Java, Ruby, etc. Ce framework
permet de manipuler des flux audio et vidéo au travers de plugins.
L&amp;rsquo;ensemble des liaisons de plugins se nomme un &amp;ldquo;pipeline&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Chaque plugins peut être nommé, un peu comme une variable dans un
script. On fait un lien entre deux plugins entre des &amp;ldquo;src&amp;rdquo; et des &amp;ldquo;sink&amp;rdquo;
avec un point d&amp;rsquo;exclamation (!). Le flux va de gauche à droite.&lt;/p&gt;
&lt;p&gt;Le plugin &amp;ldquo;decodebin&amp;rdquo; est un bonheur magique qui permet de décoder un
flux sans avoir à chercher constamment quel démultiplexeur et décodeur
utiliser.&lt;/p&gt;
&lt;p&gt;On utilise gst-inspect pour trouver un plugin et/ou savoir comment il
fonctionne, par exemple pour les &amp;ldquo;caps&amp;rdquo; qui sont des options.&lt;/p&gt;
&lt;p&gt;On teste un pipeline avec gst-launch.&lt;/p&gt;
&lt;p&gt;Un exemple de pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;monfichier.ogg&amp;quot; ! decodebin ! \
audioconvert ! audioresample ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici le flux part d&amp;rsquo;une source fichier, on utilise le &amp;ldquo;caps&amp;rdquo; location qui
définit le chemin du fichier. Le flux est envoyé vers decodebin, puis
dans audioconvert, puis audioresample et enfin dans un plugin qui va
trouver la sortie sonore adéquate (en général c&amp;rsquo;est pulseaudio, donc on
entend le son).&lt;/p&gt;
&lt;p&gt;On prend l&amp;rsquo;habitude de resampler le son, de le convertir etc. Avec
l&amp;rsquo;habitude ça va tout seul.&lt;/p&gt;
&lt;p&gt;Quand on a besoin de manipuler deux flux en même temps, par exemple une
vidéo avec du son, on fait deux pipes et on les place dans une queue
pour avoir deux threads en parallèle. On prend soin de nommer quel
plugin réutiliser pour récupérer les &amp;ldquo;src&amp;rdquo; dans les pipes qui composent
le pipeline. Exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;mavideo.mp4&amp;quot; ! decodebin name=foo \
foo. ! queue ! ffmpegcolorspace ! videoscale ! autovideosink \
foo. ! queue ! audioconvert ! audioresample ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, &amp;ldquo;foo&amp;rdquo; est le nom que je donne à &amp;ldquo;decodebin&amp;rdquo;. On utilise un point
pour récupérer le plugin qui porte ce nom (sinon gstreamer cherchait un
plugin &amp;ldquo;foo&amp;rdquo;) et je le place dans une queue. Gstreamer se débrouille
pour raccoder les src de bon type aux sink qui peuvent supporter le
flux. Ici la vidéo est traitée par ffmpegcolorspace, puis videoscale et
arrive sur mon écran. Le son part en queue et va dans audioconvert, puis
audioresample, puis mes enceintes.&lt;/p&gt;
&lt;p&gt;Faites attention de ne pas mettre de &amp;ldquo;!&amp;rdquo; partout, il ne sert que de lien
quand on en a besoin. Par exemple dans le pipeline précédent, on ne fait
pas de lien entre autovideosink et &amp;ldquo;foo.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Bon, et bien jouons un peu. J&amp;rsquo;avais envie d&amp;rsquo;utiliser un visualisateur de
son, vous savez&amp;hellip; le truc graphique qui bouge avec le son, c&amp;rsquo;est joli
en plus ! Il existe des plugins gstreamer qui le permettent. Ils se
trouvent dans l&amp;rsquo;ensemble &amp;ldquo;audiovisualizer&amp;rdquo;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-inspect | grep -i audiovisual
audiovisualizers:  wavescope: Waveform oscilloscope
audiovisualizers:  synaescope: Synaescope
audiovisualizers:  spectrascope: Frequency spectrum scope
audiovisualizers:  spacescope: Stereo visualizer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si je vais vous parler de ces outils, c&amp;rsquo;est surtout pour vous montrer un
problème qu&amp;rsquo;on peut résoudre aisément. Ce problème est simplement de
pouvoir réutiliser le même flux dans deux &amp;ldquo;sink&amp;rdquo;. **Car gstreamer ne
peut pas raccorder une src à deux sink.** Mais il existe un plugin qui
va nous permettre de le faire :)&lt;/p&gt;
&lt;p&gt;Je vous prépare à un autre problème &amp;hellip; ces visualisateurs ne sortent
pas une vidéo, mais des images. Du coup &amp;ldquo;autovideosink&amp;rdquo; ne fonctionnera
pas, il faudra utiliser &amp;ldquo;ximagesink&amp;rdquo; ou xvimagesink&amp;quot; selon certains cas.&lt;/p&gt;
&lt;p&gt;Ici, ce sont des images brutes&amp;hellip; on va utiliser ximagesink comme
fenêtre de visualisation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;Musique/good_night_moon-shivare_e.ogg&amp;quot; ! decodebin !\
audioconvert ! audioresample ! wavescope ! ximagesink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, cool hein :) c&amp;rsquo;est joli, on voit la courbe&amp;hellip; mais on entend rien !
Alors voilà le souci, on se dit &amp;ldquo;bha ok, simple, je vais nommer
decodebin, et réutiliser la sortie pour la mettre dans autoaudiosink&amp;rdquo;&amp;hellip;
Pas bête&amp;hellip; mais ça marchera pas. On tente (en utilisant des queues
hein, pour faire tourner le son en même temps que le visualisteur):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;Musique/good_night_moon-shivare_e.ogg&amp;quot; ! decodebin name=d \
d. ! queue ! audioconvert ! audioresample ! wavescope ! ximagesink \
d. ! queue ! audioconvert ! audioresample ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bon, ça bloque&amp;hellip; effectivement &amp;ldquo;decodebin&amp;rdquo; n&amp;rsquo;a qu&amp;rsquo;un flux de sortie, un
seul &amp;ldquo;src&amp;rdquo; donc&amp;hellip; il se raccorde à la queue, et on a plus d&amp;rsquo;autre src à
raccorder à l&amp;rsquo;autre queue. Ce serait donc cool de pouvoir démultiplier
le flux pour l&amp;rsquo;envoyer dans les deux plugins.&lt;/p&gt;
&lt;p&gt;Il existe un plugin qui va faire cela avec une facilité déconcertante:
tee. Oui, comme un &amp;ldquo;T&amp;rdquo; qu&amp;rsquo;on utilise pour dériver des câbles d&amp;rsquo;antennes.
Notez aussi que &amp;ldquo;tee&amp;rdquo; est une commande linux qui fait pareil en shell:
sortir un contenu à deux endroits différents.&lt;/p&gt;
&lt;p&gt;Bref, &amp;ldquo;tee&amp;rdquo; fait à peu près cela:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;        +-------------+
        |    Tee      |
----&amp;gt; (sink)         (src0) -&amp;gt; plugin
        |            (src1) -&amp;gt; plugin
        |            (...)
        |            (srcN) -&amp;gt; plugin...
        |             |
        +-------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il sort un src à la demande, sitôt qu&amp;rsquo;on tente de le connecter à un
autre plugin. &amp;ldquo;Magique&amp;rdquo; est encore le mot qui me vient à l&amp;rsquo;esprit.&lt;/p&gt;
&lt;p&gt;Bref, dans notre cas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;Musique/good_night_moon-shivare_e.ogg&amp;quot; ! decodebin ! \
audioconvert ! audioresample ! tee name=T \
T. ! queue ! wavescope ! ximagesink \
T. ! queue ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je vous fait un petit asciart pour comprendre:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;filesrc &amp;gt;-+
          |
      decodebin ---&amp;gt; audioconvert --+
                                    |
          +------------------------+
          |
          +---&amp;gt;audioresample -------+
                                    |
    +-------------------------------+
    |
    |    +------------------+
    |    |    tee T         &amp;gt;&amp;gt; queue ---&amp;gt; wavescope -&amp;gt; ximagesink
    +---&amp;gt;&amp;gt;                  |
         |                  |
         |                  &amp;gt;&amp;gt; queue --&amp;gt; autoaudiosink
         +------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le flux sortant de audioresample est dédoublé en deux via le &amp;ldquo;tee&amp;rdquo;. On
place chaque sortie en queue, et ça tourne nickel !&lt;/p&gt;
&lt;p&gt;Ce genre de tee peut justement vous permettre de &amp;ldquo;voir&amp;rdquo; une vidéo en
l&amp;rsquo;encodant et l&amp;rsquo;enregistrant dans un fichier.&lt;/p&gt;
&lt;p&gt;Ou alors, pour le fun, raccorder plusieur visualisteur de son, de ce
genre:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;filesrc &amp;gt;-+
          |
      decodebin ---&amp;gt; audioconvert --+
                                    |
          +------------------------+
          |
          +---&amp;gt;audioresample -------+
                                    |
    +-------------------------------+
    |
    |    +------------------+
    |    |    tee T         &amp;gt;&amp;gt; queue ---&amp;gt; wavescope -&amp;gt; ximagesink
    +---&amp;gt;&amp;gt;                  |
         |                  |
         |                  &amp;gt;&amp;gt; queue --&amp;gt; spacescope -&amp;gt; ximagesink
         |                  |
         |                  &amp;gt;&amp;gt; queue --&amp;gt; autoaudiosink
         +------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En tapant ce pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;Musique/good_night_moon-shivare_e.ogg&amp;quot; ! decodebin ! \
audioconvert ! audioresample ! tee name=T \
T. ! queue ! wavescope ! ximagesink \
T. ! queue ! spacescope ! ximagesink \
T. ! queue ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je sais que parfois la lecture d&amp;rsquo;un pipeline peut paraître un peu
ostère, mais à la longue, en quelques heures, vous prendrez le coup.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Didacticiel GStreamer</title>
      <link>https://www.metal3d.org/blog/2012/didacticiel-gstreamer/</link>
      <pubDate>Mon, 13 Aug 2012 11:35:11 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2012/didacticiel-gstreamer/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Gstreamer est un framework extrêmement puissant, bien pensé et vraiment
utile permettant de travailler avec des flux audio et vidéo. Decodage,
encodage, streaming, effets, et j&amp;rsquo;en passe&amp;hellip; Il est impressionnant de
simplicité à condition d&amp;rsquo;avoir des bases de connaissance sur les
méthodes. C&amp;rsquo;est donc avec plaisirs que je vous propose une montée en
compétence sur ce système de &amp;ldquo;pipeline&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai certainement beaucoup de retard pour créer un tel didacticiel, mais
je ne peux pas tout faire à temps. J&amp;rsquo;ai eut envie de me pencher sur
Gstreamer pour gérer une lecture de vidéo dans une application. J&amp;rsquo;ai
tellement aimé gstreamer que j&amp;rsquo;ai décidé de vous proposer ce
didacticiel.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord, qui a besoin de gstreamer ?&lt;/p&gt;
&lt;p&gt;Gstreamer est utilisé dans la plupart des applications utilisant des
flux vidéo et audio. Totem, le lecteur inclu dans Gnome, KDE, Empathy
pour les vidéo conférences, Web le navigateur Gnome pour la gestion des
médias HTML5&amp;hellip; La liste est longue. De ce fait, l&amp;rsquo;utilisateur final a
besoin des plugins intégrés en général nativement par les distributions
GNU/Linux ou BSD.&lt;/p&gt;
&lt;p&gt;Mais si en plus vous avez envie de créer un outil que ce soit en script
Bash ou via C, Java, Python ou je ne sais quel langage, GStreamer va
vous soulager de la plupart des tâches complexes de gestion de flux
(stream). En effet, il va vous permettre de lire des médias en tant que
source, de l&amp;rsquo;envoyer dans des encodeurs, des décodeurs, des effets, puis
d&amp;rsquo;aller directement se brancher sur le serveur de son (pulse, jack&amp;hellip;)
ou X pour la vidéo. Et ce avec un minimum d&amp;rsquo;effort.&lt;/p&gt;
&lt;p&gt;Donc, clairement, les &amp;ldquo;utilisateurs&amp;rdquo; des pipeline gstreamer sont surtout
les développeurs ou au moins des utilisateurs avertis qui veulent se
créer des outils, des script ou les utiliser par curiosité.&lt;/p&gt;
&lt;p&gt;Avant de savoir utiliser Gstreamer, il faut appréhender le
fonctionnement de flux, de multiplexage et de &amp;ldquo;pipeline&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Prenons un exemple relativement général: une vidéo avec du son. Nous
allons prendre le cas où vous voulez lire un fichier avi qui contient un
film complet.&lt;/p&gt;
&lt;p&gt;Le principe est de récupérer deux flux: la vidéo et la bande son. C&amp;rsquo;est
ce que nous appelons le démultiplexage. Le fichier conteneur sort une
source binaire que nous devons découper en deux flux distincts.&lt;/p&gt;
&lt;p&gt;Ces flux ont un format d&amp;rsquo;encodage (mp3, mp4, vorbis&amp;hellip;) il faut donc les
décoder afin de les utiliser. Et enfin il faut envoyer le flux décodé
dans les sorties adéquates: la vidéo sur l&amp;rsquo;écran via le serveur
graphique, et le son sur le serveur sonore qui envoit le tout dans les
enceintes.&lt;/p&gt;
&lt;p&gt;Ça en fait un paquet d&amp;rsquo;opérations n&amp;rsquo;est-ce pas ?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vidéo avec du son
       |
       |
+--------------------+
|   démultiplexage   |- flux de son -&amp;gt; décodage -&amp;gt; ... -&amp;gt; pulseaudio (sortie sonore) 
+--------------------+
          |
          +- flux vidéo -&amp;gt; décodage -&amp;gt; callage de taille -&amp;gt; ... -&amp;gt; Affichage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gstreamer va vous permettre de faire ces opérations via une ligne de
connexion appelée &amp;ldquo;pipeline&amp;rdquo;. C&amp;rsquo;est le même principe que les
redirections de sortie de commandes en shell.&lt;/p&gt;
&lt;p&gt;Petit rappel:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &amp;quot;coucou vous&amp;quot; | rev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;la commande &amp;ldquo;echo&amp;rdquo; fait sortir &amp;ldquo;coucou vous&amp;rdquo;, puis le pipe &amp;ldquo;|&amp;rdquo; redirrige
cette chaine dans la command &amp;ldquo;rev&amp;rdquo; (reverse, retourne le texte), ce qui
sort &amp;ldquo;suov uocuoc&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Avec Gstreamer on utilisera non pas &amp;ldquo;|&amp;rdquo; mais &amp;ldquo;!&amp;rdquo; pour ne pas interférer
avec le shell.&lt;/p&gt;
&lt;p&gt;Chaque plugin GStreamer peut avoir ces &amp;ldquo;PADS&amp;rdquo; qui représentent des
entrées/sorties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;des sorties: src&lt;/li&gt;
&lt;li&gt;des entrées : sink&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En plus de ces PADS, on peut utiliser des options, nommées &amp;ldquo;caps&amp;rdquo;, par
exemple pour manipuler la qualité d&amp;rsquo;un encodage ou donner un nom à un
plugin qu&amp;rsquo;on va réutiliser plusieurs fois.&lt;/p&gt;
&lt;p&gt;Par conséquent, un pipeline gstreamer va être une simple série de
branchements entre des &amp;ldquo;src&amp;rdquo; et des &amp;ldquo;sink&amp;rdquo;. Notez que le mot &amp;ldquo;sink&amp;rdquo; veut
dire &amp;ldquo;lavabo&amp;rdquo;, l&amp;rsquo;idée étant de déverser un flux dans le lavabo qui se
rempli, le plugin se débrouillera tout seul pour le vider à son rythme.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+----------+         +-----------+
|   pad    |         |   pad 2   |
|        (src) -&amp;gt; (sink)       (src) -&amp;gt; ... 
|          |         |           |
|        (src) -&amp;gt; (sink)         |
|          |         |           |
+----------+         +-----------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Les bases sont en place, on entre dans le vif du sujet.&lt;/p&gt;
&lt;p&gt;Afin de tester un pipeline, on peut utiliser la commande &amp;ldquo;gst-launch&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Utilisons un fichier de son, disons &amp;ldquo;hello.ogg&amp;rdquo;. Je vous montrerai après
comment on peut trouver quel plugin utiliser. Pour le moment faites moi
simplement confiance.&lt;/p&gt;
&lt;p&gt;On va :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Utiliser une source de type fichier: filesrc&lt;/li&gt;
&lt;li&gt;demultiplexer de fichier oggvorbis pour avoir une sortie : oggdemux&lt;/li&gt;
&lt;li&gt;on l&amp;rsquo;envoit ensuite dans le décodeur vorbis: vorbisdec&lt;/li&gt;
&lt;li&gt;on l&amp;rsquo;envoit dans pulseaudio: pulsesink&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L&amp;rsquo;idée est donc de raccorder un pipeline de ce type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;filesrc -&amp;gt; oggdemux -&amp;gt; vorbisdec -&amp;gt; pulsesink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le plugin filesrc a une option nommée &amp;ldquo;location&amp;rdquo; qui permet de donner le
chemin du fichier à utiliser. Voici la commande de lecture:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;hello.ogg&amp;quot; ! oggdemux ! vorbisdec ! pulsesink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous comprenez, on lit de gauche à droite, de sortie en entrée. Les
&amp;ldquo;caps&amp;rdquo; (option de plugin) sont déposés à coté du nom du plugin, et on
utilise &amp;ldquo;=&amp;rdquo; pour donner une valeur. Ainsi, le plugin filesrc à un caps
&amp;ldquo;location&amp;rdquo; que j&amp;rsquo;utilise. C&amp;rsquo;est assez simple.&lt;/p&gt;
&lt;p&gt;Vous allez me dire &amp;ldquo;ok, pour coder un truc qui fait de la lecture c&amp;rsquo;est
cool, mais c&amp;rsquo;est pas drôle de devoir chercher constamment quel
demultiplexeur et décodeur utiliser&amp;rdquo;. Et ce à quoi je vous répond que si
je vous dis que gstreamer est vraiment bien pensé, c&amp;rsquo;est qu&amp;rsquo;il existe
des méthodes qui ressemblent à de la magie.&lt;/p&gt;
&lt;p&gt;En effet, il existe des plugins qui vous font gagner un temps fou. L&amp;rsquo;un
d&amp;rsquo;entre eux est &amp;ldquo;decodebin&amp;rdquo; qui est un décodeur générique sortant du
&amp;ldquo;raw&amp;rdquo;. Il fait office de proxy et se débrouille comme un chef pour
trouver quel décodeur utiliser. Testons-le:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;hello.ogg&amp;quot; ! decodebin ! pulsesink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Magique non ?&lt;/p&gt;
&lt;p&gt;Et là je vous vois venir: &amp;ldquo;oui, et si j&amp;rsquo;utilise pas pulseaudio ? Comment
je sais quel sink final utiliser ?&amp;rdquo;. C&amp;rsquo;est là qu&amp;rsquo;intervient
&amp;ldquo;autoaudiosink&amp;rdquo;. On teste:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;hello.ogg&amp;quot; ! decodebin ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;ldquo;autoaudiosink&amp;rdquo; trouve quelle sortie sonore utiliser&amp;hellip; Et tout seul en
plus !&lt;/p&gt;
&lt;p&gt;Dans le cas où on doit être générique, pour créer un petit lecteur par
exemple, c&amp;rsquo;est simplement (je me répète) magique.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Il faudra tout de même prendre quelques habitudes. Il est toujours
intéressant de rééchantillonner le son avant de le faire sortir afin de
soulager le calcul. En effet un son à un bitrate de 192khz pour une
simple écoute de musique n&amp;rsquo;est pas forcément nécessaire, l&amp;rsquo;oreille
humaine ne pourra pas entendre les hautes fréquences. D&amp;rsquo;autre part,
envoyer un flux trop gros à un décodeur ou un lecteur peut avoir un
véritable impact sur les performances, et certain encodeur n&amp;rsquo;accèpte pas
certaines fréquences. Donc pour rester dans un seuil correct, on
&amp;ldquo;resample&amp;rdquo; le son:&lt;/p&gt;
&lt;p&gt;Ce sera aussi le cas avec les vidéo, il est recommandé de passer par les
plugins ffmpegcolorspace et videoscale lors des traitements. Si je ne
les mets pas dans mes exemples, pensez à le faire&amp;hellip;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=&amp;quot;hello.ogg&amp;quot; ! decodebin ! audioresample ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;On fait compliqué, et on simplifira après&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Cette partie va vous faire peur&amp;hellip; alors avant de la lire en vous disant
&amp;ldquo;ouais bha c&amp;rsquo;est le foutoir pour trouver quel decodeur, encodeur etc&amp;hellip;&amp;rdquo;
rapellez vous qu&amp;rsquo;on utilisera &amp;ldquo;decodebin&amp;rdquo; par la suite, et que par
conséquent vous n&amp;rsquo;aurez pas à vous arracher les cheveux. Ici je vais
simplement vous présenter comment on peut traiter deux flux en même
temps. C&amp;rsquo;est **primordial** car sans cela vous ne saurez jamais
lire/encoder une vidéo avec du son.&lt;/p&gt;
&lt;p&gt;Ok pour le son. On va passer par la partie &amp;ldquo;vidéo&amp;rdquo; puis &amp;ldquo;audio/vidéo&amp;rdquo;
mais avant tout il faut que je vous montre les outils qui permettent
d&amp;rsquo;avoir des informations sur les plugins. Deux sont vraiment utiles dans
tous les cas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gst-inspect&lt;/li&gt;
&lt;li&gt;gst-typefind&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le premier sert à inspecter les capacités d&amp;rsquo;un plugins. Sans argument
vous allez lister tous les plugins installés. En utilisant &amp;ldquo;grep&amp;rdquo; vous
pourrez alors trouver votre bonheur. Par exemple, pour trouver un plugin
qui traite du mp3:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-inspect | grep mp3
typefindfunctions: application/x-id3v2: mp3, mp2, mp1, mpga, ogg, flac, tta
typefindfunctions: application/x-id3v1: mp3, mp2, mp1, mpga, ogg, flac, tta
typefindfunctions: application/x-apetag: mp3, ape, mpc, wv
typefindfunctions: audio/mpeg: mp3, mp2, mp1, mpga
mad:  mad: mad mp3 decoder
mpegaudioparse:  mp3parse: MPEG1 Audio Parser
ffmpeg:  ffmux_mp3: FFmpeg MPEG audio layer 3 formatter (not recommended, use id3v2mux instead)
ffmpeg:  ffdec_mp3on4float: FFmpeg MP3onMP4 decoder
ffmpeg:  ffdec_mp3on4: FFmpeg MP3onMP4 decoder
ffmpeg:  ffdec_mp3adufloat: FFmpeg ADU (Application Data Unit) MP3 (MPEG audio layer 3) decoder
ffmpeg:  ffdec_mp3adu: FFmpeg ADU (Application Data Unit) MP3 (MPEG audio layer 3) decoder
ffmpeg:  ffdec_mp3float: FFmpeg MP3 (MPEG audio layer 3) decoder
ffmpeg:  ffdec_mp3: FFmpeg MP3 (MPEG audio layer 3) decoder
lame:  lame: L.A.M.E. mp3 encoder
lame:  lamemp3enc: L.A.M.E. mp3 encoder
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notez qu&amp;rsquo;on aime beaucoup utiliser &amp;ldquo;mad&amp;rdquo; pour décoder le mp3 sous
gstreamer. Et pour connaitre les informations d&amp;rsquo;un plugins, ce qui vous
permettra en plus d&amp;rsquo;avoir la liste de pads et de caps:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-inspect mad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je ne vous colle pas la sortie complète, mais vous y trouverez les
sections &amp;ldquo;Pad Templates&amp;rdquo; et &amp;ldquo;Element Properties&amp;rdquo;. Vous voyez donc quel
sont les capacités d&amp;rsquo;un plugin, les entrées, les sorties et les options.&lt;/p&gt;
&lt;p&gt;Pour vous amuser, on va ajouter un echoe au son. On cherche ça:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-inspect | grep echo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On trouve &amp;ldquo;audioecho&amp;rdquo;&amp;hellip; on regarde les options:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-inspect audioecho
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je trouve 3 options intéressantes: delay, feedback et intensity. &amp;ldquo;delay&amp;rdquo;
étant en nanoseconde, feedback entre 0 et 1 et intensity entre 0 et 1.
Voici un réglage sympa:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst filesrc location=&amp;quot;hello.ogg&amp;quot; ! decodebin ! audioresample ! audioecho feedback=0.8 delay=1200000000 intensity=0.2 ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quant à &amp;ldquo;gst-typefind&amp;rdquo; il vous donnera le type du fichier que vous
voulez traiter, par exemple :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-typefind hello.ogg
hello.ogg - application/ogg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je suis donc &amp;ldquo;sûr&amp;rdquo; que mon fichier est en oggvorbis je sais donc quel
plugin chercher&amp;hellip; ou presque&amp;hellip;&lt;/p&gt;
&lt;p&gt;Allez, on passe à la vidéo. On utilisera encore une fois &amp;ldquo;decodebin&amp;rdquo;,
mais pour vous montrer comment on se sert des sources et sink, je
commence par la partie compliquée.&lt;/p&gt;
&lt;p&gt;Prenons une vidéo et regardons quel type est utilisé:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-typefind Portishead\ -\ Wandering\ Star.mp4 
Portishead - Wandering Star.mp4 - video/quicktime, variant=(string)iso
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok&amp;hellip; du quicktime&amp;hellip; bon et bien cherchons un demuxer&amp;hellip;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-inspect | grep -i quicktime
...
isomp4:  qtdemux: QuickTime demuxer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, là on va utiliser qtdemux. Quels sont les pads à utiliser:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-inspect qtdemux
...
Pad Templates:
  SRC template: &#39;subtitle_%02d&#39;
    Availability: Sometimes
    Capabilities:
      ANY

  SRC template: &#39;audio_%02d&#39;
    Availability: Sometimes
    Capabilities:
      ANY

  SRC template: &#39;video_%02d&#39;
    Availability: Sometimes
    Capabilities:
      ANY

  SINK template: &#39;sink&#39;
    Availability: Always
    Capabilities:
      video/quicktime
      video/mj2
      audio/x-m4a
      application/x-3gp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aïe&amp;hellip; ça se complique&amp;hellip; Plusieurs &amp;ldquo;src&amp;rdquo; à utiliser, et en plus ce sont
des &amp;ldquo;template&amp;rdquo;. Car ce type de fichier peut contenir des sous-titres,
plusieurs sources vidéos, plusieurs piste de sons. Mais ne prenons pas
peur. En informatique &amp;ldquo;%02d&amp;rdquo; veut dire &amp;ldquo;deux chiffres de type double
(entier) et si le nombre ne tient pas 2 caractère on lui met un 0
(zéro), par exemple 01&amp;rdquo;, donc en toutes logique on va utiliser la vidéo:
&amp;ldquo;video_00&amp;rdquo; pour avoir la première piste vidéo, et &amp;ldquo;audio_00&amp;rdquo; pour la
première piste audio.&lt;/p&gt;
&lt;p&gt;Ne cherchez pas encore comment savoir quel décodeur utiliser, ici je
vous le dit: ma vidéo est en &amp;ldquo;h264&amp;rdquo; et le son en &amp;ldquo;aac&amp;rdquo;. Si je vous dis
de ne pas chercher trop longtemps c&amp;rsquo;est que vous pourrez vous en passer
par la suite. Je veux juste vous montrer comment récupérer des sources
dans un plugin.&lt;/p&gt;
&lt;p&gt;Dans un premier lieu, on peut se passer de cette option. Gstreamer va
trouver tout seule quoi raccorder à quoi. De ce fait:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=Portishead\ -\ Wandering\ Star.mp4 ! qtdemux ! ffdec_h264 ! autovideosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et pour le son:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=Portishead\ -\ Wandering\ Star.mp4 ! qtdemux ! ffdec_aac ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alors comment je vais faire pour récupérer les deux en même temps ?&lt;/p&gt;
&lt;p&gt;Et voilà comment on en vient à donner un nom à un pipe ! Le &amp;ldquo;caps&amp;rdquo;
&amp;ldquo;name&amp;rdquo; qui est utilisable par tous les plugins va nous permettre cette
manipulation. Le but est de pouvoir réutiliser un plugin en cours
d&amp;rsquo;utilisation pour récupérer d&amp;rsquo;autres &amp;ldquo;src&amp;rdquo; (ou d&amp;rsquo;autres sink).&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est là qu&amp;rsquo;il faut y aller par étape:&lt;/p&gt;
&lt;p&gt;-on donne un nom au plugin de démultiplexeur pour l&amp;rsquo;identifier (avec l&amp;rsquo;option &amp;ldquo;name&amp;rdquo;), ici on l&amp;rsquo;appelera &amp;ldquo;q&amp;rdquo;
-on récupère la piste qu&amp;rsquo;on veutavec nom_plugin.nom_propiété, donc d&amp;rsquo;abord la vidéo &amp;ldquo;video_00&amp;rdquo;, puis l&amp;rsquo;audio &amp;ldquo;audio_00&amp;rdquo; (comme indiqué par gst-inspect), via q.video_00 et q.audio_00.
-On dira quel &amp;ldquo;src&amp;rdquo; ira dans quel &amp;ldquo;sink&amp;rdquo;, donc q.video_00 dans ffdec_h264 et q.audio_00 dans ffdec_aac&lt;/p&gt;
&lt;p&gt;Allons-y par étape, je commence par la vidéo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch filesrc location=Portishead\ -\ Wandering\ Star.mp4 ! qtdemux name=q q.video_00 ! ffdec_h264 ! autovideosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Etudiez la ligne:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;filesrc est envoyé à qtdemux -on ne met pas de &amp;ldquo;!&amp;rdquo;, on repart avec un
src&lt;/li&gt;
&lt;li&gt;je pars de q.video_00 qui est une source, et je l&amp;rsquo;envois dans
ffdec_h264&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela fonctionne donc comme les lignes précédentes.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;insiste sur l&amp;rsquo;absence de &amp;ldquo;!&amp;rdquo; entre &amp;ldquo;name=q&amp;rdquo; et &amp;ldquo;q.video_00&amp;rdquo;. Pour
être plus clair, cette commande se découpe en deux pipes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;file -&amp;gt; qtmux nommé &amp;ldquo;q&amp;rdquo;&lt;/li&gt;
&lt;li&gt;q.video_00 -&amp;gt; ffdec -&amp;gt; sortie vidéo&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour ajouter le pipe de son, on va donc mettre un nouveau pipe qui
réutilise &amp;ldquo;q&amp;rdquo;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;q.audio_00 -&amp;gt; ffdec -&amp;gt; sortie sonore&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est à dire:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;... q.audio_00 ! ffdec_aac ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et voici la ligne de commande (qui ne marchera pas encore&amp;hellip; vous allez
comprendre):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch  -v filesrc location=./Portishead\ -\ Wandering\ Star.mp4 ! qtdemux name=q \
q.video_00 ! ffdec_h264 ! autovideosink \
q.audio_00 ! ffdec_aac ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous voyez les 3 pipes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;filesrc -&amp;gt; qtdemux nommé &amp;ldquo;q&amp;rdquo;, &amp;ldquo;q&amp;rdquo; est donc le demultiplexeur&lt;/li&gt;
&lt;li&gt;q.video_00 -&amp;gt; ffdec_h264 -&amp;gt; autovideosink&lt;/li&gt;
&lt;li&gt;q.audio_00 -&amp;gt; ffdec_aac -&amp;gt; autoaudiosink&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Important pour cette section&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Avant de craquer, parce que d&amp;rsquo;une part vous n&amp;rsquo;avez peut-être pas compris
la ligne, et que en plus ça marche toujours pas, lisez tout jusqu&amp;rsquo;au
bout.&lt;/p&gt;
&lt;p&gt;Alors pourquoi ça marche pas ? Et bien le problème c&amp;rsquo;est que là nous
utilisons 2 flux. Il faut créer une file d&amp;rsquo;attente pour que gstreamer
parse un peu les données et puisse gérer le tout en parallèle.&lt;/p&gt;
&lt;p&gt;Explication: gstreamer a pris tout le pipeline et le passe comme on lui
donne&amp;hellip; du coup il tente de lire la vidéo mais l&amp;rsquo;audio est en pause
dans un coin.&lt;/p&gt;
&lt;p&gt;Ce n&amp;rsquo;est pas si compliqué, il faut mettre en &amp;ldquo;queue&amp;rdquo; les différentes
parties du pipeline pour que tout parte en même temps, correctement
callé. Et pour cela, on utiliser un plugin de base nommé &amp;ldquo;queue&amp;rdquo;. Il est
super simple à utiliser, on le calle à des endroits stratégiques pour
définir la mise en fil d&amp;rsquo;attente. On peut en utiliser autant qu&amp;rsquo;on veut.&lt;/p&gt;
&lt;p&gt;Voici la commande que je vous propose:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch  -v filesrc location=./Portishead\ -\ Wandering\ Star.mp4 ! qtdemux name=q \
q.video_00 ! queue ! ffdec_h264 ! autovideosink \
q.audio_00 ! queue ! ffdec_aac ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, gstreamer sait maintenant qu&amp;rsquo;il faut travailler avec une mise en
queue sur chaque src. Et du coup ça fonctionne.&lt;/p&gt;
&lt;p&gt;Notez que là je mets en queue juste avant le décodage, on peut aussi lui
demander de la faire après:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch  -v filesrc location=./Portishead\ -\ Wandering\ Star.mp4 ! qtdemux name=q \
q.video_00 ! ffdec_h264 ! queue ! autovideosink \
q.audio_00 ! ffdec_aac ! queue  ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou carrément avant et après:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch  -v filesrc location=./Portishead\ -\ Wandering\ Star.mp4 ! qtdemux name=q \
q.video_00 ! queue ! ffdec_h264 ! queue ! autovideosink \
q.audio_00 ! queue ! ffdec_aac ! queue  !  autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A vous d&amp;rsquo;optimiser, en testant, on plantant le stream comme une
misérable pomme oubliée en fin de récolte&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ne vous découragez pas&amp;hellip; car là, vous allez adorer la suite: on va
rendre tout ça très très simple !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On va tout simplifier !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Je vous ai parlé du magique &amp;ldquo;decodebin&amp;rdquo;, mais j&amp;rsquo;ai oublié de vous dire
qu&amp;rsquo;en plus il évite les prises de tête pour des recherche de nom de
source etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et ajoutons un point encore plus sympa: pas la peine de donner les nom
de sources, car GStreamer sait quel src doit se racorder au plugin.&lt;/p&gt;
&lt;p&gt;Comme je vous le disais, &amp;ldquo;nom_du_plugin.nom_de_propriété&amp;rdquo; permet
d&amp;rsquo;avoir la propriété nommée &amp;ldquo;nom_de_propriété&amp;rdquo; dans un plugin nommé
&amp;ldquo;nom_du_plugin&amp;rdquo;. Mais ce que je ne vous ai pas dit, c&amp;rsquo;est qu&amp;rsquo;en fait
&amp;ldquo;nom_du_plugin.&amp;rdquo; (n&amp;rsquo;oubliez pas le point) permet de récupérer le
plugin complet. On utilise un point pour ne pas interférer avec les nom
de plugin. Un point en fin de nom veut simplement dire &amp;ldquo;nom que j&amp;rsquo;ai
donné&amp;rdquo;. Donc là, quand je tape &amp;ldquo;qtdemux name=q&amp;rdquo; je pourrais réutiliser
ce demuxer en utilisant &amp;ldquo;q.&amp;rdquo; dans mon pipline.&lt;/p&gt;
&lt;p&gt;Comme gstreamer est cool, qu&amp;rsquo;il sait quoi raccorder en src dans un sink
selon le type de flux, on va simplement donner le nom du demuxer, ça
sera bien plus simple.&lt;/p&gt;
&lt;p&gt;Ce que je vous dit là, c&amp;rsquo;est que le pipeline qu&amp;rsquo;on a créé plus haut pour
demultiplexer un fichier quicktime, trouver les &amp;ldquo;src&amp;rdquo; qui vont bien, les
raccorder à decodeur&amp;hellip; peuvent simplement se faire via:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;decodebin qui va démultiplexer et décoder&lt;/li&gt;
&lt;li&gt;q. pour raccorder les pads au bon endroit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Laissez-moi vous montrer:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch  -v filesrc location=./Portishead\ -\ Wandering\ Star.mp4 ! decodebin name=q \
q. ! queue !  autoaudiosink \ 
q. ! queue ! autovideosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et oui, gstreamer sait quel pad utiliser, le met en queue, et le branche
à la sortie. Mais attendez, on peut simplifier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch  -v filesrc location=./Portishead\ -\ Wandering\ Star.mp4 ! decodebin name=q \ 
! queue !  autoaudiosink \ 
q. ! queue ! autovideosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pas la peine de donner &amp;ldquo;q.&amp;rdquo; au premier branchement, puisque le plugin
(decodebin ici) a des sorties. Inutile de le donner explicitement, le
pipe &amp;ldquo;!&amp;rdquo; branche la sortie de decodebin sur la queue.&lt;/p&gt;
&lt;p&gt;Par contre pour le second branchement (vers l&amp;rsquo;audio) je dois le donner
car autoaudiosink n&amp;rsquo;a pas de sortie (c&amp;rsquo;est un sink).&lt;/p&gt;
&lt;p&gt;Et il existe encore plus simple ! Le plugin &amp;ldquo;playbin&amp;rdquo; qui prend
directement toutes les sorties et les plug dans les sorties audio et
vidéo automatiquement.&lt;/p&gt;
&lt;p&gt;Autre plugin intéressant: uridecodebin. Il permet de récupérer un stream
depuis le disque dur, le net, un périphérique, etc. Exemple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch uridecodebin uri=&amp;quot;http://mirrorblender.top-ix.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_surround.avi&amp;quot; name=u ! autovideosink u. ! autoaudiosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce plugin a d&amp;rsquo;autant plus des options pour régler la taille de tampon,
le déclenchement de mise en buffer, etc.&lt;/p&gt;
&lt;p&gt;On a parlé de décodeur, parlons maintenant d&amp;rsquo;encodage.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée est la même, mais au lieu de brancher la sortie sur la carte son
ou vidéo, on va l&amp;rsquo;envoyer dans un fichier. Il faut donc encoder les
éléments puis les multiplexer pour en faire un seul élément.&lt;/p&gt;
&lt;p&gt;On commence encore une fois par un truc simple, on va encoder un wav en
oggvorbis, la chaine sera:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;filesrc -&amp;gt; decode -&amp;gt; conversion + resample -&amp;gt; encode en vorbis -&amp;gt; écriture dans un fichier (filesink)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On va utiliser les éléments simples, j&amp;rsquo;entends par là que j&amp;rsquo;utiliserai
&amp;ldquo;decodebin&amp;rdquo; pour avoir le flux décodé de mon fichier source afin
d&amp;rsquo;éviter de vous mettre une chaine de décodage complexe. Par contre pour
la sortie en oggvorbis, il est logique de devoir spécifier l&amp;rsquo;encodeur&amp;hellip;
Puisque c&amp;rsquo;est le but.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;filesrc monfichier mp3 -decodebin pour avoir un flux raw&lt;/li&gt;
&lt;li&gt;audioconvert pour avoir un flux converti&lt;/li&gt;
&lt;li&gt;audioresample pour rééchantillonner&lt;/li&gt;
&lt;li&gt;vorbisenc pour encoder au format vorbis&lt;/li&gt;
&lt;li&gt;oggmux pour en faire un fichier au format .ogg -filesink pour écrire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pas mal de plugins, mais c&amp;rsquo;est logique.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-launch -v filesrc location=&amp;quot;Portishead - Roads.mp3&amp;quot; ! decodebin ! \
audioconvert ! \
audioresample ! \
vorbisenc ! \
oggmux ! \
filesink location=out.ogg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&amp;rsquo;est très rapide. Testez le fichier out.ogg et vous verrez que ça
fonctionne bien.&lt;/p&gt;
&lt;p&gt;Bon et bien encodons une vidéo au format ogv ! On va utiliser la même
méthode, c&amp;rsquo;est-à-dire récupérer les sources, encoder, multiplexer le
tout dans un seul fichier, et enfin le déposer dans un fichier.&lt;/p&gt;
&lt;p&gt;Ça va ressembler à ça:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   filesrc (fichier d&#39;entrée)
     |
     |     -(vidéo) -&amp;gt; queue -------&amp;gt; theoraenc --------
     |    /                                             \
 decodebin                                               -&amp;gt; oggmux --+ 
     d.   \                                             /    mux.    |
           -(audio) -&amp;gt; queue -&amp;gt; resample -&amp;gt; vorbisenc --             |
                                                                     |
                                                                     |
+--------------------------------------------------------------------+
|
+---&amp;gt; filesink (fichier de sortie)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avant de lire la commande ci dessous, prenez le temps de comprendre. On
va ouvrir un décodeur qu&amp;rsquo;on nomme &amp;ldquo;d&amp;rdquo;, chaque fois que je vais appeler
&amp;ldquo;d.&amp;rdquo; je récupère un flux que j&amp;rsquo;envois dans un plugin.&lt;/p&gt;
&lt;p&gt;Un multiplexeur (muxer) nommé &amp;ldquo;mux&amp;rdquo; va récupérer les deux sorties, la
vidéo et l&amp;rsquo;audio. On redirige enfin les muxer dans un sink fichier pour
sauver le résultat.&lt;/p&gt;
&lt;p&gt;Je vous le fait pas à pas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je décode la source&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;gst-lauch filesrc location=&amp;quot;video.mp4&amp;quot; decodebin name=d&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je récupère la sortie son, on la place en queue, on resample, on encode
en vorbis et j&amp;rsquo;envois ça dans un muxer ogg&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;d. ! queue ! audioconvert ! audioresample ! vorbisenc  ! oggmux name=mux&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on reprend le flux decodé (d), cette fois la vidéo, on la place en
queue, dans theoraenc, puis envoyée dans le muxer ogg déclaré plus haut&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;d. ! queue ! theoraenc ! mux.&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;je reprend le muxer, j&amp;rsquo;envoi le tout dans un fichier&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;mux. ! filesink location=&amp;quot;out.ogv&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;En une ligne:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-lauch filesrc location=&amp;quot;video.mp4&amp;quot; decodebin name=d \
d. ! queue ! audioconvert ! audioresample ! vorbisenc ! oggmux name=mux \
d. ! queue ! theoraenc ! mux. \
mux. ! filesink location=&amp;quot;out.ogv&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et sans le &amp;ldquo;d.&amp;rdquo; implicite en seconde ligne:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-lauch filesrc location=&amp;quot;video.mp4&amp;quot; ! decodebin name=d \
! queue !audioconvert ! audioresample ! vorbisenc ! oggmux name=mux \
d. ! queue ! theoraenc ! mux. \
mux. ! filesink location=&amp;quot;out.ogv&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Petit conseil: utiliser ffmpegcolorspace pour permettre à Gstreamer de
récuperer le bon espace de couleur avant d&amp;rsquo;encoder, ou décoder une
vidéo&amp;hellip; vous savez, l&amp;rsquo;hitoire des vidéo en RGB, YUV, etc.&lt;/p&gt;
&lt;p&gt;Donc, plus proprement:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gst-lauch filesrc location=&amp;quot;video.mp4&amp;quot; ! decodebin name=d \
! queue ! audioconvert ! audioresample ! vorbisenc ! oggmux name=mux \
d. ! queue ! ffmpegcolorspace ! theoraenc ! mux. \
mux. ! filesink location=&amp;quot;out.ogv&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bon, ça va servir à quoi tout ça ?&lt;/p&gt;
&lt;p&gt;Ce que je me suis efforcé de vous montrer, ce n&amp;rsquo;est pas &amp;ldquo;comment
lire/encoder un fichier audio/vidéo&amp;rdquo; car pour cela vous avez des
lecteurs bien plus simples. L&amp;rsquo;idée et de vous montrer comment fonctionne
un pipeline gstreamer pour s&amp;rsquo;en servir dans du code. Python, C, Java,
etc. Car désormais vous savez sortir un flux vidéo ou audio, et par
conséquent vous allez pouvoir l&amp;rsquo;utiliser. L&amp;rsquo;idée principale étant
d&amp;rsquo;intégrer une vidéo dans un programme.&lt;/p&gt;
&lt;p&gt;Il sera effectivement simple d&amp;rsquo;utiliser un effet vidéo, de le placer
après un stream, d&amp;rsquo;encoder dans différent format, simplement en créant
un pipeline gstreamer.&lt;/p&gt;
&lt;p&gt;Je pense créer un didacticiel python très prochainement pour vous
montrer comment on peut utiliser gstreamer dans un programme Gtk. Mais
pour l&amp;rsquo;heure, essayez de créer des pipeline, regardez du coté du plugin
&amp;ldquo;v4l2src&amp;rdquo; pour capter la caméra (petit conseil, utilisez ximagesink pour
la sortie plutôt que autovideosink), tenter des encodages, revoyez
comment on utilise les nom assigné au plugins (le nom avec un point)
etc. Ne paniquez pas si ça passe pas du premier coup, je passe parfois
un peu de temps à trouver le bon pipeline, je m&amp;rsquo;y reprend à plusieurs
fois, mais à la fin vous serez heureux d&amp;rsquo;avoir un bon pipeline qui vous
permette de faire exactement ce que vous voulez.&lt;/p&gt;
&lt;p&gt;Notez bien que que c&amp;rsquo;est vraiment simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sink = entrée&lt;/li&gt;
&lt;li&gt;src = sortie source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C&amp;rsquo;est tout !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Gnome 3 est pourtant meilleur</title>
      <link>https://www.metal3d.org/blog/2012/gnome-3-est-pourtant-meilleur/</link>
      <pubDate>Sat, 11 Aug 2012 08:03:16 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2012/gnome-3-est-pourtant-meilleur/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Voilà des années que j&amp;rsquo;utilisais Xmonad, un tiling desktop très
intéressant vous permettant l&amp;rsquo;utilisations de terminaux avec aisance.
J&amp;rsquo;étais passé sur cet environnement de fenêtre pour deux raisons:
mémoire mangée par Gnome 2 ou KDE et parce que l&amp;rsquo;interface de Gnome,
XFCE ou encore KDE était lourde visuellement (bureau, icones, barres de
menus, etc. ça m&amp;rsquo;agace moi). Et me revoilà sur Gnome 3 par hasard&amp;hellip; et
je vais me permettre de m&amp;rsquo;opposer aux détracteurs de cet environnement,
car personnellement (et ce contrairement au maître Linus Thorvalds) moi
je trouve Gnome 3 vraiment excellent.&lt;/p&gt;
&lt;p&gt;Voilà l&amp;rsquo;histoire. Je travaille donc sur Xmonad et pour des raisons de
mauvaise gestion de ma carte graphique, je passe d&amp;rsquo;une version ma Fedora
(donc je passe de Fedora 16 à 17). Arrivé sur Xmonad, je lance deux ou
trois softs 3D&amp;hellip; et catastrophe: rien ne passe. Ce n&amp;rsquo;est pas la faute
de Xmonad, loin de là. Une configuration mal passée, je décide de m&amp;rsquo;en
retourner sur une interface plus &amp;ldquo;user friendly&amp;rdquo; pour voir si au moins
Blender ou MakeHuman (dernière version) veulent bien démarrer. Comme je
déteste KDE&amp;hellip; et que Gnome 3 est présent (j&amp;rsquo;avais oublié de l&amp;rsquo;enlever
??) je me retrouve sur ce dernier.&lt;/p&gt;
&lt;p&gt;Tiens donc&amp;hellip; Blender démarre, MakeHuman aussi&amp;hellip; cool ! bon bha je vais
rester sur Gnome 3 (gnome-shell hein) pour bosser un peu, le temps de
chercher pourquoi mon Xmonad a des soucis.&lt;/p&gt;
&lt;p&gt;Quelques jours passent. J&amp;rsquo;ai découvert comment on installait des
extensions sur Gnome 3, je teste Epiphany (le navigateur web de gnome),
j&amp;rsquo;enregistre mes comptes en ligne et je peux chatter directement depuis
des notifications&amp;hellip; attendez mais c&amp;rsquo;est pas mal du tout en fait.&lt;/p&gt;
&lt;p&gt;En plus, Gnome 3 m&amp;rsquo;offre exactement ce que j&amp;rsquo;aime, l&amp;rsquo;inverse de ce que
je retrouve dans les autre environnements: une interface épurée ! Une
barre, une seule, en haut de l&amp;rsquo;écran. Pas de menu, pas de double barre.
L&amp;rsquo;écran &amp;ldquo;Activités&amp;rdquo; est bien pensée (en pressant la super touche du
clavier, ou en posant la souris en haut à gauche de l&amp;rsquo;écran&amp;hellip;). Et des
extensions offre même la possibilité d&amp;rsquo;avoir du &amp;ldquo;tilling desktop&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Je commence à me dire que les détracteurs y sont allé un peu fort sur
Gnome 3&amp;hellip; Il est vraiment simple, utile, ergonomique, facile à
appréhender. Et comme le dit &lt;a href=&#34;http://jeff.ecchi.ca/blog/2011/04/07/gnome-3-0s-ram-usage/&#34; title=&#34;wikilink&#34;&gt;ce
bloggeur&lt;/a&gt;
Gnome 3 prend peu de mémoire finalement. Oui&amp;hellip; 120mo c&amp;rsquo;est pas non plus
super léger&amp;hellip; mais ça l&amp;rsquo;est bien plus que Gnome 2 (je me souviens de
200Mo de RAM utilisé par Gnome 2 en me débrouillant un peu). Alors dire
que Gnome 3 est plus lourd me surprend.&lt;/p&gt;
&lt;p&gt;Loin de moi l&amp;rsquo;idée de comparer Xmonad et Gnome 3. La seule chose que je
veux souligner est l&amp;rsquo;utilisation que j&amp;rsquo;attends d&amp;rsquo;un environnement de
travail. Je déteste littéralement que mon bureau soit surchargé de
fenêtres, de menus, d&amp;rsquo;icones en tout genre. J&amp;rsquo;aime avoir une seule
barre, avec des infos rapides dedans (température du CPU, état de la
batterie du portable, un ou deux raccourcis à la limite&amp;hellip;, date,
heure). Et là, Gnome 3 me donne exactement ça. C&amp;rsquo;est même étonnant à
quel point ma barre Gnome 3 resseble à celle de mon Xmonad :)&lt;/p&gt;
&lt;p&gt;Vraiment, les nostalgiques de Gnome 2 je peux les comprendre. Quand on
aime une interface, on a du mal à passer à autre chose. Mais la
découverte d&amp;rsquo;une autre manière de travailler, sur un environnement (ne
vous en déplaise) plus léger en terme visuel et mémoire, doit tout de
même être étudié. J&amp;rsquo;estime pour ma part que Gnome 3 est bien meilleurs
que XFCE (qui soyez honnête n&amp;rsquo;apporte rien de spécial&amp;hellip;) KDE, Gnome 2
(forké ou non)&amp;hellip; c&amp;rsquo;est un avis personnel. Vous avez entièrement le
droit de me contredire !&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Arkyne le site</title>
      <link>https://www.metal3d.org/blog/2012/arkyne-le-site/</link>
      <pubDate>Fri, 27 Jul 2012 10:21:41 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2012/arkyne-le-site/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Cela faisait des mois que je n&amp;rsquo;avais pas pu travailler sur mon projet
musical. L&amp;rsquo;heure est à la composition, mais aussi à la création du site.
Voici donc &lt;a href=&#34;http://arkyne.net&#34;&gt;http://arkyne.net&lt;/a&gt; réalisé en HTML5.&lt;/p&gt;
&lt;p&gt;Internet Explorer pourra pleurer, tant pis si je perds des &amp;ldquo;visiteurs&amp;rdquo;
sur le site, mais clairement je ne pouvais pas me passer d&amp;rsquo;animation sur
le site. Mettre une vidéo en fond de page et jouer avec les
&amp;ldquo;keyframes&amp;rdquo;&amp;hellip; impossible pour le moment sur les versions &amp;ldquo;répandues&amp;rdquo; de
IE.&lt;/p&gt;
&lt;p&gt;Donc voilà, je me suis lancé sur Blender, et j&amp;rsquo;ai commencé à faire ce
que je voulais. Un arbre qui a un mouvement un peu &amp;ldquo;tordu&amp;rdquo;. Non pas
comme si le vent le poussait, mais comme si il cherchait à s&amp;rsquo;étirer,
puis se relache.&lt;/p&gt;
&lt;p&gt;Quelques nuages, réalisés sur Gimp, puis placé en deux divs animées.&lt;/p&gt;
&lt;p&gt;Ce qui est le plus étonnant dans l&amp;rsquo;histoire, c&amp;rsquo;est que j&amp;rsquo;ai codé ça en
quelques heures, sans vraiment chercher à faire en sorte que ça passe
sur Chromium et Firefox. Je travaillais sur mon vieux vim et je testais
sur Chromium. Puis j&amp;rsquo;ai simplement ajouté les keyframes et transition en
remplaçant les &amp;ldquo;-webkit&amp;rdquo; en &amp;ldquo;-moz&amp;rdquo;&amp;hellip; et ça a marché du premier coup&amp;hellip;&lt;/p&gt;
&lt;p&gt;Ce que j&amp;rsquo;en retiens, c&amp;rsquo;est que Firefox et les navigateurs Webkit ont des
comportement très proches, et par conséquent permettent au développeur
ou designer de se concentrer seulement sur la créa, et non pas sur les
&amp;ldquo;workaround&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Reste que je en sais pas ce que ça rend sur Opera, j&amp;rsquo;ai ajouté les mots
clefs utiles, mais pas encore testé.&lt;/p&gt;
&lt;p&gt;Parlons du projet musique, le prochain album arrive à grands pas. Les
compos se peaufinent, l&amp;rsquo;enregistrement prend un peu de temps, et j&amp;rsquo;ai
encore des voix à replacer. Mais si vous suivez un peu les infos sur
Google+, ou (mon dieu&amp;hellip;) Facebook, ou encore Twitter&amp;hellip; alors vous
aurez la primeur de la nouvelle :)&lt;/p&gt;
&lt;p&gt;Voilà, ce message est certes peu utile, mais comme c&amp;rsquo;est un blog perso,
je dis ce que je veux hein ;)&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Outrepasser un proxy</title>
      <link>https://www.metal3d.org/blog/2012/outrepasser-un-proxy/</link>
      <pubDate>Thu, 12 Jul 2012 10:15:31 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2012/outrepasser-un-proxy/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Me voilà sur un projet Python qui demande une installation via le fameux
bootstrap.py assez intéressant pour automatiser une installation
complète du projet. Or, je me retrouve derrière un proxy http très
restrictif, et python a beaucoup de mal à passer outre. Voici la méthode
qui m&amp;rsquo;a permis de faire mon installation.&lt;/p&gt;
&lt;p&gt;Les prérequis sont:&lt;/p&gt;
&lt;p&gt;-avoir un serveur externe qui permette une connexion SSH -les outils
corkscrew et proxychains -un peu d&amp;rsquo;aspirine&lt;/p&gt;
&lt;p&gt;On commence par configurer le client SSH pour qu&amp;rsquo;il puisse se connecter
en SSH à un serveur externe dont vous avez le contrôle. Ici, on
appellera le serveur externe &amp;ldquo;extserv&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Le serveur proxy HTTP sera nommé ici &amp;ldquo;httpprox&amp;rdquo; et utilise le port 8080.&lt;/p&gt;
&lt;p&gt;Sur le client, donc votre poste, vous devez installer corkscrew et
proxychains:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;su -c &amp;quot;yum install corkscrew proxychains&amp;quot;
ou bien sur ubuntu, parce qu&#39;il y en a plein hein...
sudo aptitude install corkscrew proxychains
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensuite, vous allez éditer le fichier ~/.ssh/config, le but est de dire
que toutes connexion ssh passera par corkscrew via le proxy http.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#fichier ~/.ssh/config
ProxyCommand corkscrew httpprox 8080 %h %p ~/.ssh/auth
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le fichier ~/.ssh/auth doit contenir les informations de connexion au
proxy http:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#fichier ~/.ssh/auth
VotreLoginProxy:VotreMotDePasseProxy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, tentons une connexion au serveur SSH:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh user@extserv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parfait, je suis connecté à mon serveur ssh. Je modifie deux options sur
le serveur, dans le fichier /etc/ssh/sshd_config:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AllowTcpForwarding yes
PermitTunnel yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si les options n&amp;rsquo;existent pas dans le fichier, ajoutez les simplement&amp;hellip;
Redémarrez le service sshd du serveur:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/etc/init.d/sshd restart
ou sur debian, ubuntu...
/etc/init.d/ssh restart
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Désormais, on peut créer un tunnel vers notre serveur.&lt;/p&gt;
&lt;p&gt;Créons maintenant un proxy SOCK5 sur le client. Donc fermez votre
connexion SSH au serveur et tapez simplement:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -N -D 8888 user@extserv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La commande se bloque après demande du mot de passe (si vous n&amp;rsquo;avez pas
utilisé d&amp;rsquo;authentification par clefs). Le port 8888 local écoute
gentillement.&lt;/p&gt;
&lt;p&gt;Gardez ce terminal dans un coin, ne le fermez pas, et ouvrez un autre
terminal.&lt;/p&gt;
&lt;p&gt;Dans ce second terminal, paramétrons proxychains. Editez
/etc/proxychains.conf. Vous devez trouver deux options. La première est
proxy_dns. Mettez la en commentaire:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#proxy_dns
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puis, normalement en bas du fichier, vous trouverez une ligne &amp;ldquo;sock4
&amp;hellip;&amp;rdquo; supprimez la, et mettez à la place:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sock5  127.0.0.1 8888
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà, vous sauvez le fichier, vous quitter et vous tentez:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#plus besoin du proxy http
unset http_proxy https_proxy

#on essait de lire une page web
proxychains wget -q http://www.google.fr -O -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;si tout se passe bien, vous aurez sur le terminal la sortie HTML de
google.fr, sinon, vérifiez vos paramètres de proxy, de ssh, etc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Vous l&amp;rsquo;avez compris, en préfixant la commande par &amp;ldquo;proxychains&amp;rdquo; vous
forcez les connexions à passer par le proxy local &amp;ldquo;127.0.0.1:8888&amp;rdquo; qui
passe ensuite par corkscrew, puis le proxy http, enfin vers votre
serveur SSH qui redirige tout vers le serveur demandé. En gros:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;connexion A ==&amp;gt; proxychains =&amp;gt; 127.0.0.1:8888 =&amp;gt; httpprox =&amp;gt; extserv =&amp;gt; A
            &amp;lt;==             &amp;lt;=               &amp;lt;=           &amp;lt;=         &amp;lt;=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà&amp;hellip; et bien lançons simplement la commande, en ayant pris soin de
supprimer les variables d&amp;rsquo;environnement &amp;ldquo;http_proxy&amp;rdquo; et &amp;ldquo;https_proxy&amp;rdquo;
via la commande unset&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unset http_proxy https_proxy
proxychains python bootstrap.py
proxychains ./bin/buildout
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;toutes les commandes nécessitant une connexion devront être préfixées
par proxychains.&lt;/p&gt;
&lt;p&gt;Notez enfin que git utilise SSH, et que par conséquent vous pouvez
désormais l&amp;rsquo;utiliser avec une configuration remote &amp;ldquo;ssh&amp;rdquo; et non plus
&amp;ldquo;https&amp;rdquo;&amp;hellip; Bref, proxychains + corkscrew, ça peut vraiment vous aider :)&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Certificat SSL et Chrome ou Chromium</title>
      <link>https://www.metal3d.org/blog/2012/certificat-ssl-et-chrome-ou-chromium/</link>
      <pubDate>Thu, 21 Jun 2012 07:43:29 +0000</pubDate>
      
      <guid>https://www.metal3d.org/blog/2012/certificat-ssl-et-chrome-ou-chromium/</guid>
      <description>
        
        
               &lt;blockquote&gt;&lt;p&gt;Si vous aimez mon contenu, vous pouvez me fournir un peu de calcul en lançant le conteneur&lt;/p&gt;&lt;p&gt;&lt;code&gt;docker run --rm -it metal3d/xmrig&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Cela lance un conteneur de minage léger, qui ne consomme que 50% de CPU &lt;strong&gt;non utilisé&lt;/strong&gt;. &lt;br /&gt; CTRL+C pour stopper.&lt;/p&gt;&lt;p&gt;Faites cela le temps de lire l&#39;article, c&#39;est un genre de don, mais pour geeks 😇&lt;/p&gt;&lt;/blockquote&gt;
            &lt;p&gt;Vous devez avoir cherché pendant des lustres la manière de faire en
sorte d&amp;rsquo;éviter un écran rouge sur les page SSL (HTTPS) en enregistrant
le certificat dans votre navigateur&amp;hellip; Voici une méthode assez simple&amp;hellip;&lt;/p&gt;
&lt;p&gt;Parce que franchement, la méthode &amp;ldquo;certutils&amp;rdquo; proposé sur différents
sites me parait assez complexe, j&amp;rsquo;ai cherché une autre méthode en
grattant un peu partout dans la configuration.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;idée est simple&amp;hellip; on enregistre le certificat en partant de
l&amp;rsquo;autorité de contrôle, puis on l&amp;rsquo;installe dans les paramètres. En fait
ça parait simple, mais fallait juste le savoir&lt;/p&gt;
&lt;p&gt;Je vous propose une vidéo (sans son) qui vous montre la méthode. Retenez
juste:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;séléctionnez bien l&amp;rsquo;entrée qui est défini comme CA (Control Authority)&lt;/li&gt;
&lt;li&gt;le fichier enregistré pourra être supprimé après insertion dans Chrome&lt;/li&gt;
&lt;li&gt;dans les paramètres, insérez l&amp;rsquo;autorité dans l&amp;rsquo;onglet &amp;ldquo;autorités&amp;rdquo; et
non &amp;ldquo;serveur&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;html&gt;
&lt;iframe width=&#34;420&#34; height=&#34;315&#34; src=&#34;http://www.youtube.com/embed/P7gC4OkSxmw&#34; frameborder=&#34;0&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/html&gt;
&lt;p&gt;Cette méthode marche très bien pour ma part&amp;hellip; si vous avez des
remarques&amp;hellip; n&amp;rsquo;hésitez pas.&lt;/p&gt;

      </description>
    </item>
    
  </channel>
</rss>
