<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us"><generator uri="https://gohugo.io/" version="0.101.0">Hugo</generator><title type="html">jenkins on A Scripter's Notes</title><subtitle type="html">Emacs, scripting and anything text oriented.</subtitle><link href="https://scripter.co/tags/jenkins/" rel="alternate" type="text/html" title="HTML"/><link href="https://scripter.co/tags/jenkins/index.xml" rel="alternate" type="application/rss+xml" title="RSS"/><link href="https://scripter.co/tags/jenkins/atom.xml" rel="self" type="application/atom+xml" title="Atom"/><link href="https://scripter.co/tags/jenkins/jf2feed.json" rel="alternate" type="application/jf2feed+json" title="jf2feed"/><updated>2026-04-22T08:24:58-04:00</updated><author><name>Kaushal Modi</name><email>kaushal.modi@gmail.com</email></author><id>https://scripter.co/tags/jenkins/</id><entry><title type="html">Version controlling Jenkins config</title><link href="https://scripter.co/version-controlling-jenkins-config/?utm_source=atom_feed" rel="alternate" type="text/html"/><link href="https://scripter.co/using-git-delta-with-magit/?utm_source=atom_feed" rel="related" type="text/html" title="Using Git Delta with Magit"/><link href="https://scripter.co/view-github-pull-requests-in-magit/?utm_source=atom_feed" rel="related" type="text/html" title="View GitHub Pull Requests in Magit"/><link href="https://scripter.co/creating-a-patch-file-using-magit/?utm_source=atom_feed" rel="related" type="text/html" title="Creating a patch file using Magit"/><link href="https://scripter.co/gujarati-transliteration/?utm_source=atom_feed" rel="related" type="text/html" title="Gujarati Transliteration"/><link href="https://scripter.co/cleaning-up-gopath-pkg/?utm_source=atom_feed" rel="related" type="text/html" title="Cleaning up ${GOPATH}/pkg/"/><id>https://scripter.co/version-controlling-jenkins-config/</id><author><name>Kaushal Modi</name></author><published>2022-07-20T00:18:00-04:00</published><updated>2022-07-20T00:18:00-04:00</updated><content type="html"><![CDATA[<blockquote>Jenkins is an amazing free and open source continuous integration and
deployment software. But its primary means of configuration is a web
UI, and recently that cost me a lot of debug time. That set me down
the path of figuring out a way to version control the Jenkins config
(the <code>$JENKINS_HOME</code> directory).</blockquote><div class="ox-hugo-toc toc">
<div class="heading">Table of Contents</div>
<ul>
<li><a href="#what-bit-me">What bit me</a></li>
<li><a href="#dot-gitignore-for-jenkins-config"><code>.gitignore</code> for Jenkins config</a></li>
<li><a href="#jenkins-plugin-manager">Jenkins Plugin Manager</a></li>
<li><a href="#full-solution">Full solution</a></li>
</ul>
</div>
<!--endtoc-->
<p><a href="https://www.jenkins.io/">Jenkins</a> is a wonderful piece of software and I use it with my
Bitbucket git repos for <a href="https://en.wikipedia.org/wiki/CI/CD">CI/CD</a>.</p>
<p>Jenkins uses a web UI for its configuration. I dislike that because
it&rsquo;s difficult to document the configuration process without
screenshots, and if I need to create a new server, it&rsquo;s a manual
process of clicking through tabs and filling in the text boxes. I
didn&rsquo;t mind this enough to do anything about it .. that is until I
finally got bit by it.</p>

<h2 id="what-bit-me">What bit me&nbsp;<a class="headline-hash no-text-decoration" href="#what-bit-me">#</a></h2>


<p>Without going into too much detail, that issue was multi-fold:</p>
<ol>
<li>I had unknowingly messed up the <em>Project-based Matrix Authorization
Strategy</em> such that other users in my team were not able to view
the Jenkins jobs.</li>
<li>I had also updated the Jenkins server that introduced a bug
(<a href="https://issues.jenkins.io/browse/JENKINS-68748">JENKINS-68748</a>) where the <em>Test LDAP Settings</em> failed with an
error, but the LDAP authentication actually worked!</li>
<li>I had also updated all the plugins after updating Jenkins. So if I
rolled back the Jenkins versions, most of the plugins would fail
because of incompatibility with the older Jenkins version. I had
updated Jenkins after months!</li>
</ol>
<p>That&rsquo;s when I wished that my whole Jenkins was
version-controlled. That would have allowed me to roll back to the
last working &ldquo;Jenkins image&rdquo; with the Jenkin version, plugins'
versions and my Jenkins config all in sync.</p>
<p>I had delayed doing this because my <code>$JENKINS_HOME</code> was more than 1GB
in size and I didn&rsquo;t have time or motivation to figure out what stuff
I should commit and what I should ignore .. But no more &mdash; The time
had finally come.</p>

<h2 id="dot-gitignore-for-jenkins-config"><code>.gitignore</code> for Jenkins config&nbsp;<a class="headline-hash no-text-decoration" href="#dot-gitignore-for-jenkins-config">#</a></h2>


<p>So I did what any good engineer would do .. start looking for a
solution online. I found <a href="https://stackoverflow.com/a/4695615">this StackOverflow answer</a> for <em>Is there a way
to keep Hudson / Jenkins configuration files in source control?</em>.</p>
<p>That answer shares a <code>.gitignore</code> that ignores files not necessary for
configuring a Jenkins server &mdash; Example: job builds, workspace, log
files, etc. But it didn&rsquo;t work out of the box because the plugin
version info wasn&rsquo;t getting committed correctly. I had committed
everything to git after using the suggested <code>.gitignore</code> and pushed to
my git remote. But if I cloned that repo to a different area and
attempted to start the Jenkins server from there, it crashed with this
message:</p>
<p><a id="code-snippet--jenkins-crash-log"></a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">2022-07-15 13:07:06.082+0000 [id=31]    SEVERE  jenkins.InitReactorRunner$1#onTaskFailed: Failed Loading global config
</span></span><span class="line"><span class="cl">com.thoughtworks.xstream.mapper.CannotResolveClassException: hudson.security.ProjectMatrixAuthorizationStrategy
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:81)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:55)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.PackageAliasingMapper.realClass(PackageAliasingMapper.java:88)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:79)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:74)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.SecurityMapper.realClass(SecurityMapper.java:71)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
</span></span><span class="line"><span class="cl">        at hudson.util.XStream2$CompatibilityMapper.realClass(XStream2.java:411)
</span></span><span class="line"><span class="cl">        at hudson.util.xstream.MapperDelegate.realClass(MapperDelegate.java:46)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
</span></span><span class="line"><span class="cl">        at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:47)
</span></span><span class="line"><span class="cl">        at hudson.util.RobustReflectionConverter.determineType(RobustReflectionConverter.java:521)
</span></span><span class="line"><span class="cl">        at hudson.util.RobustReflectionConverter.doUnmarshal(RobustReflectionConverter.java:346)
</span></span><span class="line"><span class="cl">Caused: jenkins.util.xstream.CriticalXStreamException:
</span></span><span class="line"><span class="cl">---- Debugging information ----
</span></span><span class="line"><span class="cl">cause-exception     : com.thoughtworks.xstream.mapper.CannotResolveClassException
</span></span><span class="line"><span class="cl">cause-message       : hudson.security.ProjectMatrixAuthorizationStrategy
</span></span><span class="line"><span class="cl">class               : hudson.model.Hudson
</span></span><span class="line"><span class="cl">required-type       : hudson.model.Hudson
</span></span><span class="line"><span class="cl">converter-type      : hudson.util.RobustReflectionConverter
</span></span><span class="line"><span class="cl">path                : /hudson/authorizationStrategy
</span></span><span class="line"><span class="cl">line number         : 12
</span></span><span class="line"><span class="cl">version             : not available
</span></span><span class="line"><span class="cl">-------------------------------
</span></span></code></pre></div><div class="src-block-caption">
  <span class="src-block-number"><a href="#code-snippet--jenkins-crash-log">Code Snippet 1</a>:</span>
  Snippet of Jenkins crash when attempting to run the server from the freshly cloned git repo
</div>

<h2 id="jenkins-plugin-manager">Jenkins Plugin Manager&nbsp;<a class="headline-hash no-text-decoration" href="#jenkins-plugin-manager">#</a></h2>


<p>So I <a href="https://community.jenkins.io/t/version-controlling-jenkins-config-help-defining-a-gitignore-that-minimizes-the-git-repo-size/3036">reached out for help</a> on the Jenkins Community. One of the key
contributors to Jenkins, <a href="https://community.jenkins.io/u/MarkEWaite">Mark Waite</a>, was tremendously helpful. He
suggested using his <a href="https://github.com/jenkinsci/plugin-installation-manager-tool"><code>jenkins-plugin-manager</code></a> tool. After trying it out
for a bit, I realized that this tool had everything I needed for
version controlling the plugin versions:</p>
<ul>
<li>Ability to save a list of installed Jenkins plugins and their
versions to a file.</li>
<li>Ability to batch install all the plugins of the versions listed in a
file.</li>
</ul>
<p>This was like doing Python&rsquo;s <a href="/saving-python-pip-dependencies/">plugin management using
<code>requirements.txt</code></a>, except that this was for Jenkins.</p>

<h2 id="full-solution">Full solution&nbsp;<a class="headline-hash no-text-decoration" href="#full-solution">#</a></h2>


<p>With a combination of the <code>.gitignore</code> that I started with from that
SO answer, managing plugins using <code>jenkins-plugin-manager</code>, tweaking
the <code>.gitignore</code> to my liking, and adding helper Bash scripts for
downloading and running Jenkins server binaries, and doing the plugin
management, I finally got what I needed:</p>
<div class="org-center">
<p><a href="https://github.com/kaushalmodi/jenkins-minimal">https://github.com/kaushalmodi/jenkins-minimal</a></p>
</div>
<p>The README on the repo has all the instructions.</p>
]]></content><category scheme="https://scripter.co/categories/unix" term="unix" label="unix"/><category scheme="https://scripter.co/tags/git" term="git" label="git"/><category scheme="https://scripter.co/tags/jenkins" term="jenkins" label="jenkins"/><category scheme="https://scripter.co/tags/100daystooffload" term="100daystooffload" label="100DaysToOffload"/></entry></feed>