<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>jenkins &amp;mdash; Jerry of the Week</title>
    <link>https://write.in0rdr.ch/tag:jenkins</link>
    <description>ˈdʒɛri - Individual who sends life against the grain no matter the consequences</description>
    <pubDate>Tue, 28 Apr 2026 13:27:14 +0000</pubDate>
    <item>
      <title>Parallel container builds with Jenkins scripted pipeline</title>
      <link>https://write.in0rdr.ch/parallel-container-builds-with-jenkins-scripted-pipeline</link>
      <description>&lt;![CDATA[Created a new pipeline for building my container images in the homelab. A dynamic choice populates a &#34;matrix&#34; that runs container build stages in parallel on runners with different architectures.&#xA;&#xA;#jenkins #containers #buildah&#xA;!--more--&#xA;&#xA;I was writing about how and why I use Jenkins last year.&#xA;&#xA;The basic idea behind the pipeline here is to build container images for multiple platforms/architectures (arm64 &amp; amd64 mainly).&#xA;&#xA;An advantage of using the runners native architecture (arm64/amd64) for the builds is that I don&#39;t need to use the QEMU emulation with buildah.&#xA;&#xA;Jenkinsfile with static nodes/stages &#xA;My previous Jenkinsfile (starting point) was very &#34;static&#34;. Static in a way, that the nodes were selected sequentially (first build on an arm64 node, then on a amd64 node) and I created two different tags for the different architectures:&#xA;@Library(&#39;in0rdr-jenkins-lib@master&#39;) &#xA;&#xA;def buildahbud = new BuildahBud(this)&#xA;def buildahpush = new BuildahPush(this)&#xA;def buildahmanifest = new BuildahManifest(this)&#xA;&#xA;node(&#39;podman&amp;&amp;arm64&#39;){&#xA;  checkout scm&#xA;&#xA;  // build with image context and name&#xA;  buildahbud.execute([:], &#34;docker/docker-snac&#34;, &#34;snac&#34;, &#34;2.90-arm64&#34;&#xA;    &#39;Dockerfile&#39;, &#39;arm64/v8&#39;)&#xA;  buildahpush.execute(&#34;snac&#34;, &#34;2.90-arm64&#34;)&#xA;}&#xA;&#xA;node(&#39;podman&amp;&amp;amd64&#39;){&#xA;  checkout scm&#xA;  buildahbud.execute([:], &#34;docker/docker-snac&#34;, &#34;snac&#34;, &#34;2.90-amd64&#34;,&#xA;    &#39;Dockerfile&#39;, &#39;amd64&#39;)&#xA;  buildahmanifest.create(&#39;haproxy.lan:5000/snac:2.90&#39;, [&#xA;    &#39;haproxy.lan:5000/snac:2.90-arm64&#39;,&#xA;    &#39;haproxy.lan:5000/snac:2.90-amd64&#39;&#xA;  ])&#xA;}&#xA;&#xA;You can find my Jenkins libraries here:&#xA;https://code.in0rdr.ch/jenkins-lib/files.html&#xA;&#xA;Pulling a different tag depending on the architecture is cumbersome. By creating a Manifest ([buildah manifest create](https://github.com/containers/buildah/blob/main/docs/buildah-manifest-create.1.md&#xA;)) I can reuse the same tag across multiple machine architectures (e.g., I can use haproxy.lan:5000/snac:2.90 on amd and arm machines):&#xA;buildah manifest inspect haproxy.lan:5000/snac:2.90&#xA;{&#xA;    &#34;schemaVersion&#34;: 2,&#xA;    &#34;mediaType&#34;: &#34;application/vnd.oci.image.index.v1+json&#34;,&#xA;    &#34;manifests&#34;: [&#xA;        {&#xA;            &#34;mediaType&#34;: &#34;application/vnd.oci.image.manifest.v1+json&#34;,&#xA;            &#34;digest&#34;: &#34;sha256:7f67daa2193f2ef9a84c3bcaa14c73d429165631cbbc6c30faf74ef69ac07d4a&#34;,&#xA;            &#34;size&#34;: 1207,&#xA;            &#34;platform&#34;: {&#xA;                &#34;architecture&#34;: &#34;arm64&#34;,&#xA;                &#34;os&#34;: &#34;linux&#34;,&#xA;                &#34;variant&#34;: &#34;v8&#34;&#xA;            }&#xA;        },&#xA;        {&#xA;            &#34;mediaType&#34;: &#34;application/vnd.oci.image.manifest.v1+json&#34;,&#xA;            &#34;digest&#34;: &#34;sha256:85b79a9014eed10f8b12cddd9f6fcf9a0e56cdf792b96cf4cde52c9c8f61c4f7&#34;,&#xA;            &#34;size&#34;: 1204,&#xA;            &#34;platform&#34;: {&#xA;                &#34;architecture&#34;: &#34;amd64&#34;,&#xA;                &#34;os&#34;: &#34;linux&#34;&#xA;            }&#xA;        }&#xA;    ]&#xA;}&#xA;&#xA;Dynamic choice, run stages on nodes in parallel&#xA;&#xA;I decided to change my static Jenkinsfile and spice it up with the dynamic choices based on the &#34;input matrix&#34;. The &#34;platform&#34; axis only holds the &#34;podman&#34; platform right now.&#xA;https://code.in0rdr.ch/jenkins-lib/file/src/BuildahParallelBuild.groovy.html&#xA;&#xA;buildahparallelbuild-choice.jpg&#xA;&#xA;For this, I took some inspiration from an older Jenkins blog post from 2019. The code example for a scripted pipeline with &#34;dynamic choices&#34; (choices based on predefined matrix axes) still works pretty fine.&#xA;&#xA;My inputs can now be kept rather simple, they simply describe which Dockerfile I want to build, from which path in the repo, with certain name &amp; tag (optionally some build arguments):&#xA;&#xA;@Library(&#39;in0rdr-jenkins-lib@master&#39;) &#xA;&#xA;def buildahParallelBuild = new BuildahParallelBuild(this)&#xA;&#xA;buildahParallelBuild.build(&#34;snac&#34;, &#34;2.90&#34;, &#34;docker/docker-snac&#34;)&#xA;&#xA;// Other example inputs:&#xA;//buildahParallelBuild.build(&#34;texlive&#34;, &#34;latest&#34;, &#34;docker/docker-texlive&#34;)&#xA;//buildahParallelBuild.build(&#34;updatecli&#34;, &#34;v0.114.0&#34;, &#34;docker/docker-updatecli&#34;)&#xA;//buildahParallelBuild.build(&#34;jenkins-inbound-agent&#34;, &#34;3355.v388858a47b33&#34;, &#34;docker/docker-jenkins-inbound-agent&#34;, [arg1: &#34;test&#34;, arg2: &#34;test2&#34;])&#xA;&#xA;buildahparallelbuild-pipeline.jpg&#xA;&#xA;It doesn&#39;t matter on which architecture I create the manifest.&#xA;&#xA;I&#39;m still a huge fan of the extensibility of these Jenkins libraries. By simply reusing my existing libraries for Buildah bud (class BuildahBud) / push (class BuildahPush) / manifest creation (class BuildahManifest) from my already working &#34;static&#34; use case, I could extend quickly with the &#34;parallel build&#34; functionality (in a new/separate class which simply imports the existing functionality) 🤗.&#xA;&#xA;Later I might figure out more about @NonCPS and NonCPS best practices.. hit me up if you would like to educate me 🤔&#xA;&#xA;Thanks for reading&#xA;&#xA;div style=&#34;text-align:center; font-size: 0.8em&#34;&#xD;&#xA;a href=&#34;https://write.in0rdr.ch/feed&#34;&amp;#128732; RSS/a | a href=&#34;https://m.in0rdr.ch/in0rdr&#34;&amp;#128024; Fediverse/a | a href=&#34;https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch&#34;&amp;#128172; XMPP/a&#xD;&#xA;/div]]&gt;</description>
      <content:encoded><![CDATA[<p>Created a new pipeline for building my container images in the homelab. A <a href="https://www.jenkins.io/blog/2019/12/02/matrix-building-with-scripted-pipeline/#full-pipeline-example-with-dynamic-choices">dynamic choice</a> populates a “matrix” that runs container build stages in parallel on runners with different architectures.</p>

<p><a href="https://write.in0rdr.ch/tag:jenkins" class="hashtag"><span>#</span><span class="p-category">jenkins</span></a> <a href="https://write.in0rdr.ch/tag:containers" class="hashtag"><span>#</span><span class="p-category">containers</span></a> <a href="https://write.in0rdr.ch/tag:buildah" class="hashtag"><span>#</span><span class="p-category">buildah</span></a>
</p>

<p>I was writing about <a href="https://write.in0rdr.ch/jenkins-works">how and why I use Jenkins last year</a>.</p>

<p>The basic idea behind the pipeline here is to build container images for multiple platforms/architectures (arm64 &amp; amd64 mainly).</p>

<p>An advantage of using the runners native architecture (arm64/amd64) for the builds is that I don&#39;t need to use the <a href="https://github.com/containers/buildah/discussions/4736">QEMU emulation with buildah</a>.</p>

<h2 id="jenkinsfile-with-static-nodes-stages">Jenkinsfile with static nodes/stages</h2>

<p>My previous Jenkinsfile (starting point) was very “static”. Static in a way, that the nodes were selected sequentially (first build on an arm64 node, then on a amd64 node) and I created two different tags for the different architectures:</p>

<pre><code class="language-groovy">@Library(&#39;in0rdr-jenkins-lib@master&#39;) _

def buildahbud = new BuildahBud(this)
def buildahpush = new BuildahPush(this)
def buildahmanifest = new BuildahManifest(this)

node(&#39;podman&amp;&amp;arm64&#39;){
  checkout scm

  // build with image context and name
  buildahbud.execute([:], &#34;docker/docker-snac&#34;, &#34;snac&#34;, &#34;2.90-arm64&#34;
    &#39;Dockerfile&#39;, &#39;arm64/v8&#39;)
  buildahpush.execute(&#34;snac&#34;, &#34;2.90-arm64&#34;)
}

node(&#39;podman&amp;&amp;amd64&#39;){
  checkout scm
  buildahbud.execute([:], &#34;docker/docker-snac&#34;, &#34;snac&#34;, &#34;2.90-amd64&#34;,
    &#39;Dockerfile&#39;, &#39;amd64&#39;)
  buildahmanifest.create(&#39;haproxy.lan:5000/snac:2.90&#39;, [
    &#39;haproxy.lan:5000/snac:2.90-arm64&#39;,
    &#39;haproxy.lan:5000/snac:2.90-amd64&#39;
  ])
}
</code></pre>

<p>You can find my Jenkins libraries here:
* <a href="https://code.in0rdr.ch/jenkins-lib/files.html">https://code.in0rdr.ch/jenkins-lib/files.html</a></p>

<p>Pulling a different tag depending on the architecture is cumbersome. By creating a Manifest (<a href="https://github.com/containers/buildah/blob/main/docs/buildah-manifest-create.1.md"><code>buildah manifest create</code></a>) I can reuse the same tag across multiple machine architectures (e.g., I can use <code>haproxy.lan:5000/snac:2.90</code> on amd and arm machines):</p>

<pre><code class="language-bash">buildah manifest inspect haproxy.lan:5000/snac:2.90
</code></pre>

<pre><code class="language-json">{
    &#34;schemaVersion&#34;: 2,
    &#34;mediaType&#34;: &#34;application/vnd.oci.image.index.v1+json&#34;,
    &#34;manifests&#34;: [
        {
            &#34;mediaType&#34;: &#34;application/vnd.oci.image.manifest.v1+json&#34;,
            &#34;digest&#34;: &#34;sha256:7f67daa2193f2ef9a84c3bcaa14c73d429165631cbbc6c30faf74ef69ac07d4a&#34;,
            &#34;size&#34;: 1207,
            &#34;platform&#34;: {
                &#34;architecture&#34;: &#34;arm64&#34;,
                &#34;os&#34;: &#34;linux&#34;,
                &#34;variant&#34;: &#34;v8&#34;
            }
        },
        {
            &#34;mediaType&#34;: &#34;application/vnd.oci.image.manifest.v1+json&#34;,
            &#34;digest&#34;: &#34;sha256:85b79a9014eed10f8b12cddd9f6fcf9a0e56cdf792b96cf4cde52c9c8f61c4f7&#34;,
            &#34;size&#34;: 1204,
            &#34;platform&#34;: {
                &#34;architecture&#34;: &#34;amd64&#34;,
                &#34;os&#34;: &#34;linux&#34;
            }
        }
    ]
}
</code></pre>

<h2 id="dynamic-choice-run-stages-on-nodes-in-parallel">Dynamic choice, run stages on nodes in parallel</h2>

<p>I decided to change my static Jenkinsfile and spice it up with the dynamic choices based on the “input matrix”. The “platform” axis only holds the “podman” platform right now.
* <a href="https://code.in0rdr.ch/jenkins-lib/file/src/BuildahParallelBuild.groovy.html">https://code.in0rdr.ch/jenkins-lib/file/src/BuildahParallelBuild.groovy.html</a></p>

<p><img src="https://code.in0rdr.ch/pub/blog/buildahparallelbuild-choice.jpg" alt="buildahparallelbuild-choice.jpg"></p>

<p>For this, I took some inspiration from an older <a href="https://www.jenkins.io/blog/2019/12/02/matrix-building-with-scripted-pipeline/#full-pipeline-example-with-dynamic-choices">Jenkins blog post from 2019</a>. The code example for a scripted pipeline with “dynamic choices” (choices based on predefined matrix axes) still works pretty fine.</p>

<p>My inputs can now be kept rather simple, they simply describe which Dockerfile I want to build, from which path in the repo, with certain name &amp; tag (optionally some build arguments):</p>

<pre><code class="language-groovy">@Library(&#39;in0rdr-jenkins-lib@master&#39;) _

def buildahParallelBuild = new BuildahParallelBuild(this)

buildahParallelBuild.build(&#34;snac&#34;, &#34;2.90&#34;, &#34;docker/docker-snac&#34;)

// Other example inputs:
//buildahParallelBuild.build(&#34;texlive&#34;, &#34;latest&#34;, &#34;docker/docker-texlive&#34;)
//buildahParallelBuild.build(&#34;updatecli&#34;, &#34;v0.114.0&#34;, &#34;docker/docker-updatecli&#34;)
//buildahParallelBuild.build(&#34;jenkins-inbound-agent&#34;, &#34;3355.v388858a_47b_33&#34;, &#34;docker/docker-jenkins-inbound-agent&#34;, [arg1: &#34;test&#34;, arg2: &#34;test2&#34;])
</code></pre>

<p><img src="https://code.in0rdr.ch/pub/blog/buildahparallelbuild-pipeline.jpg" alt="buildahparallelbuild-pipeline.jpg"></p>

<p>It doesn&#39;t matter on which architecture I create the manifest.</p>

<p>I&#39;m still a huge fan of the extensibility of these Jenkins libraries. By simply reusing my existing libraries for Buildah bud (class <code>BuildahBud</code>) / push (class <code>BuildahPush</code>) / manifest creation (class <code>BuildahManifest</code>) from my already working “static” use case, I could extend quickly with the “parallel build” functionality (in a new/separate class which simply imports the existing functionality) 🤗.</p>

<p>Later I might figure out more about <a href="https://www.jenkins.io/doc/book/pipeline/cps-method-mismatches/"><code>@NonCPS</code></a> and <a href="https://www.jenkins.io/doc/book/pipeline/pipeline-best-practices/#using-noncps"><code>NonCPS</code> best practices</a>.. hit me up if you would like to educate me 🤔</p>

<p>Thanks for reading</p>

<div style="text-align:center; font-size: 0.8em">
<a href="https://write.in0rdr.ch/feed">🛜 RSS</a> | <a href="https://m.in0rdr.ch/in0rdr">🐘 Fediverse</a> | <a href="https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch">💬 XMPP</a>
</div>
]]></content:encoded>
      <guid>https://write.in0rdr.ch/parallel-container-builds-with-jenkins-scripted-pipeline</guid>
      <pubDate>Wed, 18 Mar 2026 19:48:12 +0000</pubDate>
    </item>
    <item>
      <title>Nomad authentication with OpenBao</title>
      <link>https://write.in0rdr.ch/nomad-authentication-with-openbao</link>
      <description>&lt;![CDATA[I started to use OpenBao as OpenID connect provider to authenticate my Nomad home lab.&#xA;&#xA;#nomad #openbao #jenkins #homelab&#xA;!--more--&#xA;&#xA;I have two automatic/system jobs that require a Token.&#xA;&#xA;The backup cron job is taking Nomad snapshots in regular intervals. The snapshot API requires a management token and the Nomad policy capability for snapshots with the operator are not implemented yet.&#xA;The Jenkins server runs Nomad jobs using the Nomad cloud plugin. This system needs a Token to access the Nomad API (AppRole not compatible, see below).&#xA;&#xA;I still keep my bootstrapping token around just in case I ever need it. That&#39;s ok unlike to procedures with OpenBao root tokens..&#xA;&#xA;  The bootstrap token can be deleted and is like any other token, care should be taken to not revoke all management tokens.&#xA;&#xA;Name             Type        Global  Accessor ID  Expired&#xA;Bootstrap Token  management  true              false&#xA;Snapshot         management  false             false&#xA;OIDC-vault       client      true              false&#xA;Jenkins          client      false             false&#xA;&#xA;The auth method in Nomad is still called Vault, never mind..&#xA;&#xA;The access for human users is authenticated by an OIDC provider in my OpenBao server.&#xA;&#xA;Because I already had an identity and alias setup (userpass authentication) including a group, I only needed to configure the provider and the assignment to the group to allow authentication with the new provider.&#xA;&#xA;In the OpenBao server, the default provider and the allowall assignment cannot be deleted. I assume it is similar to the “master” realm in a Keycloak instance 🤔.&#xA;&#xA;bao-openidconnect-vault.svg&#xA;&#xA;I had to define a NOMADTOKEN as “Jenkins credential”, because the Nomad cloud plugin for Jenkins cannot read secrets from OpenBao using an AppRole (the Nomad jobs spawned by the plugin can do this, just not the plugin itself).&#xA;&#xA;When I type nomad login in the shell, the browser opens and I can authenticate with OpenBao. What could be improved is outputting the OIDC redirect URI in the terminal. This is helpful when you need to login from disconnected machines (i.e., not the shell on your local machine).&#xA;&#xA;div style=&#34;text-align:center; font-size: 0.8em&#34;&#xD;&#xA;a href=&#34;https://write.in0rdr.ch/feed&#34;&amp;#128732; RSS/a | a href=&#34;https://m.in0rdr.ch/in0rdr&#34;&amp;#128024; Fediverse/a | a href=&#34;https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch&#34;&amp;#128172; XMPP/a&#xD;&#xA;/div]]&gt;</description>
      <content:encoded><![CDATA[<p>I started to use <a href="https://openbao.org/docs/secrets/identity/oidc-provider">OpenBao as OpenID connect provider</a> to authenticate my Nomad home lab.</p>

<p><a href="https://write.in0rdr.ch/tag:nomad" class="hashtag"><span>#</span><span class="p-category">nomad</span></a> <a href="https://write.in0rdr.ch/tag:openbao" class="hashtag"><span>#</span><span class="p-category">openbao</span></a> <a href="https://write.in0rdr.ch/tag:jenkins" class="hashtag"><span>#</span><span class="p-category">jenkins</span></a> <a href="https://write.in0rdr.ch/tag:homelab" class="hashtag"><span>#</span><span class="p-category">homelab</span></a>
</p>

<p>I have two automatic/system jobs that require a Token.</p>
<ul><li>The backup cron job is taking Nomad snapshots in regular intervals. The <a href="https://developer.hashicorp.com/nomad/api-docs/operator/snapshot">snapshot API</a> requires a management token and the <a href="https://github.com/hashicorp/nomad/issues/23614">Nomad policy capability for snapshots with the operator</a> are not implemented yet.</li>
<li>The Jenkins server runs Nomad jobs using the <a href="https://github.com/jenkinsci/nomad-plugin">Nomad cloud plugin</a>. This system needs a Token to access the Nomad API (AppRole not compatible, see below).</li></ul>

<p>I still keep my bootstrapping token around just in case I ever need it. That&#39;s <a href="https://developer.hashicorp.com/nomad/tutorials/archive/access-control-bootstrap">ok</a> unlike to procedures with OpenBao root tokens..</p>

<blockquote><p>The bootstrap token can be deleted and is like any other token, care should be taken to not revoke all management tokens.</p></blockquote>

<pre><code>Name             Type        Global  Accessor ID  Expired
Bootstrap Token  management  true    ***          false
Snapshot         management  false   ***          false
OIDC-vault       client      true    ***          false
Jenkins          client      false   ***          false
</code></pre>

<p>The auth method in Nomad is still called Vault, never mind..</p>

<p>The access for human users is authenticated by an <a href="https://developer.hashicorp.com/nomad/tutorials/archive/sso-oidc-vault">OIDC provider in my OpenBao server</a>.</p>

<p>Because I already had an identity and alias setup (userpass authentication) including a group, I only needed to configure the provider and the assignment to the group to allow authentication with the new provider.</p>

<p>In the OpenBao server, the <a href="https://openbao.org/docs/concepts/oidc-provider/#oidc-providers"><code>default</code> provider</a> and the <a href="https://openbao.org/docs/concepts/oidc-provider/#assignments"><code>allow_all</code> assignment</a> cannot be deleted. I assume it is similar to the “master” realm in a Keycloak instance 🤔.</p>

<p><img src="https://code.in0rdr.ch/pub/blog/bao-openidconnect-vault.svg" alt="bao-openidconnect-vault.svg"></p>

<p>I had to define a <code>NOMAD_TOKEN</code> as “Jenkins credential”, because the Nomad cloud plugin for Jenkins cannot read secrets from OpenBao using an AppRole (the Nomad jobs spawned by the plugin can do this, just not the plugin itself).</p>

<p>When I type <code>nomad login</code> in the shell, the browser opens and I can authenticate with OpenBao. What could be improved is outputting the OIDC redirect URI in the terminal. This is helpful when you need to login from disconnected machines (i.e., not the shell on your local machine).</p>

<div style="text-align:center; font-size: 0.8em">
<a href="https://write.in0rdr.ch/feed">🛜 RSS</a> | <a href="https://m.in0rdr.ch/in0rdr">🐘 Fediverse</a> | <a href="https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch">💬 XMPP</a>
</div>
]]></content:encoded>
      <guid>https://write.in0rdr.ch/nomad-authentication-with-openbao</guid>
      <pubDate>Sat, 15 Nov 2025 20:22:29 +0000</pubDate>
    </item>
    <item>
      <title>Bump NPM dependencies with Updatecli</title>
      <link>https://write.in0rdr.ch/bump-npm-dependencies-with-updatecli</link>
      <description>&lt;![CDATA[I built a new Jenkins pipeline based on Updatecli for updating the NPM packages in my hobby project MyHeats.&#xA;&#xA;#updatecli #pipeline #jenkins #myheats #nodejs #npm&#xA;!--more--&#xA;&#xA;I was looking for a way to automatically bump the version of the npm dependencies (package.json) whenever there is an update available. This is also important for security reasons (e.g., have a look at the output of npm audit from time to time to see the recent security issues in the dependencies).&#xA;&#xA;I was looking into Renovate and Dependabot, but neither of these scratched my itch of simple automatic dependency updates.&#xA;&#xA;A coworker suggested me to try Updatecli and it fits my workflows perfectly well. The Jenkins example on the projects website got me started. So I created a Jenkins shared library function to run my own build, which includes npm to perform the version bumps:&#xA;&#xA;A class to describe the updatecli stages: https://code.in0rdr.ch/jenkins-lib/file/src/Updatecli.groovy.html&#xA;&#xA;The scripted pipeline in the repository of the application loads the library and performs the version bumps to a new branch:&#xA;&#xA;The Jenkinsfile that makes use of the updatecli groovy library: https://code.in0rdr.ch/myheats/file/Jenkinsfile.html&#xA;&#xA;I did not even have to configure Updatecli a lot, because the autodiscovery feature automatically detects that this is a npm repository/project. The final version of my pipeline includes all the git/scm steps in the updatecli.d/default.yaml configuration file:&#xA;&#xA;Updatecli configuration file: https://code.in0rdr.ch/myheats/file/updatecli.d/default.yaml.html&#xA;&#xA;First I tried to perform the SCM/git steps in Jenkins checkout and sh steps. But I noticed it could be much sleeker by defining the SCM/git settings in the Updatecli config file directly. This way, updatecli takes care of the clone/checkout/push steps. Here the extract from my previous pipeline with the &#34;manual git steps&#34; for comparison:&#xA;&#xA;// alternative approach I did not pursue any further&#xA;sh &#39;&#39;&#39;&#xA;git config --global user.name &#34;$GITAUTHORNAME&#34;&#xA;git config --global user.email &#34;$GITAUTHOREMAIL&#34;&#xA;&#39;&#39;&#39;&#xA;&#xA;dir(&#34;myyheats.git-$BUILDNUMBER&#34;) {&#xA;  // checkout update branch in new directory&#xA;  checkout scmGit(&#xA;      extensions: [localBranch(&#34;$branch&#34;)],&#xA;      userRemoteConfigs: [[url: &#39;https://git.in0rdr.ch/myheats.git&#39;]]&#xA;  )&#xA;&#xA;  updatecli.run(&#39;apply&#39;)&#xA;&#xA;  // commit changes&#xA;  sh &#39;&#39;&#39;&#xA;  git add -u&#xA;  git commit -m &#34;chore(updatecli-$BUILDNUMBER): bump node modules&#34;&#xA;  git push -f -u origin &#34;$branch&#34;&#xA;  &#39;&#39;&#39;&#xA;}&#xA;&#xA;I definitely like the updatecli configuration better, since it keeps the actual pipeline tidy. Also, I like how you can use the {{ requiredEnv &#34;GIT_PASSWORD&#34; }} configuration in updatecli to read secrets from the environment. The Git credentials are sourced from OpenBao with Nomad workload identities.&#xA;&#xA;I hope the post is helpful for anyone that would like to give updatecli a try or would like to configure a similar Jenkins pipeline.&#xA;&#xA;div style=&#34;text-align:center; font-size: 0.8em&#34;&#xD;&#xA;a href=&#34;https://write.in0rdr.ch/feed&#34;&amp;#128732; RSS/a | a href=&#34;https://m.in0rdr.ch/in0rdr&#34;&amp;#128024; Fediverse/a | a href=&#34;https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch&#34;&amp;#128172; XMPP/a&#xD;&#xA;/div]]&gt;</description>
      <content:encoded><![CDATA[<p>I built a new Jenkins pipeline based on <a href="https://www.updatecli.io">Updatecli</a> for updating the NPM packages in my hobby project <a href="https://myheats.p0c.ch">MyHeats</a>.</p>

<p><a href="https://write.in0rdr.ch/tag:updatecli" class="hashtag"><span>#</span><span class="p-category">updatecli</span></a> <a href="https://write.in0rdr.ch/tag:pipeline" class="hashtag"><span>#</span><span class="p-category">pipeline</span></a> <a href="https://write.in0rdr.ch/tag:jenkins" class="hashtag"><span>#</span><span class="p-category">jenkins</span></a> <a href="https://write.in0rdr.ch/tag:myheats" class="hashtag"><span>#</span><span class="p-category">myheats</span></a> <a href="https://write.in0rdr.ch/tag:nodejs" class="hashtag"><span>#</span><span class="p-category">nodejs</span></a> <a href="https://write.in0rdr.ch/tag:npm" class="hashtag"><span>#</span><span class="p-category">npm</span></a>
</p>

<p>I was looking for a way to automatically bump the version of the npm dependencies (<code>package.json</code>) whenever there is an update available. This is also important for security reasons (e.g., have a look at the output of <code>npm audit</code> from time to time to see the recent security issues in the dependencies).</p>

<p>I was looking into <a href="https://github.com/renovatebot/renovate">Renovate</a> and <a href="https://github.com/dependabot">Dependabot</a>, but neither of these scratched my itch of simple automatic dependency updates.</p>

<p>A coworker suggested me to try <a href="https://www.updatecli.io">Updatecli</a> and it fits my workflows perfectly well. The <a href="https://www.updatecli.io/docs/automate/jenkins">Jenkins example</a> on the projects website got me started. So I created a <a href="https://www.jenkins.io/doc/book/pipeline/shared-libraries">Jenkins shared library function</a> to run my own build, which includes <code>npm</code> to perform the version bumps:</p>
<ul><li>A class to describe the updatecli stages: <a href="https://code.in0rdr.ch/jenkins-lib/file/src/Updatecli.groovy.html">https://code.in0rdr.ch/jenkins-lib/file/src/Updatecli.groovy.html</a></li></ul>

<p>The scripted pipeline in the repository of the application loads the library and performs the version bumps to a new branch:</p>
<ul><li>The Jenkinsfile that makes use of the updatecli groovy library: <a href="https://code.in0rdr.ch/myheats/file/Jenkinsfile.html">https://code.in0rdr.ch/myheats/file/Jenkinsfile.html</a></li></ul>

<p>I did not even have to configure Updatecli a lot, because the <a href="https://www.updatecli.io/docs/core/autodiscovery">autodiscovery feature</a> automatically detects that this is a npm repository/project. The final version of my pipeline includes all the git/scm steps in the <code>updatecli.d/default.yaml</code> configuration file:</p>
<ul><li>Updatecli configuration file: <a href="https://code.in0rdr.ch/myheats/file/updatecli.d/default.yaml.html">https://code.in0rdr.ch/myheats/file/updatecli.d/default.yaml.html</a></li></ul>

<p>First I tried to perform the SCM/git steps in Jenkins <code>checkout</code> and <code>sh</code> steps. But I noticed it could be much sleeker by defining the SCM/git settings in the Updatecli config file directly. This way, updatecli takes care of the clone/checkout/push steps. Here the extract from my previous pipeline with the “manual git steps” for comparison:</p>

<pre><code class="language-java">// alternative approach I did not pursue any further
sh &#39;&#39;&#39;
git config --global user.name &#34;$GIT_AUTHOR_NAME&#34;
git config --global user.email &#34;$GIT_AUTHOR_EMAIL&#34;
&#39;&#39;&#39;

dir(&#34;myyheats.git-$BUILD_NUMBER&#34;) {
  // checkout update branch in new directory
  checkout scmGit(
      extensions: [localBranch(&#34;$branch&#34;)],
      userRemoteConfigs: [[url: &#39;https://git.in0rdr.ch/myheats.git&#39;]]
  )

  updatecli.run(&#39;apply&#39;)

  // commit changes
  sh &#39;&#39;&#39;
  git add -u
  git commit -m &#34;chore(updatecli-$BUILD_NUMBER): bump node modules&#34;
  git push -f -u origin &#34;$branch&#34;
  &#39;&#39;&#39;
}
</code></pre>

<p>I definitely like the <a href="https://code.in0rdr.ch/myheats/file/updatecli.d/default.yaml.html">updatecli configuration</a> better, since it keeps the actual pipeline tidy. Also, I like how you can use the <code>{{ requiredEnv &#34;GIT_PASSWORD&#34; }}</code> configuration in updatecli to read secrets from the environment. The Git credentials are sourced from OpenBao with Nomad workload identities.</p>

<p>I hope the post is helpful for anyone that would like to give updatecli a try or would like to configure a similar Jenkins pipeline.</p>

<div style="text-align:center; font-size: 0.8em">
<a href="https://write.in0rdr.ch/feed">🛜 RSS</a> | <a href="https://m.in0rdr.ch/in0rdr">🐘 Fediverse</a> | <a href="https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch">💬 XMPP</a>
</div>
]]></content:encoded>
      <guid>https://write.in0rdr.ch/bump-npm-dependencies-with-updatecli</guid>
      <pubDate>Fri, 26 Jul 2024 20:50:19 +0000</pubDate>
    </item>
    <item>
      <title>Jenkins works</title>
      <link>https://write.in0rdr.ch/jenkins-works</link>
      <description>&lt;![CDATA[I was dabbling around with Jenkins in my Nomad cluster lately. In this post I quickly share my experiences and what I learned along the way.&#xA;&#xA;#cicd #coding #jenkins #nomad&#xA;!--more--&#xA;&#xA;You may ask why I spend time to setup a good old Jenkins while everyone else seems to jump on some newer CI systems (GitLab, Forgejo, etc.)? Well, as said, it&#39;s still good old Jenkins and I assume it will be around for some more time.&#xA;&#xA;To run Jenkins agents on a Nomad cluster I followed Ola Ogunseghas instructions and example code here:&#xA;&#xA;https://faun.pub/jenkins-build-agents-on-nomad-workers-626b0df4fc57&#xA;https://github.com/GastroGee/jenkins-nomad/blob/main/jenkins-controller/nomad.yaml&#xA;&#xA;It involves installing the Nomad plugin for Jenkins and configuring this &#34;Nomad cloud&#34; (that&#39;s how the integration is called in Jenkins) with a template for the Jenkins agents.&#xA;&#xA;Obviously, I wanted to integrate Jenkins with my Git repos. The most straight forward way seemed to use post-receive hooks to nudge Jenkins on every push. This has worked fabulously so far.&#xA;&#xA;Even though the runners are spawned as Nomad jobs, I still wanted to run other Docker containers in the pipeline. This is where it got very confusing for me, because of the different Docker plugins for Jenkins. Most notably, there exist at least these two plugins:&#xA;&#xA;docker-workflow, runs the Docker container from the Jenkins agent&#xA;docker-plugin, runs Jenkins agents as Docker containers&#xA;&#xA;I decided to go with the former &#34;docker-workflow&#34; plugin because I already deployed the Jenkins agents as Nomad jobs. docker-workflow can run arbitrary containers from any Docker image, whereas the docker-plugin needs to be based on the Jenkins inbound-agent image to be able to connect to the Jenkins server.&#xA;&#xA;I wanted the containers that are launched from Jenkins to be contained in a another users namespace. There exists the option to rebuild the inbound-agent with user-supplied attributes for uid/gid, but since I wanted to modify the image anyways I simply forked and built my own agent images, mostly inspired by the example for running Docker inside the agent.&#xA;&#xA;At this stage, my tooling was sophisticated enough I could run a simple gitleaks container on each push to scan for secrets. I&#39;m always afraid to publish secrets accidentally (it happened to me before).&#xA;&#xA;Furthermore, I needed to establish some kind of build process, so I can also build images with Jenkins and directly push them to my local image registry. After some reading, I discarded the thought to use Kaniko because it still requires nodes in the native architecture of the respective build for multi-architecture builds.&#xA;&#xA;Therefore, I followed RedHat best-practices to integrate Buildah, which is also the tool I use to build multi-arch container images locally on my laptop.&#xA;&#xA;If you are interested in more example code, here the link to some of the key components:&#xA;&#xA;jenkins.yaml.tmpl.html Jenkins infrastructure code&#xA;jenkins.nomad.html Jenkins nomad job&#xA;docker-agent Modified Jenkins inbound-agent with Docker and Buildah&#xA;&#xA;I&#39;m not finished with playing around, because I still haven&#39;t figured out some things fully yet (e.g., how to properly use jlink in multi-arch builds) and because I&#39;m also curious how other members of the community use Jenkins on Nomad.&#xA;&#xA;div style=&#34;text-align:center; font-size: 0.8em&#34;&#xD;&#xA;a href=&#34;https://write.in0rdr.ch/feed&#34;&amp;#128732; RSS/a | a href=&#34;https://m.in0rdr.ch/in0rdr&#34;&amp;#128024; Fediverse/a | a href=&#34;https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch&#34;&amp;#128172; XMPP/a&#xD;&#xA;/div]]&gt;</description>
      <content:encoded><![CDATA[<p>I was dabbling around with Jenkins in my Nomad cluster lately. In this post I quickly share my experiences and what I learned along the way.</p>

<p><a href="https://write.in0rdr.ch/tag:cicd" class="hashtag"><span>#</span><span class="p-category">cicd</span></a> <a href="https://write.in0rdr.ch/tag:coding" class="hashtag"><span>#</span><span class="p-category">coding</span></a> <a href="https://write.in0rdr.ch/tag:jenkins" class="hashtag"><span>#</span><span class="p-category">jenkins</span></a> <a href="https://write.in0rdr.ch/tag:nomad" class="hashtag"><span>#</span><span class="p-category">nomad</span></a>
</p>

<p>You may ask why I spend time to setup a good old Jenkins while everyone else seems to jump on some newer CI systems (GitLab, Forgejo, etc.)? Well, as said, it&#39;s still good old Jenkins and I assume it will be around for some more time.</p>

<p>To run Jenkins agents on a Nomad cluster I followed Ola Ogunseghas instructions and example code here:</p>
<ul><li><a href="https://faun.pub/jenkins-build-agents-on-nomad-workers-626b0df4fc57">https://faun.pub/jenkins-build-agents-on-nomad-workers-626b0df4fc57</a></li>
<li><a href="https://github.com/GastroGee/jenkins-nomad/blob/main/jenkins-controller/nomad.yaml">https://github.com/GastroGee/jenkins-nomad/blob/main/jenkins-controller/nomad.yaml</a></li></ul>

<p>It involves installing the <a href="https://plugins.jenkins.io/nomad">Nomad plugin for Jenkins</a> and configuring this “Nomad cloud” (that&#39;s how the integration is called in Jenkins) with a template for the Jenkins agents.</p>

<p>Obviously, I wanted to integrate Jenkins with my <a href="https://code.in0rdr.ch">Git repos</a>. The most straight forward way seemed to use <a href="https://plugins.jenkins.io/git/#plugin-content-push-notification-from-repository">post-receive hooks</a> to nudge Jenkins on every push. This has worked fabulously so far.</p>

<p>Even though the runners are spawned as Nomad jobs, I still wanted to run other Docker containers in the pipeline. This is where it got very confusing for me, because of the different Docker plugins for Jenkins. Most notably, there exist at least these two plugins:</p>
<ul><li><a href="https://plugins.jenkins.io/docker-workflow">docker-workflow</a>, runs the Docker container from the Jenkins agent</li>
<li><a href="https://plugins.jenkins.io/docker-plugin">docker-plugin</a>, runs Jenkins agents as Docker containers</li></ul>

<p>I decided to go with the former “docker-workflow” plugin because I already deployed the Jenkins agents as Nomad jobs. <code>docker-workflow</code> can run arbitrary containers from any Docker image, whereas the <code>docker-plugin</code> needs to be based on the <a href="https://hub.docker.com/r/jenkins/inbound-agent">Jenkins <code>inbound-agent</code> image</a> to be able to connect to the Jenkins server.</p>

<p>I wanted the containers that are launched from Jenkins to be contained in a another users namespace. There exists the option to rebuild the <code>inbound-agent</code> with user-supplied attributes for uid/gid, but since I wanted to modify the image anyways I simply forked and <a href="https://github.com/jenkinsci/docker-agent/compare/master...in0rdr:docker-agent:debug/podman_x86_64?diff=unified">built my own agent</a> images, mostly inspired by the <a href="https://github.com/jenkinsci/docker-inbound-agents/blob/master/docker/Dockerfile">example for running Docker</a> inside the agent.</p>

<p>At this stage, my tooling was sophisticated enough I could run a simple <a href="https://gitleaks.io/">gitleaks</a> container on each push to scan for secrets. I&#39;m always afraid to publish secrets accidentally (it happened to me before).</p>

<p>Furthermore, I needed to establish some kind of build process, so I can also build images with Jenkins and directly push them to my local image registry. After some reading, I discarded the thought to use Kaniko because it still requires nodes in the native architecture of the respective build for <a href="https://github.com/GoogleContainerTools/kaniko?tab=readme-ov-file#creating-multi-arch-container-manifests-using-kaniko-and-manifest-tool">multi-architecture builds</a>.</p>

<p>Therefore, I followed RedHat <a href="https://developers.redhat.com/blog/2019/08/14/best-practices-for-running-buildah-in-a-container">best-practices</a> to integrate Buildah, which is also the tool I use to build multi-arch container images locally on my laptop.</p>

<p>If you are interested in more example code, here the link to some of the key components:</p>
<ul><li><a href="https://code.in0rdr.ch/nomad/file/hcl/default/jenkins/templates/jenkins.yaml.tmpl.html">jenkins.yaml.tmpl.html</a> Jenkins infrastructure code</li>
<li><a href="https://code.in0rdr.ch/nomad/file/hcl/default/jenkins/jenkins.nomad.html">jenkins.nomad.html</a> Jenkins nomad job</li>
<li><a href="https://code.in0rdr.ch/nomad/file/docker/docker-jenkins-inbound-agent/Dockerfile.html">docker-agent</a> Modified Jenkins inbound-agent with Docker and Buildah</li></ul>

<p>I&#39;m not finished with playing around, because I still haven&#39;t figured out some things fully yet (e.g., how to properly use <a href="https://community.jenkins.io/t/usage-of-jlink-in-jenkinsci-docker-agent/15456">jlink in multi-arch builds</a>) and because I&#39;m also curious <a href="https://discuss.hashicorp.com/t/jenkins-nomad-plugin/67020">how other members of the community use Jenkins on Nomad</a>.</p>

<div style="text-align:center; font-size: 0.8em">
<a href="https://write.in0rdr.ch/feed">🛜 RSS</a> | <a href="https://m.in0rdr.ch/in0rdr">🐘 Fediverse</a> | <a href="https://chat.in0rdr.ch/#/guest?join=p0c@conference.in0rdr.ch">💬 XMPP</a>
</div>
]]></content:encoded>
      <guid>https://write.in0rdr.ch/jenkins-works</guid>
      <pubDate>Sun, 09 Jun 2024 19:32:05 +0000</pubDate>
    </item>
  </channel>
</rss>