Published 2026-09-01
tag(s): #programming #overblown-minor-annoyances #emacs
This is yet another gripping tale of "do everything in Emacs". In this case running Jenkins
jobs.
It is also a good overview of my current approach to building Emacs packages. Which is by
half-assing them.
We run deployments at work with an in-house CLI tool.
Why not use Jenkins/Octopus/Bamboo/Whatever directly to move code around? I have no idea.
Actually, a better way to put it is: I think we could do it, maybe even should. But I
am not idealistic enough to have volunteered to, you know, actually make it happen.
I accepted the CLI as part of our workflow: it is mature, stable, and at this point I can
modify to support whatever we require.
Just accept that part. Like I do. :)
Also, as I said, it is a CLI. I always have a shell buffer open in the server. Time to run a
deploy? Easy peasy.
Except that the powers that be decided we need to click around a web page instead of
an easy to use CLI. Why, oh why, misguided management...
... well, actually, there is a couple very good reasons. By running the tool via Jenkins we
get traceability, limit the users that can run the tool, and get log output archived
automatically. Hard to argue with that many benefits. :)
But! I still wanted to run deployments without leaving Emacs. What to do?
If you peek around the emacs-utils
repository, you will find a bunch of half-baked packages. Most of them consume a CLI tool,
which tends to be simpler than calling an API yourself.[1]
And they are half-baked for a reason. My days of trying to build an all-encompassing tool are
behind me. Sharper, the dotnet
wrapper, was probably the last attempt.
Nowadays, I am fluent enough in elisp that I can put together a small set of commands to accomplish what I need now, and then I can add features as time goes by. Focus only on the low hanging fruit each time. I call these "proto-packages".
As an example, I wrote commands to upload/download from Azure blob storage asynchronously
using AZ CLI in like ten minutes, the day I needed them. A couple weeks later, since I was
still using blob storage regularly, I turned that starting point into a basic
"directory" navigation.
How basic? Enough for I needed regularly, and no more: list files, move up/down the tree,
upload something at current level.
Putting together the last two paragraphs - is there a CLI that I can use to run deployments in
Jenkins directly, without opening the browser first?
Yes, there is a Jenkins CLI.
What if I write a small interaction library for said tool, so I can then add a command to run
deploys from Emacs?
Sure thing, here
is butler.
As it stands now, it offers a grand total of TWO commands, one to read the console output of
the last job execution, and another to launch one. The package innards are modified versions
of the ones I used in the GH CLI and AZ CLI proto-packages.
Can I use butler-run-job to start a deployment directly from Emacs?
Indeed:
(require 'butler)
(setopt butler-url "https://employer.jenkins.url:9999"
;; it's important to use `expand-file-name', I run this in Windows and Linux :)
butler-jar-path (expand-file-name "~/.local/bin/jenkins-cli.jar"))
(defun hoagie-colo-deploy ()
"Interactively run a colo deploy.
Prompts for a command and environment."
(interactive)
(let ((commands '(deployproject1 deployproject2 ... deployprojectN))
(environments '(dev uat prod etc etc1 etc2))
(tickets (read-string "Tickets (csv): ")))
(butler-run-job "Job Name For Internal Deployments"
(list (concat "COMMAND="
(completing-read "Command: " commands nil t))
(concat "ENVIRONMENT="
(completing-read "Environment: " environments nil t))
(concat "TICKET="
tickets)))))
Mission accomplished!
Nothing.
Well, nothing planned.
After the first version of Butler, I added a function to update a job definition. We changed the name of an agent, and since a lot of jobs were impacted, I scripted a mass update using butler. Yak shaving, baby.
But I don't expect to publicize it[2]. Or to try to cover 100% of the
features offered by the Jenkins CLI, unless I happen to need exactly that.
Like with everything else in that repo, people are free to copy pieces, fork, submit patches,
whatever.
For reasons even I don't understand, I don't feel compelled to write The One Solution
For az|gh|jenkins-cli|etc. That Everyone Can Rely On.
I used to, which is why I submitted things to MELPA in the past. But these days? I don't see
the point.
Does that make me a "bad community member"? I don't know. Maybe. 🤷