Version controlling Jenkins config
20 July 2022 | 4:18 am

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 $JENKINS_HOME directory).

Jenkins is a wonderful piece of software and I use it with my Bitbucket git repos for CI/CD.

Jenkins uses a web UI for its configuration. I dislike that because it’s difficult to document the configuration process without screenshots, and if I need to create a new server, it’s a manual process of clicking through tabs and filling in the text boxes. I didn’t mind this enough to do anything about it .. that is until I finally got bit by it.

What bit me #

Without going into too much detail, that issue was multi-fold:

  1. I had unknowingly messed up the Project-based Matrix Authorization Strategy such that other users in my team were not able to view the Jenkins jobs.
  2. I had also updated the Jenkins server that introduced a bug (JENKINS-68748) where the Test LDAP Settings failed with an error, but the LDAP authentication actually worked!
  3. 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!

That’s when I wished that my whole Jenkins was version-controlled. That would have allowed me to roll back to the last working “Jenkins image” with the Jenkin version, plugins' versions and my Jenkins config all in sync.

I had delayed doing this because my $JENKINS_HOME was more than 1GB in size and I didn’t have time or motivation to figure out what stuff I should commit and what I should ignore .. But no more — The time had finally come.

.gitignore for Jenkins config #

So I did what any good engineer would do .. start looking for a solution online. I found this StackOverflow answer for Is there a way to keep Hudson / Jenkins configuration files in source control?.

That answer shares a .gitignore that ignores files not necessary for configuring a Jenkins server — Example: job builds, workspace, log files, etc. But it didn’t work out of the box because the plugin version info wasn’t getting committed correctly. I had committed everything to git after using the suggested .gitignore 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:

2022-07-15 13:07:06.082+0000 [id=31] SEVERE jenkins.InitReactorRunner$1#onTaskFailed: Failed Loading global config
com.thoughtworks.xstream.mapper.CannotResolveClassException: hudson.security.ProjectMatrixAuthorizationStrategy
 at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:81)
 at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
 at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:55)
 at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
 at com.thoughtworks.xstream.mapper.PackageAliasingMapper.realClass(PackageAliasingMapper.java:88)
 at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
 at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:79)
 at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
 at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:74)
 at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
 at com.thoughtworks.xstream.mapper.SecurityMapper.realClass(SecurityMapper.java:71)
 at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
 at hudson.util.XStream2$CompatibilityMapper.realClass(XStream2.java:411)
 at hudson.util.xstream.MapperDelegate.realClass(MapperDelegate.java:46)
 at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
 at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:47)
 at hudson.util.RobustReflectionConverter.determineType(RobustReflectionConverter.java:521)
 at hudson.util.RobustReflectionConverter.doUnmarshal(RobustReflectionConverter.java:346)
Caused: jenkins.util.xstream.CriticalXStreamException:
---- Debugging information ----
cause-exception : com.thoughtworks.xstream.mapper.CannotResolveClassException
cause-message : hudson.security.ProjectMatrixAuthorizationStrategy
class : hudson.model.Hudson
required-type : hudson.model.Hudson
converter-type : hudson.util.RobustReflectionConverter
path : /hudson/authorizationStrategy
line number : 12
version : not available
-------------------------------
Code Snippet 1: Snippet of Jenkins crash when attempting to run the server from the freshly cloned git repo

Jenkins Plugin Manager #

So I reached out for help on the Jenkins Community. One of the key contributors to Jenkins, Mark Waite, was tremendously helpful. He suggested using his jenkins-plugin-manager tool. After trying it out for a bit, I realized that this tool had everything I needed for version controlling the plugin versions:

  • Ability to save a list of installed Jenkins plugins and their versions to a file.
  • Ability to batch install all the plugins of the versions listed in a file.

This was like doing Python’s plugin management using requirements.txt, except that this was for Jenkins.

Full solution #

With a combination of the .gitignore that I started with from that SO answer, managing plugins using jenkins-plugin-manager, tweaking the .gitignore 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:

The README on the repo has all the instructions.


Using Git Delta with Magit
7 July 2022 | 2:04 am

Git Delta is a command line utility that beautifies git diffs in the terminal. But did you know that it can do the same in Magit too?

Delta is a highly configurable I am not kidding. Check out the output of delta --help. command line utility that makes the git diffs look better, while also syntax-highlighting Difftastic is another popular diff tool which compares files based on their syntax. I like reviewing git diffs from within Emacs (Magit). But difftastic does not support Magit. the code in the diffs.

When I first heard of “syntax highlighted diffs”, I wasn’t sure what that meant. If you are in the same boat, here’s a screenshot that shows that.

Figure 1: Example of how delta renders a git diff for an ox-hugo commit

Figure 1: Example of how delta renders a git diff for an ox-hugo commit

But I do most of my git operations including viewing of diffs from within Emacs, using Magit.

        .. and thankfully delta works with Magit!

Below screenshot shows how the same diff looks like in Magit.

Figure 2: Example of how magit-delta renders a git diff for an ox-hugo commit

Figure 2: Example of how magit-delta renders a git diff for an ox-hugo commit

The magit-delta Emacs package makes this possible, which is also developed by the delta author Dan Davison.

Caveat
If the line numbers are enabled in delta, they mess up the interactive expanding and collapsing of diffs in Magit. See Magit Delta Issue # 13 for more details.

Now, I am alright with not seeing the line numbers in Magit. But I really liked to see the line numbers in the side-by-side view in the terminal. Luckily, if disabled the line-numbers feature but enabled the side-by-side view, I got what I wanted!

  1. Line numbers are disabled in Magit and expanding/collapsing of diffs works correctly. I am also really glad that I don’t see the side-by-side view in Magit diffs even when I enable that feature in delta, because I like to have my Emacs buffers only about 90 characters wide.
  2. Line numbers and side-by-side view are enabled in the terminal.

I’ll end this post with pointers to installing delta and magit-delta and how to configure them.

Installing delta #

You can install delta (it’s called git-delta in some package managers) using one of the methods listed in its manual, or you can download → extract its statically compiled binary for your OS from its GitHub Releases page.

Installing magit-delta #

Once you put this snippet in your Emacs config and evaluate it, it will install this package and enable the magit-delta-mode in the Magit buffers.

(use-package magit-delta
 :ensure t
 :hook (magit-mode . magit-delta-mode))
Code Snippet 1: Installing and enabling magit-delta using use-package

Configuring delta #

Here’s a snippet for delta configuration from my .gitconfig. It’s mostly the same as the one in delta’s the Getting Started guide. The main difference in my workaround for the magit-delta issue.

[core]
 pager = delta

[interactive]
 diffFilter = delta --color-only
[add.interactive]
 useBuiltin = false # required for git 2.37.0

[diff]
 colorMoved = default

[delta]
 # https://github.com/dandavison/magit-delta/issues/13
 # line-numbers = true # Don't do this.. messes up diffs in magit
 #
 side-by-side = true # Display a side-by-side diff view instead of the traditional view
 # navigate = true # Activate diff navigation: use n to jump forwards and N to jump backwards
 relative-paths = true # Output all file paths relative to the current directory
 file-style = yellow
 hunk-header-style = line-number syntax
Code Snippet 2: My configuration for delta in .gitconfig

Gujarati Transliteration
27 June 2022 | 10:43 pm

You can phonetically write a non-English language on an English keyword in Emacs, and that transforms into that non-English script. This is called transliteration, and I demonstrate that for the Gujarati language in this post.

Emacs provides the transliteration feature using the set-input-method command. I’ll introduce that and few related functions in this post to get to help get started with transliteration quickly.

Enabling transliteration #

Emacs uses the “input method” feature to do character conversion from ASCII to the target language or script. The “input method”, stored in current-input-method, is nil by default. In this state, you see the exact ASCII in Emacs buffer, that you typed on the keyboard1.

In this post, my target non-English language is Gujarati. So I want to type on my English keyboard and have Gujarati script letters show up in the buffer.

Emacs provides the set-input-method command to change the current input method. This command is bound to C-x RET C-\ by default. Pick the new input method after calling that command.

To see the available input methods, do M-x list-input-methods.

As I want to do Gujarati transliteration, I pick the gujarati-itrans method.

If you don’t know Gujarati, don’t fret! The commands shown here will work when transliterating to other languages too — only the Gujarati-specific input method gujarati-itrans will change to the input method of your choice.

Toggling the input method #

I often need to switch between the Gujarati and English languages in the same document. You can see me doing that in this post next section onwards. The toggle-input-method command bound by default to C-\ is helpful here.

So if I am already in the “Gujarati transliteration mode” calling this command will set current-input-method back to nil. Repeating that same call will again set current-input-method to gujarati-itrans, and I will once again be in the “Gujarati transliteration mode”.

Caveats with Gujarati and other Indic language transliteration #

Apologies, but this section is meaningful only if you know how to read Gujarati. So you can safely skip to the next section.

Below table is a quick glimpse of some nuances in Gujarati transliteration. I will save my explanation and instead show some of the mistakes I made in transliteration using examples.

ASCII input Gujarati Transliteration Rough Translation
ram રમ્ (incorrect spelling, no meaning)
rama રમ play
raama રામ a popular name Raama (as in Lord Raama)
angreji અન્ગ્રેજિ (incorrect spelling)
hu.n હું I
chhu.n છું am
a.ngrejii અંગ્રેજી2 English (language)
Ime.cksa ઈમૅક્સ this literally reads “Emacs”

“Input method” cheat sheet #

Thankfully Emacs provides full help through the describe-input-method command bound to C-h C-\ If you haven’t already noticed the consistency in these bindings, the default bindings with C-\ in them are related to “input method” commands. by default.

For example, M-x describe-input-method gujarati-itrans gives this:

Figure 1: Partial screen capture of Gujarati transliteration cheat sheet C-h C- gujarati-itrans

Figure 1: Partial screen capture of Gujarati transliteration cheat sheet C-h C- gujarati-itrans

The ∗Help∗ that shows up looks formidable at the first glance. Though, I found comfort in the fact that roughly half of the key sequences were obvious and roughly half resulted in Gujarati characters that I have never found the need of! 😃

Closing #

Typing this in the “transliteration mode”:

maaru naama kaushala chhe. mane e jaaNii ne aana.nda thaaya chhe ke hu.n aa sahelaaI thI lakhI shaku chhu.n. (joDanI-bhula maapha.)

will result in:

મારુ નામ કૌશલ છે. મને એ જાણી ને આનંદ થાય છે કે હું આ સહેલાઈ થી લખી શકુ છું. (જોડણી-ભુલ માફ.)

Translation: My name is Kaushal. I am happy knowing that I can write this easily.

References #


  1. I am assuming an English keyboard here. ↩︎

  2. This spelling might still render incorrectly on your browser depending on the unicode character set available for Gujarati on your system. ↩︎



More News from this Feed See Full Web Site