<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Jean-Yves Gastaud blog</title>
    <link>https://gastaud.io/en/tags/kaniko/</link>
    <description>Recent content on Jean-Yves Gastaud blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-en</language>
    <lastBuildDate>Tue, 11 Aug 2026 10:00:00 +0200</lastBuildDate>
    
        <atom:link href="https://gastaud.io/en/tags/kaniko/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Running GitLab CI on Cloudflare Workers &amp; Containers: a custom GitLab Runner executor</title>
      <link>https://gastaud.io/en/article/gitlab-runner-cloudflare/</link>
      <pubDate>Tue, 11 Aug 2026 10:00:00 +0200</pubDate>
      
      <guid>https://gastaud.io/en/article/gitlab-runner-cloudflare/</guid>
      
        <description>&lt;p&gt;GitLab CI runners traditionally run on VMs or Kubernetes pods that you have to size, keep warm, and pay for even when idle.&lt;/p&gt;
&lt;p&gt;What if job execution was pushed onto a serverless platform like Cloudflare instead, with a container provisioned on demand, by the second, for each job?&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the question I wanted to dig into with &lt;code&gt;gitlab-runner-cloudflare&lt;/code&gt;: a GitLab Runner executor that runs CI jobs on Cloudflare Workers + Containers.&lt;/p&gt;
&lt;h2 id=&#34;why-this-project&#34;&gt;Why this project&lt;/h2&gt;
&lt;p&gt;GitLab Runner exposes a &lt;a href=&#34;https://docs.gitlab.com/runner/executors/custom.html&#34;&gt;Custom Executor&lt;/a&gt;: a very simple interface (&lt;code&gt;config&lt;/code&gt; / &lt;code&gt;prepare&lt;/code&gt; / &lt;code&gt;run&lt;/code&gt; / &lt;code&gt;cleanup&lt;/code&gt;), deliberately agnostic of transport and underlying infrastructure. There&amp;rsquo;s no built-in notion of Docker or SSH — it&amp;rsquo;s entirely up to the executor to decide how and where the job runs.&lt;/p&gt;
&lt;p&gt;That genericity is what made me want to test a hypothesis: is this interface open enough to be wired onto a serverless platform like &lt;a href=&#34;https://developers.cloudflare.com/workers/&#34;&gt;Cloudflare Workers&lt;/a&gt; and &lt;a href=&#34;https://developers.cloudflare.com/containers/&#34;&gt;Cloudflare Containers&lt;/a&gt;, instead of the usual VMs, Docker, or Kubernetes?&lt;/p&gt;
&lt;p&gt;If it holds up, the interesting payoff is a very different cost model: no runner fleet to keep warm at all times, a container provisioned (and billed) on demand for each job, at Cloudflare&amp;rsquo;s edge scale.&lt;/p&gt;
&lt;p&gt;This is a POC/R&amp;amp;D-style project: the goal wasn&amp;rsquo;t to ship a finished product, but to validate feasibility, document the decisions as &lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/tree/main/docs/adr&#34;&gt;ADRs&lt;/a&gt;, and push through the hard parts (building images without Docker-in-Docker, dynamic job images, arm64&amp;hellip;) until there was a concrete answer rather than a gut feeling.&lt;/p&gt;
&lt;h2 id=&#34;how-it-fits-together&#34;&gt;How it fits together&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GitLab Runner (on any host, anywhere)
  │  Custom Executor contract (config/prepare/run/cleanup)
  ▼
executor/   Go binary: gitlab-cf-executor
  │  HTTP
  ▼
worker/     Cloudflare Worker control plane
  │  one Durable Object + Container per job
  ▼
container/  Alpine + Kaniko agent (runs the job&amp;#39;s script, builds images
            without a Docker daemon)
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;executor/&lt;/code&gt;&lt;/strong&gt; is the binary GitLab Runner invokes directly. It talks HTTP to a deployed &lt;code&gt;worker/&lt;/code&gt;, with a fully local fallback mode (no Cloudflare account needed) to develop and test without any external dependency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;worker/&lt;/code&gt;&lt;/strong&gt; is the control plane: it provisions one Container (via a Durable Object) per job, and proxies &lt;code&gt;prepare&lt;/code&gt;/&lt;code&gt;run&lt;/code&gt;/&lt;code&gt;cleanup&lt;/code&gt; to a small CGI agent baked into the container image.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;objectives-achieved&#34;&gt;Objectives achieved&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The feasibility question has an answer&lt;/strong&gt;: yes, a GitLab Runner Custom Executor can drive CI jobs on Cloudflare Workers + Containers, validated against a real Cloudflare deployment (not just locally with &lt;code&gt;wrangler dev&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Image builds without Docker-in-Docker&lt;/strong&gt;: Cloudflare Containers doesn&amp;rsquo;t grant privileged mode, which rules out Docker-in-Docker, Buildah, and BuildKit (all three need &lt;code&gt;unshare(CLONE_NEWUSER)&lt;/code&gt;). The &lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/spikes/dind-validation.md&#34;&gt;&lt;code&gt;dind-validation&lt;/code&gt;&lt;/a&gt; spike confirmed that &lt;a href=&#34;https://github.com/GoogleContainerTools/kaniko&#34;&gt;Kaniko&lt;/a&gt; manages without any special privileges, and it&amp;rsquo;s the one that got adopted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live-streamed logs&lt;/strong&gt;: a job&amp;rsquo;s output streams live instead of being buffered and returned all at once, with a dedicated endpoint to fetch the exit code once the stream reaches EOF — a constraint forced by CGI headers needing to precede the response body.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic job images&lt;/strong&gt;: instead of one fixed, curated container image, &lt;code&gt;prepare.cgi&lt;/code&gt; pulls and unpacks (via &lt;code&gt;skopeo&lt;/code&gt;/&lt;code&gt;umoci&lt;/code&gt;) whichever &lt;code&gt;image:&lt;/code&gt; a job requests, and &lt;code&gt;run.cgi&lt;/code&gt; runs the job&amp;rsquo;s script &lt;code&gt;chroot&lt;/code&gt;ed into it — see &lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/adr/0004-dynamic-job-images.md&#34;&gt;ADR-0004&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;git clone&lt;/code&gt; that works even on an image without &lt;code&gt;git&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;get_sources&lt;/code&gt; runs outside the &lt;code&gt;chroot&lt;/code&gt;, in the sandbox that does have &lt;code&gt;git&lt;/code&gt;, symlinking &lt;code&gt;/builds&lt;/code&gt;/&lt;code&gt;/cache&lt;/code&gt; into the job image&amp;rsquo;s rootfs — the same principle GitLab Runner&amp;rsquo;s own Docker/Kubernetes executors use with a dedicated helper.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;services:&lt;/code&gt;, &lt;code&gt;cache:&lt;/code&gt;, and &lt;code&gt;artifacts:&lt;/code&gt; support&lt;/strong&gt;: covered in detail just below.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests and CI&lt;/strong&gt;: 41 Go unit tests on the &lt;code&gt;executor/&lt;/code&gt; side, Vitest unit tests on the &lt;code&gt;worker/&lt;/code&gt; side, and an E2E test that runs a real Kaniko build through a local &lt;code&gt;wrangler dev&lt;/code&gt;. All of it automated in &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; (tests on every relevant change, manual deploy on &lt;code&gt;main&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: 5 ADRs tracking the architecture decisions (execution platform, multi-architecture support, &lt;code&gt;docker_autoscaler&lt;/code&gt;/fleeting compatibility, dynamic job images, services/cache/artifacts), 2 spikes documenting the hands-on validations, and a README per component.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authenticated control plane&lt;/strong&gt;: the Worker&amp;rsquo;s HTTP API used to be open to anyone who knew its &lt;code&gt;*.workers.dev&lt;/code&gt; URL — every route now requires a shared-secret &lt;code&gt;Authorization: Bearer&lt;/code&gt; token, see below.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;services-cache-artifacts-how-it-actually-works&#34;&gt;&lt;code&gt;services:&lt;/code&gt;, &lt;code&gt;cache:&lt;/code&gt;, &lt;code&gt;artifacts:&lt;/code&gt;: how it actually works&lt;/h2&gt;
&lt;p&gt;GitLab exposes a job&amp;rsquo;s &lt;code&gt;services:&lt;/code&gt; to a Custom Executor through exactly one variable, &lt;code&gt;CUSTOM_ENV_CI_JOB_SERVICES&lt;/code&gt; — a JSON array, and it&amp;rsquo;s entirely up to the executor to start/stop them itself. Nothing else is provided: no Docker network, no API to implement, just that one piece of information.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# .gitlab-ci.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;cf-executor:demo-services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;redis:7-alpine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;alias&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;redis&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#ae81ff&#34;&gt;for i in $(seq 1 10); do nc -z redis 6379 &amp;amp;&amp;amp; break; sleep 1; done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#ae81ff&#34;&gt;nc -z redis 6379 &amp;amp;&amp;amp; echo &amp;#34;OK&amp;#34; || exit 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because every job already runs inside &lt;strong&gt;one&lt;/strong&gt; Cloudflare Container instance (one network namespace), a service doesn&amp;rsquo;t need container-level isolation — it just needs to run as a background process, &lt;code&gt;chroot&lt;/code&gt;ed into its own rootfs (unpacked with &lt;code&gt;skopeo&lt;/code&gt;/&lt;code&gt;umoci&lt;/code&gt;, same as the job&amp;rsquo;s own image), and be reachable by its alias. &lt;code&gt;start-service.sh&lt;/code&gt; uses &lt;code&gt;setsid&lt;/code&gt; to detach the process from the CGI request that started it, and the OCI runtime-spec &lt;code&gt;config.json&lt;/code&gt; that &lt;code&gt;umoci&lt;/code&gt; produces to recover the image&amp;rsquo;s &lt;code&gt;ENTRYPOINT&lt;/code&gt;/&lt;code&gt;CMD&lt;/code&gt; without having to redeclare them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# simplified excerpt from start-service.sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ROOTFS_DIR&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;pull-image.sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$IMAGE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$BUNDLE_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jq -r &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.process.args[]&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$BUNDLE_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;/config.json&amp;#34;&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$ARGV_FILE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;# ENTRYPOINT+CMD already resolved by umoci&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;setsid &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$LAUNCH_SCRIPT&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$ENV_FILE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$ARGV_FILE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$ROOTFS_DIR&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$LOG_FILE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; 2&amp;gt;&amp;amp;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo $! &amp;gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$PID_FILE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;cache:&lt;/code&gt;/&lt;code&gt;artifacts:&lt;/code&gt; turned out differently: &lt;code&gt;restore_cache&lt;/code&gt;/&lt;code&gt;download_artifacts&lt;/code&gt;/&lt;code&gt;archive_cache&lt;/code&gt;/&lt;code&gt;upload_artifacts&lt;/code&gt; are scripts GitLab Runner generates like any other stage, but its own docs are explicit that they shell out directly to the &lt;code&gt;gitlab-runner&lt;/code&gt; binary itself — which has no reason to be inside a job&amp;rsquo;s (dynamically pulled) image. The fix reuses the same pattern already in place for &lt;code&gt;get_sources&lt;/code&gt;/&lt;code&gt;git&lt;/code&gt;: widen the set of stages that run outside the &lt;code&gt;chroot&lt;/code&gt;, in the sandbox that has that binary:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# run.cgi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$stage&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  get_sources|restore_cache|download_artifacts|archive_cache|archive_cache_on_failure|upload_artifacts_on_success|upload_artifacts_on_failure&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    unchrooted_stage&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  *&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    unchrooted_stage&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;docker:dind&lt;/code&gt; as a service remains explicitly unsupported, for the same reason it is as a job image (needs privileged mode) — see &lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/adr/0005-service-containers-and-cache-artifacts.md&#34;&gt;ADR-0005&lt;/a&gt; for the full writeup.&lt;/p&gt;
&lt;h2 id=&#34;what-the-real-deployment-revealed&#34;&gt;What the real deployment revealed&lt;/h2&gt;
&lt;p&gt;This POC runs on one rule: nothing counts as working until it&amp;rsquo;s run against a real Cloudflare deployment, not just locally or under &lt;code&gt;wrangler dev&lt;/code&gt;. Adding &lt;code&gt;services:&lt;/code&gt; support paid that rule back three times over — three bugs invisible locally, only surfacing once &lt;code&gt;worker:deploy&lt;/code&gt; actually ran and the demo jobs went through the registered runner for real.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;/etc/hosts&lt;/code&gt; is mounted read-only on a real Cloudflare Container.&lt;/strong&gt; The first implementation appended &lt;code&gt;alias 127.0.0.1&lt;/code&gt; straight into &lt;code&gt;/etc/hosts&lt;/code&gt; — this works perfectly in a local Docker test (where &lt;code&gt;/etc/hosts&lt;/code&gt; is an ordinary writable bind-mounted file), and fails in production:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;1.2.3.4 foo&amp;#34; &amp;gt;&amp;gt; /etc/hosts
/tmp/run-script.sh: line 1: can&amp;#39;t create /etc/hosts: Read-only file system

$ cat /proc/mounts | grep -E &amp;#34;hosts|resolv&amp;#34;
overlay /etc/hosts       overlay ro,relatime,... 0 0
overlay /etc/resolv.conf overlay rw,relatime,... 0 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cloudflare mounts &lt;code&gt;/etc/hosts&lt;/code&gt; as its own dedicated overlay, separate from the rest of &lt;code&gt;/etc&lt;/code&gt; — but &lt;code&gt;/etc/resolv.conf&lt;/code&gt; stays writable. Alias resolution now goes through a local &lt;code&gt;dnsmasq&lt;/code&gt;, started the first time a service is declared, answering for known aliases and forwarding everything else to the real upstream resolvers captured before &lt;code&gt;resolv.conf&lt;/code&gt; gets rewritten:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# setsid dnsmasq --no-daemon --addn-hosts=/tmp/service-hosts --server=&amp;lt;upstream1&amp;gt; --server=&amp;lt;upstream2&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nameserver 127.0.0.1&amp;#34;&lt;/span&gt; &amp;gt; /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. A failed &lt;code&gt;prepare_exec&lt;/code&gt; gets retried up to 3 times by GitLab Runner, against the same container instance.&lt;/strong&gt; A service already started by an earlier attempt (because a &lt;em&gt;different&lt;/em&gt; service in the same job had failed) was still running and still holding its port:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;185:M ... # Warning: Could not create server TCP listening socket *:6379: bind: Address in use
185:M ... # Failed listening on port 6379 (tcp), aborting.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A false negative that pointed at the wrong culprit entirely. Each service&amp;rsquo;s PID is now tracked in its own file, and any leftover instance is killed before a new one starts:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; -f &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$PID_FILE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; kill -0 &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$PID_FILE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; 2&amp;gt;/dev/null; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  kill &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$PID_FILE&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; 2&amp;gt;/dev/null &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  sleep &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;3. The bundled &lt;code&gt;gitlab-runner&lt;/code&gt; binary&amp;rsquo;s version has to track the registered runner&amp;rsquo;s, not just any recent release.&lt;/strong&gt; &lt;code&gt;archive_cache&lt;/code&gt; invokes &lt;code&gt;gitlab-runner cache-archiver&lt;/code&gt; with flags that change between versions; a version mismatch (17.9 bundled vs. 19.2 registered, in this case) fails the call on an unrecognized flag:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Incorrect Usage: flag provided but not defined: -alternate-file
FATAL: flag provided but not defined: -alternate-file
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Silently, too: GitLab Runner treats cache failures as non-fatal and still reports the job as succeeded. A green job can be hiding a cache that never actually got created — this only surfaced by reading &lt;code&gt;archive_cache&lt;/code&gt;&amp;rsquo;s own stage log, not the job&amp;rsquo;s overall status.&lt;/p&gt;
&lt;p&gt;All three make the same point as the &lt;code&gt;wrangler dev&lt;/code&gt; limitation further down: it looks like Cloudflare, but it isn&amp;rsquo;t one.&lt;/p&gt;
&lt;h2 id=&#34;securing-the-control-plane-api&#34;&gt;Securing the control-plane API&lt;/h2&gt;
&lt;p&gt;The Worker was deployed on the public &lt;code&gt;*.workers.dev&lt;/code&gt; subdomain, no custom route or domain — and its HTTP API (&lt;code&gt;/jobs/:jobId/{prepare,run,cleanup}&lt;/code&gt;) had no authentication whatsoever. Anyone who found the URL could provision and drive containers.&lt;/p&gt;
&lt;p&gt;Two ways to fix that: Cloudflare Access (Zero Trust) service tokens, the more &amp;ldquo;native&amp;rdquo; Cloudflare-side option, or a plain shared-secret &lt;code&gt;Authorization: Bearer&lt;/code&gt; token. Access would have needed a custom domain and zone — which this Worker doesn&amp;rsquo;t have — plus an Access application configured outside &lt;code&gt;wrangler.toml&lt;/code&gt;. The shared secret needs no extra Cloudflare infrastructure at all, just code.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// worker/src/handlers.ts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isAuthorized&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Request&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;sharedSecret&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;sharedSecret&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// fails closed if the secret isn&amp;#39;t configured
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;header&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;.&lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;??&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;header&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;startsWith&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bearer &amp;#34;&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;timingSafeEqual&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;header&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;sharedSecret&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Checked &lt;em&gt;before&lt;/em&gt; the route is even parsed, so an unauthenticated caller can&amp;rsquo;t tell an existing route from a 404. The constant-time comparison is hand-rolled rather than &lt;code&gt;crypto.subtle.timingSafeEqual&lt;/code&gt; — that method is a Cloudflare-only Web Crypto extension, missing from Node&amp;rsquo;s &lt;code&gt;SubtleCrypto&lt;/code&gt;, and this file is deliberately kept testable under plain &lt;code&gt;vitest&lt;/code&gt; (not &lt;code&gt;@cloudflare/vitest-pool-workers&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;On the &lt;code&gt;executor/&lt;/code&gt; side, &lt;code&gt;GITLAB_CF_EXECUTOR_WORKER_TOKEN&lt;/code&gt; (same value as the Worker&amp;rsquo;s &lt;code&gt;SHARED_SECRET&lt;/code&gt;, set via &lt;code&gt;wrangler secret put&lt;/code&gt;, never committed) rides on every request. And like the three bugs in the previous section, the real proof came from a real deployment: the first demo job retried after wiring this up failed with &lt;code&gt;401 unauthorized&lt;/code&gt; — not a false negative, but proof that the &lt;code&gt;gitlab-cf-executor&lt;/code&gt; binary already running on the registered runner predated the fix and was sending no token at all. Rebuilt, the job went green again.&lt;/p&gt;
&lt;h2 id=&#34;current-limitations--whats-left&#34;&gt;Current limitations / what&amp;rsquo;s left&lt;/h2&gt;
&lt;p&gt;This is a POC, not a finished product. What&amp;rsquo;s still unresolved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No arm64 lane&lt;/strong&gt;: Cloudflare Containers is amd64-only. The provider choice for the arm64 side (AWS Graviton, Oracle Ampere, Hetzner) hasn&amp;rsquo;t been made, and the resulting &lt;code&gt;docker_autoscaler&lt;/code&gt;/fleeting integration isn&amp;rsquo;t built yet — see &lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/adr/0002-multi-architecture-support.md&#34;&gt;ADR-0002&lt;/a&gt; and &lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/adr/0003-docker-autoscaler-fleeting-plugin-compatibility.md&#34;&gt;ADR-0003&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No private registry auth&lt;/strong&gt; for dynamic image pulls: &lt;code&gt;skopeo copy&lt;/code&gt; supports &lt;code&gt;--src-creds&lt;/code&gt;, but it isn&amp;rsquo;t wired up yet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No cross-job layer cache&lt;/strong&gt;: each job&amp;rsquo;s container is destroyed at &lt;code&gt;cleanup&lt;/code&gt;, so every job re-pulls its image from scratch. An R2-backed cache is a candidate if pull latency becomes a real problem in practice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cache:&lt;/code&gt; persistence across jobs is still the runner operator&amp;rsquo;s responsibility&lt;/strong&gt;: &lt;code&gt;restore_cache&lt;/code&gt;/&lt;code&gt;archive_cache&lt;/code&gt; now run correctly, but without &lt;code&gt;[runners.cache]&lt;/code&gt; configured in &lt;code&gt;config.toml&lt;/code&gt; (an S3-compatible backend, e.g. R2), there&amp;rsquo;s nowhere for them to persist to — that&amp;rsquo;s infrastructure configuration, not something this project can provide from inside the container.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Services get only a single default alias&lt;/strong&gt;, derived from the image name (&lt;code&gt;postgres:14&lt;/code&gt; → &lt;code&gt;postgres&lt;/code&gt;), not GitLab&amp;rsquo;s full multi-alias scheme — an explicit &lt;code&gt;alias:&lt;/code&gt; covers the more complex cases. They also start sequentially rather than in parallel, and with no readiness protocol: a job script has to poll the service itself (matching GitLab&amp;rsquo;s own general guidance).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build ≠ run within the same job&lt;/strong&gt;: Kaniko can build and push an image, but there&amp;rsquo;s no mechanism yet to run that freshly built image as part of the same job.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No direct SSH/debug access&lt;/strong&gt; into a running Cloudflare Container instance for local troubleshooting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;wrangler dev&lt;/code&gt; is unreliable on long-running requests&lt;/strong&gt; (Kaniko builds, cold starts) — confirmed local-dev-only, with no impact on a real deployment, and tolerated rather than fixed in the E2E tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these is tracked with its sources in &lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/TODO.md&#34;&gt;&lt;code&gt;docs/TODO.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;links&#34;&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare&#34;&gt;gitlab.com/jygastaud/gitlab-runner-cloudflare&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/adr/0001-execution-platform.md&#34;&gt;ADR-0001 — Execution platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/adr/0002-multi-architecture-support.md&#34;&gt;ADR-0002 — Multi-architecture support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/adr/0003-docker-autoscaler-fleeting-plugin-compatibility.md&#34;&gt;ADR-0003 — &lt;code&gt;docker_autoscaler&lt;/code&gt;/fleeting compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/adr/0004-dynamic-job-images.md&#34;&gt;ADR-0004 — Dynamic job images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/adr/0005-service-containers-and-cache-artifacts.md&#34;&gt;ADR-0005 — Services, cache, and artifacts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.com/jygastaud/gitlab-runner-cloudflare/-/blob/main/docs/spikes/dind-validation.md&#34;&gt;Spike — Docker-in-Docker/Kaniko validation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      
    </item>
    
    <item>
      <title>Install and configure Renovate Bot on GitLab CE</title>
      <link>https://gastaud.io/en/article/gitlab-renovatebot/</link>
      <pubDate>Sat, 27 Nov 2021 16:14:35 +0100</pubDate>
      
      <guid>https://gastaud.io/en/article/gitlab-renovatebot/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;https://renovatebots.com&#34;&gt;Renovate Bot&lt;/a&gt; is a tool to control the versions of dependencies of your projects.&lt;/p&gt;
&lt;p&gt;It can be compared to the &lt;a href=&#34;https://github.com/dependabot&#34;&gt;Dependabot&lt;/a&gt; project which is available on GitHub.&lt;/p&gt;
&lt;p&gt;In this article, we will explore how to install and configure Renovate to run on a Gitlab CE instance.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;In order to follow the initialization process you must be able to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create groups and projects&lt;/li&gt;
&lt;li&gt;Have access or install at least 1 GitLab Runner with a docker executor&lt;/li&gt;
&lt;li&gt;Have a GitHub account (even if we are going to use GitLab, it will be used to get the release notes of the projects)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;initialization&#34;&gt;Initialization&lt;/h2&gt;
&lt;h3 id=&#34;project-structure&#34;&gt;Project structure&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;Renovate&lt;/code&gt; project group&lt;/li&gt;
&lt;li&gt;Create 2 projects in the group
&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;renovate-config&lt;/code&gt; project which will contain configuration files that can be shared with all GitLab projects;&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;renovate-runner&lt;/code&gt; project which will contain the configurations to dedicate to the Renovate container.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;gitlab-user&#34;&gt;Gitlab user&lt;/h3&gt;
&lt;p&gt;As the name of the project indicates (Renovate&lt;strong&gt;Bot&lt;/strong&gt;) we will have to create a user account representing our Bot.&lt;/p&gt;
&lt;p&gt;For our example, we will call it &lt;code&gt;Renovate Bot &amp;lt;renovatebot@example.com&amp;gt;&lt;/code&gt; and the username &lt;code&gt;renovatebot&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This user will be used for 2 things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;merge requests, commits&amp;hellip; will be created by the Bot ;&lt;/li&gt;
&lt;li&gt;restrict renovate&amp;rsquo;s rights to browse only those projects on which it is explicitly invited.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;renovate-runner-project-configuration&#34;&gt;Renovate-runner project configuration&lt;/h2&gt;
&lt;p&gt;We will start by working on the runner part. Let&amp;rsquo;s go to the &lt;code&gt;renovate-runner&lt;/code&gt; project we created earlier.&lt;/p&gt;
&lt;h3 id=&#34;creation-of-user-tokens-pat--personal-access-token&#34;&gt;Creation of user tokens (PAT : Personal Access Token)&lt;/h3&gt;
&lt;h4 id=&#34;gitlab&#34;&gt;GitLab&lt;/h4&gt;
&lt;p&gt;On your GitLab instance, log in with the &lt;code&gt;renovatebot&lt;/code&gt; account created earlier.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll create an Access Token via your user profile (/-/profile/personal_access_tokens).&lt;/p&gt;
&lt;p&gt;Name the token whatever you like (&lt;code&gt;renovatebot&lt;/code&gt; for example) and choose the following scopes as &lt;a href=&#34;https://docs.renovatebot.com/getting-started/running/#gitlab&#34;&gt;indicated in the documentation&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;read_user&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;api&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;write_repository&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Copy the token and go back to the &lt;code&gt;renovate-runner&lt;/code&gt; project, in the CI/CD Settings (menu &lt;code&gt;Settings&lt;/code&gt; &amp;gt; &lt;code&gt;CI/CD&lt;/code&gt; &amp;gt; &lt;code&gt;Variables&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;and create a new variable named &lt;code&gt;RENOVATE_TOKEN&lt;/code&gt; with the retrieved token value.&lt;/p&gt;
&lt;h4 id=&#34;github&#34;&gt;GitHub&lt;/h4&gt;
&lt;p&gt;In order to be able to retrieve release notes from projects and not be subject to the limitations of the GitHub API, we will need to create a GitHub and create a &lt;a href=&#34;https://docs.renovatebot.com/getting-started/running/#githubcom-token-for-release-notes&#34;&gt;Personal Access Token&lt;/a&gt; there.&lt;/p&gt;
&lt;p&gt;Copy the token and go back to the &lt;code&gt;renovate-runner&lt;/code&gt; project, in the CI/CD Settings (menu &lt;code&gt;Settings&lt;/code&gt; &amp;gt; &lt;code&gt;CI/CD&lt;/code&gt; &amp;gt; &lt;code&gt;Variables&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;and create a new variable named &lt;code&gt;GITHUB_COM_TOKEN&lt;/code&gt; with the value of the recovered token.&lt;/p&gt;
&lt;h3 id=&#34;our-gitlab-ciyml-file&#34;&gt;Our &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;image: renovate/renovate:29	

variables:
  RENOVATE_BASE_DIR: $CI_PROJECT_DIR/renovate
  RENOVATE_GIT_AUTHOR: Renovate Bot &amp;lt;renovatebot@exemple.com&amp;gt;
  RENOVATE_OPTIMIZE_FOR_DISABLED: &amp;#39;true&amp;#39;
  RENOVATE_REPOSITORY_CACHE: &amp;#39;true&amp;#39;
  LOG_LEVEL: debug


cache:
  key: ${CI_COMMIT_REF_SLUG}-renovate
  paths:
    - $CI_PROJECT_DIR/renovate

renovate:
  stage: deploy
  resource_group: production
  only:
    - schedules
  script:
    - renovate $RENOVATE_EXTRA_FLAGS
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Renovate offers most of these configurations as environment variables.
However, to be able to use the bot in other contexts than GitLab (a manual launch for example), I only kept the GitLab specific parameters in the &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;The rest of the configurations are in the &lt;code&gt;config.js&lt;/code&gt; file that we&amp;rsquo;ll look at right now. &amp;#x2b07;&amp;#xfe0f;&lt;/p&gt;
&lt;h3 id=&#34;renovate-configuration-file&#34;&gt;Renovate configuration file&lt;/h3&gt;
&lt;p&gt;Renovate is based on a configuration file &lt;code&gt;config.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It defines the Git platform to use, the &lt;a href=&#34;https://docs.renovatebot.com/self-hosted-configuration/&#34;&gt;configuration options&lt;/a&gt; of the runner and the &lt;a href=&#34;https://docs.renovatebot.com/config-presets/&#34;&gt;configuration profiles (Config Presets)&lt;/a&gt; which will be applied for all the projects &lt;code&gt;onboardingConfig&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;module.exports = {
        endpoint: &amp;#39;https://[url of gitlab]/api/v4/&amp;#39;,
        platform: &amp;#39;gitlab&amp;#39;,
        persistRepoData: true,
        logLevel: &amp;#39;debug&amp;#39;,
        onboardingConfig: {
                &amp;#39;extends&amp;#39;: [
                        &amp;#34;local&amp;gt;groups/subgroups/renovate/renovate-config&amp;#34;
                ],
        },
        autodiscover: true,
};
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;recurring-task&#34;&gt;Recurring task&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CI/CD menu &amp;gt; Schedules&lt;/li&gt;
&lt;li&gt;Create a new scheduled task with the frequency you want (I made the choice, totally arbitrary, to run the analysis twice a day) and to trigger.&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;configuration-of-default-configurations&#34;&gt;Configuration of default configurations&lt;/h2&gt;
&lt;p&gt;In the &lt;code&gt;renovate-config&lt;/code&gt; project we will create a simple &lt;code&gt;renovate.json&lt;/code&gt; file which will be the one searched by default by the &lt;code&gt;config.json&lt;/code&gt; file and in particular the line &lt;code&gt;&amp;quot;local&amp;gt;groups/subgroups/renovate/renovate-config&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here are the choices I made:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; {
  &amp;#34;$schema&amp;#34;: &amp;#34;https://docs.renovatebot.com/renovate-schema.json&amp;#34;,
  &amp;#34;packageRules&amp;#34;: [
    {
      &amp;#34;depTypeList&amp;#34;: [ &amp;#34;devDependencies&amp;#34;, &amp;#34;require-dev&amp;#34; ],
      &amp;#34;updateTypes&amp;#34;: [ &amp;#34;patch&amp;#34;, &amp;#34;minor&amp;#34;, &amp;#34;digest&amp;#34;],
      &amp;#34;groupName&amp;#34;: &amp;#34;devDependencies (non-major)&amp;#34;
    }
  ],
  &amp;#34;extends&amp;#34;: [
    &amp;#34;config:base&amp;#34;,
    &amp;#34;:preserveSemverRanges&amp;#34;,
    &amp;#34;:dependencyDashboard&amp;#34;,
    &amp;#34;:rebaseStalePrs&amp;#34;,
    &amp;#34;:enableVulnerabilityAlertsWithLabel(&amp;#39;security&amp;#39;)&amp;#34;,
    &amp;#34;group:recommended&amp;#34;
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;packageRules&lt;/code&gt; allows to create new groupings. In our case, this allows us to have a single Merge Request containing all the development dependencies of the project when they are not major.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Extends&lt;/code&gt; allows us to define the rules/configs presets that we want to activate. In particular, we activate the &lt;a href=&#34;https://docs.renovatebot.com/key-concepts/dashboard/&#34;&gt;Dependency Dashboard&lt;/a&gt; which allows us to follow in a ticket the status of all available updates and/or current MRs and various &lt;a href=&#34;https://docs.renovatebot.com/presets-group/&#34;&gt;Group Presets&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;activate-the-bot-for-projects&#34;&gt;Activate the bot for projects&lt;/h2&gt;
&lt;p&gt;With these configurations, each project that wants to activate the bot has only 4 steps to follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add the user &lt;code&gt;renovatebot&lt;/code&gt; on the project, with at least a &lt;code&gt;contributor&lt;/code&gt; right&lt;/li&gt;
&lt;li&gt;Enable the &lt;code&gt;issues&lt;/code&gt; for the &lt;code&gt;dependency Dashboard&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;merge requests&lt;/code&gt; for first with a 1st Merge Request to create the renovate configuration, then the MRs associated with the updates to apply.&lt;/li&gt;
&lt;li&gt;Refine the Renovate configurations according to the specificities of your project&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And that&amp;rsquo;s it &amp;#x1f37e;&lt;/p&gt;
&lt;p&gt;We now have a Renovate bot that runs regularly and will update all the repositories it has access to.&lt;/p&gt;</description>
      
    </item>
    
    <item>
      <title>Sorting tags from a Git repo using semantic versioning</title>
      <link>https://gastaud.io/en/article/git-sort-tags-semantic-version/</link>
      <pubDate>Mon, 20 Sep 2021 23:10:00 +0200</pubDate>
      
      <guid>https://gastaud.io/en/article/git-sort-tags-semantic-version/</guid>
      
        <description>&lt;p&gt;A quick post today to tell you about a recent discovery in Git&amp;rsquo;s sorting functions and tags based on &lt;a href=&#34;https://semver.org/&#34;&gt;semantic versioning&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By default, if you run the &lt;code&gt;git tag -l&lt;/code&gt; command, Git will do an alphabetical sort.&lt;/p&gt;
&lt;p&gt;However, this sorting gives confusing results when the tags use semantic versioning notation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git tag -l
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.11
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.12
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.6
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.7
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.9
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.7.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice here the sequence &lt;code&gt;v2.6.10&lt;/code&gt;, &lt;code&gt;v2.6.11&lt;/code&gt; and &lt;code&gt;v2.6.12&lt;/code&gt; which is interposed between version &lt;code&gt;v2.6.1&lt;/code&gt; and &lt;code&gt;v2.6.2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To solve this problem, it is possible to use the &lt;code&gt;--sort&lt;/code&gt; function with the &lt;code&gt;version&lt;/code&gt; attribute in Git.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git tag --sort&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;version:refname
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will display the sorted results consistently:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git tag --sort&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;version:refname
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.6
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.7
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.9
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.10
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.11
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.6.12
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;v2.7.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      
    </item>
    
    <item>
      <title>Upgrade Buildx in Docker CLI</title>
      <link>https://gastaud.io/en/article/docker-buildx-upgrade/</link>
      <pubDate>Sat, 02 May 2020 14:29:42 +0200</pubDate>
      
      <guid>https://gastaud.io/en/article/docker-buildx-upgrade/</guid>
      
        <description>&lt;p&gt;Docker CLI, since version 19.03 includes the &lt;a href=&#34;https://github.com/docker/buildx&#34;&gt;buildx&lt;/a&gt; plugin to extend the build functions of Docker based on &lt;a href=&#34;https://github.com/moby/buildkit&#34;&gt;Buildkit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Among the main points that Buildkit brings are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;parallel resolution of dependencies&lt;/li&gt;
&lt;li&gt;better cache management (import/export, resolution)&lt;/li&gt;
&lt;li&gt;ability to distribute workloads&lt;/li&gt;
&lt;li&gt;run without root privileges&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And so, as mentioned above, buildkit is now included in docker CLI.
Problem, the available and pre-packaged version is not up to date with the latest developments.&lt;/p&gt;
&lt;p&gt;If you want to take advantage of the latest evolutions,
you will need to update.&lt;/p&gt;

&lt;div class=&#34;shortcode-notice info&#34;&gt;
  &lt;div class=&#34;shortcode-notice-title info&#34;&gt;
    
      buildx version
    
  &lt;/div&gt;
  &lt;p&gt;This tutorial uses buildx version &lt;strong&gt;0.4.1&lt;/strong&gt;.&lt;br&gt;
Remember to check the latest version number before copying and pasting the instructions.&lt;/p&gt;

&lt;/div&gt;

&lt;h2 id=&#34;the-steps&#34;&gt;The steps&lt;/h2&gt;
&lt;p&gt;Check the existence of the &lt;code&gt;~/.docker/cli-plugins&lt;/code&gt; directory.
If it doesn&amp;rsquo;t exist, create the&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir ~/.docker/cli-plugins
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Download the latest buildx version from the &lt;a href=&#34;https://github.com/docker/buildx/releases&#34;&gt;Buildx release page on Github&lt;/a&gt; or directly from your terminal.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wget -O ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/v0.4.1/buildx-v0.4.1.linux-amd64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Set execution rights on the binary: &lt;code&gt;chmod +x ~/.docker/cli-plugins/docker-buildx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;and voila!&lt;/p&gt;
&lt;p&gt;We just have to check if the buildx version is now the expected one.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker buildx version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want buildx to become the default builder of Docker CLI&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker builx install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;demo&#34;&gt;Demo&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://asciinema.org/a/aWtsg3uCTb2wbEeZHh79c2ntS&#34;&gt;&lt;img src=&#34;https://asciinema.org/a/aWtsg3uCTb2wbEeZHh79c2ntS.svg&#34; alt=&#34;asciicast&#34;&gt;&lt;/a&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>Azure-cli, activate autocomplete on Zsh</title>
      <link>https://gastaud.io/en/article/azure-cli-autocomplete/</link>
      <pubDate>Tue, 26 Nov 2019 23:35:18 +0100</pubDate>
      
      <guid>https://gastaud.io/en/article/azure-cli-autocomplete/</guid>
      
        <description>&lt;p&gt;Out of the box, Azure-cli doesn&amp;rsquo;t have autocomplete activated in Zsh and Oh-My-Zsh framework doesn&amp;rsquo;t have plugin available natively.&lt;/p&gt;
&lt;p&gt;However, Azure-cli comes with an autocomplete script that should be located in &lt;code&gt;bash_completion.d&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;In my setup, using Ubuntu 18.04, I found the &lt;code&gt;azure-cli&lt;/code&gt; script in &lt;code&gt;/etc/bash_completion.d/&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Now we just need to load the autocomplete script inside our &lt;code&gt;~/.zshrc&lt;/code&gt; with the following line:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;source /etc/bash_completion.d/azure-cli
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Save and reload your terminal. Done.&lt;/p&gt;
&lt;h3 id=&#34;additional-notes&#34;&gt;Additional notes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;If you encounter an error when restarting, you may have to add that line before the script loading to ensure cross compatibility between bash and zsh script&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;autoload -U +X bashcompinit &amp;amp;&amp;amp; bashcompinit
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Looking at different blog post, you may find that the script is called &lt;code&gt;az&lt;/code&gt;, not &lt;code&gt;azure-cli&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;According to the directory I found it, the script was natively loaded if I use bash shell. If you use bash and doesn&amp;rsquo;t have autocomplete activate, you probably have to search for &lt;code&gt;azure-cli&lt;/code&gt; or &lt;code&gt;az&lt;/code&gt; script and load it to your &lt;code&gt;~/.bashrc&lt;/code&gt; file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
    <item>
      <title>GoHugo - Generate alternative images that can be used in the homepage</title>
      <link>https://gastaud.io/en/article/hugo-homepage-images-processing/</link>
      <pubDate>Thu, 11 Apr 2019 16:51:22 +0200</pubDate>
      
      <guid>https://gastaud.io/en/article/hugo-homepage-images-processing/</guid>
      
        <description>&lt;p&gt;Translate from French on &lt;a href=&#34;https://dev.to/jygastaud/gohugo-generate-image-declinations-that-can-be-used-as-a-home-page-530j&#34;&gt;dev.to&lt;/a&gt; and backport here.&lt;/p&gt;
&lt;p&gt;GoHugo, since version 0.32, is able to manage &lt;a href=&#34;https://gohugo.io/about/new-in-032/#page-resources&#34;&gt;dedicated one-page resources&lt;/a&gt;
as well as to generate &lt;a href=&#34;https://gohugo.io/about/new-in-032/#image-processing&#34;&gt;image declinations&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This feature is particularly useful for automatically managing responsible declinations of these images and using them in a template or shortcode.&lt;/p&gt;
&lt;p&gt;It works perfectly when you are in a content.&lt;/p&gt;
&lt;h2 id=&#34;so-whats-the-problem&#34;&gt;So what&amp;rsquo;s the problem?&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s assume that we want to use responsible images on the homepage of our site.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The home page is not part of a section or bundle and therefore does not have access to resources&lt;/li&gt;
&lt;li&gt;Files placed in the &lt;code&gt;static&lt;/code&gt; directory are not treated as resources by Hugo&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It will therefore be necessary to manage &amp;ldquo;by hand&amp;rdquo;, the declination of these images and include them manually in our template.&lt;/p&gt;
&lt;p&gt;To simplify, we have the following aborescence:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;.
| content
| | | _index.md
| layouts
| | | index.html
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;what-but-i-have-too-many-images-to-decline-and-the-content-changes-often&#34;&gt;What? But I have too many images to decline and the content changes often?!&lt;/h2&gt;
&lt;p&gt;To automate the generation process, we will use the &lt;a href=&#34;https://gohugo.io/news/0.35-relnotes/&#34;&gt;Headless Bundles&lt;/a&gt; introduced in version 0.35.&lt;/p&gt;
&lt;p&gt;This new type of bundle allows you to manage content that will not produce a full page when rendered by Hugo.&lt;/p&gt;
&lt;p&gt;It is therefore convenient to use it to manage blocks, messages&amp;hellip; which are then injected into the pages.&lt;/p&gt;
&lt;p&gt;By using this type of bundle and combining it with the image generation functionality, we will be able to automatically obtain our various image declinations.&lt;/p&gt;
&lt;p&gt;We will follow the following steps.&lt;/p&gt;
&lt;h4 id=&#34;creating-a-new-bundle-headless-img&#34;&gt;Creating a new bundle &lt;code&gt;headless-img&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;We will now have the following content tree structure:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;.
| content
| | | _index.md
| | | headless-img
| | | | | images
| | | | | | my_image.png
| | | | index.md
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The file &lt;code&gt;headless-img/index.md&lt;/code&gt; will contain&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+++
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;headless = true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;title = &amp;#34;homepage images&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+++
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&amp;lt; imghp src=&amp;#34;images/mon_image.png&amp;#34; alt=&amp;#34;A nice image for our homepage&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So we have created here a &lt;code&gt;headless&lt;/code&gt; resource that will call a hugo shortcode named &lt;code&gt;imghp&lt;/code&gt; and that takes a path and an alternative text as parameters.&lt;/p&gt;
&lt;h4 id=&#34;creating-the-shortcode&#34;&gt;Creating the shortcode&lt;/h4&gt;
&lt;p&gt;In the &lt;code&gt;layouts/shortcodes&lt;/code&gt; aborescence we will create a new file named &lt;code&gt;imghp.html&lt;/code&gt; which will contain the logic of generating our images and their rendering.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#75715e&#34;&gt;/* The shortcode for HP&amp;#39;s responsive images */&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#75715e&#34;&gt;/* get file that matches the filename as specified as src=&amp;#34;&amp;#34;&amp;#34; in shortcode */&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; (.&lt;span style=&#34;color:#a6e22e&#34;&gt;Site&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetPage&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/headless-img&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Resources&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetMatch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;printf&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*%s**&amp;#34;&lt;/span&gt; (.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;) }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#75715e&#34;&gt;/* set image sizes, these are hardcoded for now, x dictates that images are resized to this width */&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tinyw&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;500x&amp;#34;&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;smallw&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;800x&amp;#34;&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mediumw&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1200x&amp;#34;&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;largew&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1500x&amp;#34;&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#75715e&#34;&gt;/* resize the src image to the given sizes */&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{{.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tiny&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Resize&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tinyw&lt;/span&gt;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{{.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;small&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Resize&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;smallw&lt;/span&gt;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{{.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;medium&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Resize&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;mediumw&lt;/span&gt;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{{.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Set&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;large&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Resize&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;largew&lt;/span&gt;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#75715e&#34;&gt;/* add the processed images to the scratch */&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tiny&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tiny&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;small&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;small&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;medium&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;medium&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;large&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;wide&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#75715e&#34;&gt;/* only use images smaller than or equal to the src (original) image size, as Hugo will upscale small images */&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#75715e&#34;&gt;/* set the sizes attribute to (min-width: 35em) 1200px, 100vw unless overridden in shortcode */&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#a6e22e&#34;&gt;img&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{{&lt;span style=&#34;color:#a6e22e&#34;&gt;with&lt;/span&gt; .&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sizes&amp;#34;&lt;/span&gt; }}&lt;span style=&#34;color:#a6e22e&#34;&gt;sizes&lt;/span&gt;=&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;{{.}}&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;{{ &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; }}&lt;span style=&#34;color:#a6e22e&#34;&gt;sizes&lt;/span&gt;=&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(min-width: 35em) 1200px, 100vw&amp;#34;&lt;/span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;srcset&lt;/span&gt;=&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ge&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Width&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;500&amp;#34;&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ &lt;span style=&#34;color:#a6e22e&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tiny&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RelPermalink&lt;/span&gt; }}{{{.}} &lt;span style=&#34;color:#ae81ff&#34;&gt;500&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;{{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ge&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Width&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;800&amp;#34;&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ &lt;span style=&#34;color:#a6e22e&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;small&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RelPermalink&lt;/span&gt; }}, {{.}} &lt;span style=&#34;color:#ae81ff&#34;&gt;800&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;{{{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ge&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Width&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1200&amp;#34;&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ &lt;span style=&#34;color:#a6e22e&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;medium&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RelPermalink&lt;/span&gt; }}, {{.}} &lt;span style=&#34;color:#ae81ff&#34;&gt;1200&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt;{{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ge&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Width&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1500&amp;#34;&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{&lt;span style=&#34;color:#a6e22e&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;large&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RelPermalink&lt;/span&gt; }}, {{.}} &lt;span style=&#34;color:#ae81ff&#34;&gt;1500&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;w&lt;/span&gt; {{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;medium&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{$medium.RelPermalink }}&amp;#34;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{{$src.RelPermalink }}}&amp;#34;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{{&lt;span style=&#34;color:#a6e22e&#34;&gt;with&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;alt&amp;#34;&lt;/span&gt; }}&lt;span style=&#34;color:#a6e22e&#34;&gt;alt&lt;/span&gt;=&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;{{.}}&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;{{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This template can be found on many sites as an example.&lt;/p&gt;
&lt;p&gt;The main difference with the others is in this line:&lt;br&gt;
&lt;code&gt;{{{$src := (.Site.GetPage &amp;quot;/headless-img&amp;quot;).Resources.GetMatch (printf &amp;quot;*%s*&amp;quot;) (.Get &amp;quot;src&amp;quot;) }}}&lt;/code&gt;&lt;br&gt;
which allows us to retrieve the resources of the &lt;code&gt;headless-img&lt;/code&gt; bundle.&lt;/p&gt;
&lt;p&gt;When this shortcode is executed, it will look for the image defined in the &lt;code&gt;headless-img/index.md&lt;/code&gt; file and automatically generate the declinations.&lt;/p&gt;
&lt;h4 id=&#34;and-now-how-do-i-display-this-on-my-homepage&#34;&gt;And now, how do I display this on my homepage?&lt;/h4&gt;
&lt;p&gt;All we have to do now is call our bundle in our template &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;
&lt;h5 id=&#34;if-you-have-only-one-single-indexmd-index-content&#34;&gt;If you have only one single index.md index content&lt;/h5&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;define&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{ &lt;span style=&#34;color:#75715e&#34;&gt;/* The rendering of the headless section. Assume */&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;headless&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Site&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetPage&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/headless-img&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{{&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;headless&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Content&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h5 id=&#34;if-you-need-to-render-multiplemd-files-based-on-a-name-img1-img2-by-example&#34;&gt;If you need to render multiple.md files based on a name. &lt;code&gt;img1&lt;/code&gt;, &lt;code&gt;img2&lt;/code&gt;&amp;hellip; by example&lt;/h5&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-golang&#34; data-lang=&#34;golang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;define&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;headless&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Site&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetPage&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/headless-img&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;reusablePages&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;headless&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Resources&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Match&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;img*&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{&lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;reusablePages&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{{.&lt;span style=&#34;color:#a6e22e&#34;&gt;Content&lt;/span&gt; }}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s it, now your images are well rendered on your homepage and their responsive versions are also available.&lt;/p&gt;
&lt;p&gt;Translated with &lt;a href=&#34;https://www.DeepL.com/Translator&#34;&gt;www.DeepL.com/Translator&lt;/a&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>Manage distinct profiles in Git</title>
      <link>https://gastaud.io/en/article/git-gerer-profils/</link>
      <pubDate>Tue, 02 Apr 2019 00:02:55 +0100</pubDate>
      
      <guid>https://gastaud.io/en/article/git-gerer-profils/</guid>
      
        <description>&lt;p&gt;Translate from French on &lt;a href=&#34;https://dev.to/jygastaud/manage-distinct-profiles-in-git-1b56&#34;&gt;dev.to&lt;/a&gt; and backport here.&lt;/p&gt;
&lt;p&gt;You use your computer to work and, more than ever, you probably use Git (what would you do on this article if not?).&lt;br&gt;
Perhaps you also work on a personal project on the same computer?&lt;br&gt;
Or to contribute to an Open-Source project, always on the same machine.&lt;/p&gt;
&lt;p&gt;You have probably defined a global identity (name + email) for your machine and overloaded it for each project according to the context.&lt;/p&gt;
&lt;p&gt;Perfect! Perfect!&lt;/p&gt;
&lt;p&gt;And yet despite these usual precautions, who has never commited in a project with a wrong identity?
Sometimes you may find this out late and your personal email is now &lt;em&gt;ad vitam æternam&lt;/em&gt; in the project logs.&lt;/p&gt;
&lt;p&gt;So how can we try to limit the risk of errors?&lt;/p&gt;
&lt;p&gt;Could we automate the selection of the right identity for our projects?&lt;/p&gt;
&lt;p&gt;Well, the good news is that &lt;strong&gt;the answer is YES&lt;/strong&gt; and we&amp;rsquo;ll see how right after that.&lt;/p&gt;
&lt;h2 id=&#34;gitconfig-and-conditional-includes-thanks-to-includeif&#34;&gt;.gitconfig and conditional includes thanks to IncludeIf&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s take the file &lt;code&gt;.gitconfig&lt;/code&gt;, usually present in your &lt;code&gt;$HOME&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[user]
	name = Jean-Yves Gastaud
	email = mon.super@mail.com

[alias]
  amend = commit --amend
  st = status
  co = checkout
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This file defines our default identity and global aliases.&lt;/p&gt;
&lt;p&gt;Thanks to the use of the &lt;code&gt;includeIf&lt;/code&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; directive in our &lt;code&gt;.gitconfig&lt;/code&gt; file, we can now add conditional includes linked, especially to directories.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[includeIf &amp;#34;gitdir:/workspace/work/&amp;#34;]
  path = ~/.gitconfig-work

[includeIf &amp;#34;gitdir:/workspace/opensource/&amp;#34;]
  path = ~/.gitconfig-opensource

[includeIf &amp;#34;gitdir:/workspace/perso/&amp;#34;]
  path = ~/.gitconfig-perso
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For each defined &lt;code&gt;includeIf&lt;/code&gt;, all we have to do is create the file corresponding to the path we defined in &lt;code&gt;path&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;~/.gitconfig-work&lt;/code&gt; file, we only have to add or override the configurations we are interested in.&lt;/p&gt;
&lt;p&gt;Example :&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[user]
        email = my.work@mail.com

[core]
        editor = &amp;#34;code -w&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this file, we overload our email and add a default code editor.&lt;/p&gt;
&lt;p&gt;Now, any folder under the &lt;code&gt;/workspace/work&lt;/code&gt; directory will automatically benefit from loading the configuration of the &lt;code&gt;.gitconfig&lt;/code&gt; file, overloading or supplementing with those of the &lt;code&gt;.gitconfig-work&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;To check your configuration, go to any directory under the target directory, &lt;code&gt;/workspace/work&lt;/code&gt; in our example and run the command &lt;code&gt;git config -l&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You should see the merging of your config files.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user.name=Jean-Yves Gastaud
email = my.super@mail.com
alias.amend=commit --amend
alias.st=status
alias.co=checkout
includeif.gitdir:/workspace/work/work/.path=~/.gitconfig-work
user.email=my.work@mail.com
core.editor=code -w
includeif.gitdir:/workspace/opensource/.path=~/.gitconfig-opensource
includeif.gitdir:/workspace/perso/.path=~/.gitconfig-perso
core.repositoryformatversion=0
core.filemode=true
…
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, the 2 defined emails are present in the listing.&lt;br&gt;
The last &amp;ldquo;speaking&amp;rdquo; being right, it is therefore the value of the file &lt;code&gt;.gitconfig-work&lt;/code&gt; which will be considered during the commit.&lt;/p&gt;
&lt;p&gt;You will also notice that the other 2 include are listed but do not add new variables / overloads because their application condition is not met.&lt;/p&gt;
&lt;p&gt;This always gives us the possibility to overload, locally at the repository, all desired configurations, as before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In a few lines and a simple management of your working directories, it is therefore possible for you to no longer risk making mistakes in changing your identities.&lt;/strong&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;Since Git, version 2.13 (2017, May). [Documentation on Includes in Git] (&lt;a href=&#34;https://git-scm.com/docs/git-config#_includes&#34;&gt;https://git-scm.com/docs/git-config#_includes&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;/ol&gt;
&lt;/div&gt;
</description>
      
    </item>
    
  </channel>
</rss>