<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ci on FtBx.fr</title><link>https://ftbx.fr/tags/ci/</link><description>Recent content in Ci on FtBx.fr</description><image><title>FtBx.fr</title><url>https://ftbx.fr/images/placeholder-image.jpg</url><link>https://ftbx.fr/images/placeholder-image.jpg</link></image><generator>Hugo -- 0.146.6</generator><language>fr</language><copyright>2025 ftbx.fr - This work is licensed under CC BY-NC-SA 4.0</copyright><lastBuildDate>Wed, 03 Sep 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://ftbx.fr/tags/ci/index.xml" rel="self" type="application/rss+xml"/><item><title>GitLab CI parallel:matrix : les needs individuels sont enfin là</title><link>https://ftbx.fr/posts/gitlab-ci-parallel-matrix-es-needs-individuels-sont-enfin-e/</link><pubDate>Wed, 03 Sep 2025 00:00:00 +0000</pubDate><guid>https://ftbx.fr/posts/gitlab-ci-parallel-matrix-es-needs-individuels-sont-enfin-e/</guid><description>&lt;p>Avant, &lt;code>parallel:matrix&lt;/code> permettait de lancer la même tâche sur
plusieurs versions. Mais impossible de référencer UN job spécifique
de la matrice dans un &lt;code>needs&lt;/code>. Toute la matrice formait un bloc
indivisible.&lt;/p>
&lt;p>GitLab 16.7 a changé ça. Maintenant, tu peux &lt;code>needs&lt;/code> un job précis
de la matrice. Et ça débloque des pipelines plus propres.&lt;/p>
&lt;h3 id="avant--le-workaround">Avant : le workaround&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># ❌ Avant GitLab 16.7 : impossible de référencer un job de la matrice&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">parallel&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">PYTHON&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;3.9&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;3.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;3.11&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pytest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">needs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Attend TOUS les jobs de la matrice&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">./deploy.sh&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="après--des-needs-chirurgicaux">Après : des needs chirurgicaux&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># ✅ GitLab 16.7+ : on cible un job précis&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">parallel&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">PYTHON&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;3.9&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;3.10&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;3.11&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pytest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">deploy_39&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">needs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;test: [3.9]&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">./deploy-py39.sh&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">deploy_latest&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">needs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;test: [3.11]&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">./deploy-pylatest.sh&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Le job &lt;code>deploy_39&lt;/code> attend UNIQUEMENT &lt;code>test: [3.9]&lt;/code>. Il n&amp;rsquo;attend pas
les tests Python 3.10 et 3.11. Résultat : le déploiement peut partir
dès que sa version est validée, sans attendre les autres.&lt;/p></description></item><item><title>GitLab CI resource_group : empêcher deux déploiements concurrents</title><link>https://ftbx.fr/posts/gitlab-ci-resource-group-empecher-deux-deploiements-concurrents/</link><pubDate>Wed, 15 Jan 2025 00:00:00 +0000</pubDate><guid>https://ftbx.fr/posts/gitlab-ci-resource-group-empecher-deux-deploiements-concurrents/</guid><description>&lt;p>Quand deux pipelines déploient sur le même environnement en parallèle,
le deuxième écrase le premier. Résultat : état incohérent, erreurs
de connexion, rollback en catastrophe.&lt;/p>
&lt;p>&lt;code>resource_group&lt;/code> règle ça proprement. Tu assignes un nom de groupe à
un job de déploiement. GitLab garantit qu&amp;rsquo;un seul job par groupe
s&amp;rsquo;exécute à la fois. Les autres attendent.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">deploy_staging&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">stage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">deploy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">./deploy.sh staging&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">staging&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resource_group&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">staging&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Si deux pipelines arrivent en même temps sur &lt;code>deploy_staging&lt;/code>, le
premier s&amp;rsquo;exécute, le deuxième attend que le premier finisse. Pas
de lock manuel, pas de vérification Slack &amp;ldquo;qui déploie ?&amp;rdquo;.&lt;/p></description></item><item><title>GitLab CI coverage regex : pourquoi ta regex ne matche jamais</title><link>https://ftbx.fr/posts/gitlab-ci-coverage-regex-pourquoi-ta-regex-ne-matche-jamais/</link><pubDate>Mon, 08 Apr 2024 00:00:00 +0000</pubDate><guid>https://ftbx.fr/posts/gitlab-ci-coverage-regex-pourquoi-ta-regex-ne-matche-jamais/</guid><description>&lt;p>GitLab peut extraire le pourcentage de couverture de tests depuis les
logs du job et l&amp;rsquo;afficher dans le badge du projet, les MR, et l&amp;rsquo;UI.
Il suffit de définir une regex dans &lt;code>coverage:&lt;/code>. Et c&amp;rsquo;est là que tout
le monde se plante.&lt;/p>
&lt;h3 id="la-regex-qui-foire-systématiquement">La regex qui foire systématiquement&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># ❌ Ne matchera JAMAIS&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">job&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pytest --cov&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">coverage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;/TOTAL.*\s+(\d+%)$/&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Pourquoi ? &lt;code>$&lt;/code> veut dire &amp;ldquo;fin de ligne&amp;rdquo;. Mais les logs GitLab
sont collectés dans un buffer multi-lignes. La vraie fin de ligne
dans ce buffer, c&amp;rsquo;est la dernière ligne du log entier, pas la
ligne &lt;code>TOTAL&lt;/code>.&lt;/p></description></item><item><title>gitlab-ci-local : tester ses pipelines sans pousser</title><link>https://ftbx.fr/posts/gitlab-ci-local-tester-ses-pipelines-pousser/</link><pubDate>Fri, 08 Mar 2024 00:00:00 +0000</pubDate><guid>https://ftbx.fr/posts/gitlab-ci-local-tester-ses-pipelines-pousser/</guid><description>&lt;p>Pousser un commit juste pour tester un changement de &lt;code>.gitlab-ci.yml&lt;/code>,
c&amp;rsquo;est 30 secondes par tentative. Sur une pipeline un peu tordue,
20 essais. 10 minutes perdues à corriger des erreurs de syntaxe.&lt;/p>
&lt;p>&lt;code>gitlab-ci-local&lt;/code> exécute tes pipelines en local, avec Docker.
Tu modifies ton &lt;code>.gitlab-ci.yml&lt;/code>, tu lances &lt;code>gitlab-ci-local&lt;/code>, tu vois
les erreurs immédiatement. Zéro push, zéro attente de runner.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Installer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">npm install -g gitlab-ci-local
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Lancer tous les jobs du .gitlab-ci.yml courant&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gitlab-ci-local
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Lancer un job spécifique&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gitlab-ci-local --job build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Lister les jobs disponibles&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gitlab-ci-local --list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="ce-que-ça-supporte">Ce que ça supporte&lt;/h3>
&lt;ul>
&lt;li>&lt;code>image&lt;/code>, &lt;code>services&lt;/code>, &lt;code>before_script&lt;/code>, &lt;code>script&lt;/code>, &lt;code>after_script&lt;/code>&lt;/li>
&lt;li>&lt;code>artifacts&lt;/code>, &lt;code>cache&lt;/code> (local, pas partagé)&lt;/li>
&lt;li>&lt;code>variables&lt;/code>, &lt;code>extends&lt;/code>, &lt;code>!reference&lt;/code>, &lt;code>needs&lt;/code>&lt;/li>
&lt;li>&lt;code>parallel:matrix&lt;/code> (partiellement)&lt;/li>
&lt;/ul>
&lt;h3 id="ce-que-ça-ne-supporte-pas">Ce que ça NE supporte PAS&lt;/h3>
&lt;ul>
&lt;li>&lt;code>trigger&lt;/code> (pipelines enfants)&lt;/li>
&lt;li>&lt;code>environment&lt;/code> (pas de déploiement réel)&lt;/li>
&lt;li>&lt;code>rules:if&lt;/code> avec des variables GitLab prédéfinies (&lt;code>$CI_COMMIT_BRANCH&lt;/code>)&lt;/li>
&lt;li>Runner tags, Kubernetes executor&lt;/li>
&lt;/ul>
&lt;h3 id="le-piège">Le piège&lt;/h3>
&lt;p>&lt;code>gitlab-ci-local&lt;/code> utilise Docker pour exécuter les jobs. Si ton job
fait référence à &lt;code>$CI_REGISTRY_IMAGE&lt;/code> ou &lt;code>$CI_JOB_TOKEN&lt;/code>, ces variables
n&amp;rsquo;existent pas en local. Il faut les définir manuellement :&lt;/p></description></item></channel></rss>