<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GitLab CI on Matteo ZINUTTI</title><link>https://mzinutti.fr/categories/gitlab-ci/</link><description>Recent content in GitLab CI on Matteo ZINUTTI</description><generator>Hugo -- gohugo.io</generator><language>fr-fr</language><lastBuildDate>Mon, 11 May 2026 16:15:36 +0200</lastBuildDate><atom:link href="https://mzinutti.fr/categories/gitlab-ci/index.xml" rel="self" type="application/rss+xml"/><item><title>Garantir la qualité des Helm Charts</title><link>https://mzinutti.fr/posts/helm-chart-quality-pipeline/</link><pubDate>Mon, 11 May 2026 14:00:00 +0000</pubDate><guid>https://mzinutti.fr/posts/helm-chart-quality-pipeline/</guid><description>&lt;img src="https://mzinutti.fr/posts/helm-chart-quality-pipeline/img/helm-flow.svg" alt="Featured image of post Garantir la qualité des Helm Charts" /&gt;&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;En tant que SRE / Platform Engineer, l&amp;rsquo;une de mes responsabilités est de fournir des Helm charts fiables aux équipes de développement. Ces charts constituent la fondation du déploiement de leurs applications sur nos plateformes Kubernetes. Un chart défaillant, ce sont des d&amp;rsquo;applications impactées, des heures d&amp;rsquo;investigation, et une confiance érodée sur les composants mis à disposition.&lt;/p&gt;
&lt;p&gt;Le déclencheur de cette réflexion vient d&amp;rsquo;un incident évitable, avec l&amp;rsquo;introduction de &lt;em&gt;breaking changes&lt;/em&gt; non documentés qui ont cassé tous les déploiements utilisant ce chart, et nous a forcés à nous poser une question fondamentale : &lt;strong&gt;comment garantir la qualité des helm charts que nous fournissons ?&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="contexte"&gt;Contexte
&lt;/h2&gt;&lt;h3 id="le-problème"&gt;Le problème
&lt;/h3&gt;&lt;p&gt;Gérer des Helm charts manuellement et sans validation automatisée présente plusieurs risques :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Breaking changes non documentés&lt;/strong&gt; : Des modifications incompatibles peuvent être introduites sans que les équipes utilisatrices en soient informées.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Erreurs de syntaxe&lt;/strong&gt; : Des fautes de frappe ou des erreurs YAML peuvent passer inaperçues jusqu&amp;rsquo;au déploiement en production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Templates invalides&lt;/strong&gt; : Des templates qui ne se rendent pas correctement ou génèrent des manifestes Kubernetes invalides.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Régression fonctionnelle&lt;/strong&gt; : Des modifications qui cassent des fonctionnalités existantes sans être détectées.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Absence de traçabilité&lt;/strong&gt; : Difficulté à comprendre l&amp;rsquo;historique des changements et leur impact.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="lobjectif"&gt;L&amp;rsquo;objectif
&lt;/h3&gt;&lt;p&gt;J&amp;rsquo;avais donc besoin d&amp;rsquo;un cadre capable de valider automatiquement chaque modification avant son intégration dans la branche principale, de signaler explicitement les &lt;em&gt;breaking changes&lt;/em&gt;, de vérifier la conformité Kubernetes des manifestes générés de tester les comportements attendus et de tracer les évolutions dans un &lt;em&gt;changelog&lt;/em&gt; exploitable.&lt;/p&gt;
&lt;p&gt;Mais il ne s&amp;rsquo;agissait pas seulement d&amp;rsquo;ajouter des outils. La réponse devait aussi être organisationnelle : mieux cadrer la contribution, rendre la collaboration plus lisible, et faire en sorte que la qualité du chart ne repose plus uniquement sur l&amp;rsquo;attention individuelle.&lt;/p&gt;
&lt;h2 id="composants"&gt;Composants
&lt;/h2&gt;&lt;p&gt;Le workflow s&amp;rsquo;articule autour de plusieurs outils et pratiques :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitLab CI/CD&lt;/strong&gt; : Orchestre l&amp;rsquo;ensemble du pipeline de validation et de publication.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Helm &amp;amp; Plugins&lt;/strong&gt; : Gestionnaire de packages pour Kubernetes, avec ses outils natifs de validation et ses plugins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git Flow&lt;/strong&gt; : Stratégie de branches et processus de Merge Request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SemVer&lt;/strong&gt; : Gestion des versions pour communiquer clairement les types de changements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep a Changelog&lt;/strong&gt; : Format standardisé pour documenter tous les changements.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="architecture-du-pipeline"&gt;Architecture du Pipeline
&lt;/h2&gt;&lt;p&gt;Le pipeline GitLab CI est organisé en trois stages principaux, chacun avec un objectif spécifique.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://mzinutti.fr/posts/helm-chart-quality-pipeline/img/helm-flow.svg"
 alt="pipeline-overview" width="800px"&gt;&lt;figcaption&gt;
 &lt;h4&gt;Vue d&amp;#39;ensemble du pipeline&lt;/h4&gt;
 &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="linting"&gt;Linting
&lt;/h3&gt;&lt;p&gt;Le premier niveau de validation dans le pipeline vérifie la syntaxe et la structure globale du chart via la commande &lt;code&gt;helm lint&lt;/code&gt; native à helm. Elle permet d&amp;rsquo;avoir un retour rapide et immédiatement compréhensible, bien qu&amp;rsquo;elle ne couvre pas tous les cas, elle élimine très tôt les erreurs grossières qui ne devraient jamais atteindre les étapes suivantes et évite donc de lancer des validations plus poussées et donc de perdre du temps.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm lint .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote class="alert alert-note"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;📝&lt;/span&gt;
 &lt;span class="alert-title"&gt;Note&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;&lt;strong&gt;Ce que Helm lint vérifie :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Syntaxe YAML correcte&lt;/li&gt;
&lt;li&gt;Structure du chart (Chart.yaml, templates/, etc.)&lt;/li&gt;
&lt;li&gt;Problèmes de formatage&lt;/li&gt;
&lt;li&gt;Warnings sur les bonnes pratiques&lt;/li&gt;
&lt;/ul&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;p&gt;Une fois que l&amp;rsquo;on s&amp;rsquo;est assuré de la bonne structure du chart, on peut passer aux étapes de validation plus spécifiques.&lt;/p&gt;
&lt;h3 id="validation-du-templating"&gt;Validation du templating
&lt;/h3&gt;&lt;p&gt;Pour répondre à la question : est-ce que le chart se rend réellement correctement avec les valeurs fournies ? J&amp;rsquo;ai retenu &lt;code&gt;helm template&lt;/code&gt; qui va permettre d&amp;rsquo;effectuer le rendu des templates, c&amp;rsquo;est souvent à ce niveau que l&amp;rsquo;on découvre des variables manquantes, des conditions mal écrites, ou des erreurs dans les fonctions Go Template.&lt;/p&gt;
&lt;p&gt;Le fonctionnement est simple : Helm interprète les templates et produit les manifestes Kubernetes finaux, sans les appliquer sur un cluster. On obtient donc une vue concrète de ce qui serait déployé, ce qui en fait une étape particulièrement utile pour détecter les erreurs de rendu au plus tôt.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;helm template .&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote class="alert alert-note"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;📝&lt;/span&gt;
 &lt;span class="alert-title"&gt;Note&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;&lt;strong&gt;Ce que Helm template vérifie :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rendu correct des templates Go (variables, fonctions, pipelines)&lt;/li&gt;
&lt;li&gt;Résolution des valeurs et des helpers définis dans &lt;code&gt;_helpers.tpl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Syntaxe correcte des conditions &lt;code&gt;{{- if }}&lt;/code&gt; et des boucles &lt;code&gt;{{- range }}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Absence de variables non définies ou mal référencées&lt;/li&gt;
&lt;/ul&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;h3 id="conformité-des-schémas"&gt;Conformité des schémas
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;helm template&lt;/code&gt; garantit que le rendu fonctionne, mais pas que le résultat soit acceptable pour Kubernetes. C&amp;rsquo;est précisément pour cette raison que j&amp;rsquo;ai choisi d&amp;rsquo;utiliser &lt;a class="link" href="https://github.com/yannh/kubeconform" target="_blank" rel="noopener"
 &gt;Kubeconform&lt;/a&gt;. Cet outil a été retenu parce qu&amp;rsquo;il est rapide, simple à intégrer dans une CI, et très efficace pour valider les manifestes contre des schémas connus.&lt;/p&gt;
&lt;p&gt;Son principe est de comparer les ressources générées aux schémas Kubernetes attendus. Il ne se contente donc pas de vérifier que le YAML est bien formé : il valide aussi la structure métier du manifeste, les champs autorisés, les types attendus et certaines incompatibilités de version. Avec l&amp;rsquo;ajout du &lt;a class="link" href="https://github.com/datreeio/crds-catalog" target="_blank" rel="noopener"
 &gt;catalogue de CRDs&lt;/a&gt;, il reste utile même dans des environnements Kubernetes enrichis par des opérateurs.&lt;/p&gt;
&lt;p&gt;À l&amp;rsquo;origine, cet outil est un binaire mais un plugin helm a été fait pour pouvoir être intégrer directement dans la CLI de helm.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm plugin install https://github.com/jtyr/kubeconform-helm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm kubeconform .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Configuration de Kubeconform (.kubeconform) :&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;schema-location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Default K8s resources schema&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;ignore-missing-schemas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Don&amp;#39;t fail on resources where schema is not found.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote class="alert alert-note"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;📝&lt;/span&gt;
 &lt;span class="alert-title"&gt;Note&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;&lt;strong&gt;Ce que kubeconform vérifie :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Champs invalides ou dépréciés&lt;/li&gt;
&lt;li&gt;Types de données incorrects&lt;/li&gt;
&lt;li&gt;Valeurs manquantes pour les champs requis&lt;/li&gt;
&lt;li&gt;Problèmes de compatibilité avec la version Kubernetes ciblée&lt;/li&gt;
&lt;/ul&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Exemple de sortie :&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;stdin - Secret mariadb-cluster is valid
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;stdin - StatefulSet mariadb-cluster is valid
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;stdin - Cluster pg-cluster is valid
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;stdin - MongoDBCommunity mongodb-cluster is valid
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Summary: 17 resources found parsing stdin - Valid: 17, Invalid: 0, Errors: 0, Skipped: 0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="tests-unitaires"&gt;Tests unitaires
&lt;/h3&gt;&lt;p&gt;J&amp;rsquo;ai ajouté &lt;code&gt;Helm Unittest&lt;/code&gt; parce qu&amp;rsquo;au-delà de la syntaxe et de la conformité, il fallait protéger les comportements fonctionnels du chart. C&amp;rsquo;est l&amp;rsquo;outil qui me permet de formaliser des attentes explicites : un nom doit suivre une convention, une ressource doit exister dans un cas donné, une image doit être construite avec telle valeur, etc.&lt;/p&gt;
&lt;p&gt;Le principe est proche d&amp;rsquo;un framework de test classique, mais appliqué au rendu Helm. On décrit un jeu de valeurs d&amp;rsquo;entrée, puis on exprime des assertions sur le manifeste produit. Cette approche est particulièrement utile pour éviter les régressions silencieuses lorsqu&amp;rsquo;un chart évolue et commence à gérer plusieurs variantes de configuration.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;helm plugin install https://github.com/helm-unittest/helm-unittest.git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;helm unittest .&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Les tests unitaires permettent de vérifier les comportements attendus du chart dans différentes configurations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemple de test (tests/deployment_test.yaml) :&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;test deployment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;templates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;deployment.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;it&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;should create a valid deployment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image.tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;asserts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;isKind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;of&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Deployment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;matchRegex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;metadata.name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="l"&gt;my-chart$&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;spec.template.spec.containers[0].image&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nginx:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote class="alert alert-note"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;📝&lt;/span&gt;
 &lt;span class="alert-title"&gt;Note&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;Ces tests vérifient :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les valeurs par défaut produisent des manifestes valides&lt;/li&gt;
&lt;li&gt;Les différentes configurations fonctionnent correctement&lt;/li&gt;
&lt;li&gt;Les comportements critiques sont préservés (non-régression)&lt;/li&gt;
&lt;/ul&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;h3 id="release-management"&gt;Release Management
&lt;/h3&gt;&lt;p&gt;La publication n&amp;rsquo;est déclenchée que lors de la création d&amp;rsquo;un tag Git sur la branche &lt;code&gt;main&lt;/code&gt;. Ce processus garantit que seules les versions taggées sont publiées, permettant d&amp;rsquo;accumuler plusieurs modifications avant de créer une release.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Point crucial : Le CHANGELOG&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Chaque Merge Request &lt;strong&gt;doit&lt;/strong&gt; inclure une mise à jour du &lt;code&gt;CHANGELOG.md&lt;/code&gt;. Ce fichier suit le format &lt;a class="link" href="https://keepachangelog.com/en/1.0.0/" target="_blank" rel="noopener"
 &gt;Keep a Changelog&lt;/a&gt; et utilise le &lt;a class="link" href="https://semver.org/" target="_blank" rel="noopener"
 &gt;Semantic Versioning&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## [1.0.10] - 2026-01-26
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### Added
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Resource limits and requests for all containers
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### Changed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Updated default memory limit to 512Mi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[1.0.10]: https://gitlab.com/project/compare/1.0.9...1.0.10
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Catégories disponibles :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; : Nouvelles fonctionnalités&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; : Modifications dans les fonctionnalités existantes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fixed&lt;/strong&gt; : Corrections de bugs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Removed&lt;/strong&gt; : Fonctionnalités supprimées&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="alert alert-important"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;📌&lt;/span&gt;
 &lt;span class="alert-title"&gt;Important&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;Si une modification introduit un breaking change, cela doit être :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clairement indiqué dans le CHANGELOG&lt;/li&gt;
&lt;li&gt;Expliqué dans la description de la Merge Request&lt;/li&gt;
&lt;li&gt;Accompagné d&amp;rsquo;un guide de migration si nécessaire&lt;/li&gt;
&lt;/ol&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;h3 id="semantic-versioning"&gt;Semantic Versioning
&lt;/h3&gt;&lt;p&gt;Nous suivons strictement le Semantic Versioning (MAJOR.MINOR.PATCH) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MAJOR&lt;/strong&gt; : Breaking changes (incompatibilité avec les versions précédentes)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MINOR&lt;/strong&gt; : Nouvelles fonctionnalités rétrocompatibles&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PATCH&lt;/strong&gt; : Corrections de bugs rétrocompatibles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Exemple :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1.0.9&lt;/code&gt; → &lt;code&gt;1.0.10&lt;/code&gt; : Ajout de resource limits (rétrocompatible) → MINOR&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1.0.10&lt;/code&gt; → &lt;code&gt;2.0.0&lt;/code&gt; : Changement de structure du values.yaml (breaking) → MAJOR&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="bénéfices-observés"&gt;Bénéfices Observés
&lt;/h2&gt;&lt;p&gt;Depuis la mise en place de ce workflow, on a pu constater :&lt;/p&gt;
&lt;h3 id="qualité"&gt;Qualité
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zéro breaking change non documenté&lt;/strong&gt; depuis 6 mois&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Réduction de 90% des erreurs&lt;/strong&gt; de déploiement liées aux charts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Détection précoce&lt;/strong&gt; des problèmes (avant la revue de code)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="collaboration"&gt;Collaboration
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Processus clair&lt;/strong&gt; pour tous les contributeurs (juniors et seniors)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Revues de code&lt;/strong&gt; plus efficaces (focus sur la logique, pas la syntaxe)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Traçabilité&lt;/strong&gt; complète des changements via le CHANGELOG&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="confiance"&gt;Confiance
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Les équipes de développement ont &lt;strong&gt;confiance&lt;/strong&gt; dans la stabilité des charts&lt;/li&gt;
&lt;li&gt;Les updates sont &lt;strong&gt;moins redoutées&lt;/strong&gt; car le processus est transparent&lt;/li&gt;
&lt;li&gt;Les rollbacks sont &lt;strong&gt;facilités&lt;/strong&gt; par le versioning clair&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="productivité"&gt;Productivité
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Réduction du temps&lt;/strong&gt; de validation manuelle&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatisation&lt;/strong&gt; de tâches répétitives&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focus&lt;/strong&gt; sur la valeur ajoutée plutôt que sur la vérification de syntaxe&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="diagramme-de-séquence"&gt;Diagramme de Séquence
&lt;/h2&gt;&lt;figure&gt;&lt;img src="https://mzinutti.fr/posts/helm-chart-quality-pipeline/img/flux-diagram.svg"
 alt="sequence-diagram" width="600px"&gt;&lt;figcaption&gt;
 &lt;h4&gt;Diagramme de séquence complet&lt;/h4&gt;
 &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="points-dattention"&gt;Points d&amp;rsquo;Attention
&lt;/h2&gt;&lt;p&gt;Quelques leçons apprises lors de la mise en place :&lt;/p&gt;
&lt;h3 id="tests-unitaires-complets"&gt;Tests Unitaires Complets
&lt;/h3&gt;&lt;p&gt;Ne testez pas uniquement les cas nominaux. Incluez :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les configurations par défaut&lt;/li&gt;
&lt;li&gt;Les cas limites&lt;/li&gt;
&lt;li&gt;Les différentes combinaisons de valeurs&lt;/li&gt;
&lt;li&gt;Les scénarios de mise à jour&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="documentation-du-changelog"&gt;Documentation du CHANGELOG
&lt;/h3&gt;&lt;p&gt;Le CHANGELOG est &lt;strong&gt;le contrat&lt;/strong&gt; avec les utilisateurs. Il doit être :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complet&lt;/strong&gt; : Tous les changements doivent être documentés&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clair&lt;/strong&gt; : Langage compréhensible, pas de jargon technique inutile&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Actionnable&lt;/strong&gt; : Pour les breaking changes, indiquer quoi faire&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="protection-de-la-branche-main"&gt;Protection de la Branche Main
&lt;/h3&gt;&lt;p&gt;La protection de &lt;code&gt;main&lt;/code&gt; doit être stricte :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pas de commit direct&lt;/li&gt;
&lt;li&gt;Pipeline obligatoire&lt;/li&gt;
&lt;li&gt;Approbation obligatoire&lt;/li&gt;
&lt;li&gt;Le CHANGELOG doit être à jour &lt;strong&gt;avant&lt;/strong&gt; le merge&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="tags-protégés"&gt;Tags Protégés
&lt;/h3&gt;&lt;p&gt;Restreindre la création de tags aux Maintainers pour éviter les publications accidentelles.&lt;/p&gt;
&lt;h3 id="communication"&gt;Communication
&lt;/h3&gt;&lt;p&gt;Lors d&amp;rsquo;un breaking change, même bien documenté :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Notifier les équipes &lt;strong&gt;en avance&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Fournir un guide de migration&lt;/li&gt;
&lt;li&gt;Offrir du support pendant la transition&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="améliorations-futures"&gt;Améliorations Futures
&lt;/h2&gt;&lt;p&gt;Nous envisageons d&amp;rsquo;ajouter :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tests de non-régression&lt;/strong&gt; : Comparer les manifestes générés entre versions avec &lt;code&gt;helm diff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security scanning&lt;/strong&gt; : Intégrer des outils comme Trivy pour détecter les vulnérabilités&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Policy enforcement&lt;/strong&gt; : Utiliser des tools comme OPA ou Kyverno pour valider les policies de sécurité&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring des déploiements&lt;/strong&gt; : Suivre l&amp;rsquo;adoption des nouvelles versions&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;La mise en place d&amp;rsquo;un pipeline de validation rigoureux pour nos Helm charts a transformé notre manière de travailler. L&amp;rsquo;incident qui nous a poussés à implémenter ce workflow est désormais impossible grâce aux multiples niveaux de validation.&lt;/p&gt;
&lt;p&gt;Ce processus garantit non seulement la qualité technique des charts, mais établit également un contrat clair avec les équipes de développement : elles peuvent faire confiance aux charts que nous fournissons, et lorsqu&amp;rsquo;un breaking change est nécessaire, il est clairement documenté et communiqué.&lt;/p&gt;
&lt;p&gt;Pour une équipe SRE, fournir une plateforme fiable est primordial. Ce workflow nous permet de tenir cette promesse tout en gardant la capacité d&amp;rsquo;évoluer et d&amp;rsquo;améliorer continuellement nos charts.&lt;/p&gt;
&lt;p&gt;Si vous gérez des Helm charts pour une plateforme Kubernetes, je vous encourage vivement à mettre en place un processus similaire. L&amp;rsquo;investissement initial est rapidement rentabilisé par la réduction des incidents et l&amp;rsquo;amélioration de la collaboration.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Ressources utiles :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://helm.sh/docs/" target="_blank" rel="noopener"
 &gt;Helm Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/yannh/kubeconform" target="_blank" rel="noopener"
 &gt;Kubeconform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/helm-unittest/helm-unittest" target="_blank" rel="noopener"
 &gt;Helm Unittest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://keepachangelog.com/" target="_blank" rel="noopener"
 &gt;Keep a Changelog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://semver.org/" target="_blank" rel="noopener"
 &gt;Semantic Versioning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>