This action requires saving a cookie to remember the page's state. You can read more about that on the cookies page.


cvMangle - When automation meets your CV

Released on: 2023-11-18

cvMangle isn’t as technically impressive as some of my other projects, like handWavey. But it has made such a huge difference that I’m just as excited about it.

This is actually my second time writing this post. In the first version, I wove a long, beautiful tale about how my CV got to 11 densely packed pages, and why I stuck with it for so long. But the TL;DR was that it had actually become a liability, and was pretty hypocritical given my stance on verbosity in documentation. Yet every time I tried to re-write it, I came back to basically the same thing.

I needed to cut ties with everything that already existed, and the ability to quickly experiment with different variants without destroying any data. This way I could quickly see if any given idea would work without the mental inertia that comes with 11 densely packed pages.

Table of contents

The requirements

I need to be able to

  • experiment.
  • have multiple variants at once.
    • Editing original data in one place, should affect all places.
    • The solution should cope with exceptions without becoming cumbersome.
    • It should keep a history of changes that I can browse back through easily.
    • Drastically different layouts shouldn’t compromise existing layouts. Yet data duplication should be minimised.
  • use the same data in very different ways.
  • still read old versions of the CV in 20 years from now.
  • back it up confidently.
  • easily move to a completely different solution if the current one is no longer the best one.
  • not have to keep paying a service that I’m not using most of the time.
  • make changes confidently, and trust that those changes will be applied sensibly. (Eg one job entry should not be split over two pages.)
  • have the filenames and footers generated for me in a sensible way, and match each other.
    • Specifically I want to instantly know which version the person at the other end of a zoom call is looking at. (This becomes relevant when the CV has been revised to address something that they wanted to see and they therefore have multiple CVs.)
    • I have been doing this manually, and it’s very prone to human error.
  • be independent of 3rd party services.
    • It’s becoming more and more common that companies try new products quickly, and then exit the market when there’s no competition left. Additionally companies simply go out of business from time to time.
    • 3rd parties have a habit of changing things from one day to the next, so you can’t rely on something working at the moment when you actually need it.

On top of explicitly mentioning 3rd party services in the last bullet point, they fail pretty much all of the bullet points. So let’s move on…

A couple of other possible solutions

There are at least a couple of command line CV tools that ticked some of my requirements. They weren’t quite what I was after, but are really cool, and might be for you. The ones I’ve found are:

Note that they are actually different projects.

The solution

Markdown + git + a little special sauce:


It takes standard Markdown, and adds a couple of commands to help with organisation and data re-use, track changes in git, and then use cvMangle to build the different variants concurrently.

The output files are versioned:

$ ls -1 *pdf

And old versions are moved to the old/ folder:

$ ls -1 old/ | wc -l

… I’ve done a bit of experimentation to see what I like ;)

Other stuff that cvMangle makes easy

Custom CVs

Sometimes you want to do a one-off customisation for one specific job that you want to apply for. You can do that with custom CVs. A custom CV is exactly the same as a variant, except that it only gets built when you specifically ask for it.

Meanwhile a variant will get built every time you do a normal build.

Stuff that’s hard in markdown

There are some things that, to the best of my knowledge, are not easy to achieve in markdown to PDF conversion:

I’ve automated both into easily include-able files. Specific-width columns via hk-pandoc-filters, and blocks via a hack using columns.

The result

I keep thinking of things that I want to try. I still have a long way to go to achieve what I’m after. But have made a lot of process so far. Here’s a quick visualisation to give you an idea:

Progress so far.
Above: Progress so far.

Here I’ve gone from a wad of text where you have to dig to find anything, to a few bullet points that say way more than the original text, in a whole lot less space.

A screenshot showing several aspects discussed below.
Above: A screenshot showing several aspects discussed below.

There are several things to observe in this screenshot:

  • Uneven columns.
  • Different font sizes in each column, optimised to how they are used.
  • Default footer includes:
    • The user’s name set by setName as described in the overview for using it.
    • Automatic page numbering.
    • Variant, and build version in the footer.

More details for those who are curious

How to use it

There is documentation specifically for using cvMangle. But the gist of it is that you create variants of your CV, which you then build using the cv command, which creates PDF files for you.

Variants are markdown files that can contain any markdown syntax, but can also include cvMangle commands like include, and forEach.

  • include is the building block for abstracting out things like an introduction, your education, or key skills.
    • Includes can be nested. There will be a technical limit to how many levels deep you can nest things. But for legitimate uses, I can’t imagine that we’ll ever get anywhere near that limit.
  • forEach is for iterating over a directory of things.
    • I use this for iterating over jobs.
      • I split recent, and older jobs into two separate directories so that I can create short CVs that only include the recent jobs, and long CVs that include all of my experience.
      • When a job needs to move from recent jobs to old jobs, simply move it and re-generate the CVs.

File naming


  • 2023-11-17: I generated this CV on the 17th of November, 2023.
  • .0: This is the first version I generated that day.
  • cv: This is a CV, as opposed to a something like a cover letter, which I name in the same format.
  • kevinSandom: That’s me! If a recruiter has a folder full of CVs, this will hopefully help them identify mine/yours more quickly.
  • 3page: This is the variant named “3page”. My other current variants (listed above) are 2page, fullDynamic, and shortDynamic.


  • Markdown is used for all data/content.
  • Git is optionally used for managing changes over time.
  • Pandoc is used for converting the markdown into the PDFs.
  • Bash is the basic glue joining everything together.

Why these tools

  • Markdown being an easily readable text format:
    • works incredibly well with git. So history is very efficient, and easy to manage.
    • is light weight.
    • is easy to read and get the data out of if any tooling breaks in the future.
  • Git is well supported, and well known. It:
    • is likely to be around for a long time.
    • has lots of tooling available.
    • can be run locally and/or on a third party service.
      • We get the advantage of being immune to 3rd party changes.
      • And the security of having it locally.
  • Pandoc is heavily used and available at the moment.
    • If this changes, the compiled markdown is still usable for other converters.
  • Bash is excellent at this type of task. But there is not much code there, so it’s easy to migrate to something else later on if that is deemed to be the better option.

Git usage

cvMangle does not create, or manage, a git repo for you. But it does have some tooling to help you track automated state changes such as turning every occurrence of a keyword into a link.

Basically, it’s up to you if you want to use it. For me, there is some piece of mind in being able to roll back changes if I do something stupid. If you don’t like git, but like the idea, you could use a completely different code versioning tool.



There are linux-specific assumptions in place at the moment. It is highly likely to work on MacOS, and WSL, but will not work on Windows natively.

For now…

If you’re interested in Windows support. Check the Work on Windows? issue to register your interest and see where things are at.


I think that I’ve got everything up-to-date. But if there’s something that isn’t clear, and you haven’t been able to figure it out and create a pull-reqeust, please create an issue so that we can figure it out together.

This post references

I took some time to extend myself with some intellectual challenges. Here's what I've done so far.
I've done a few software projects over the years. Here's a collection of some of them.

Posts using the same tags

It was time for a much needed update to the resolution app. Here's what I've improved.
Getting the information and access you need to your infrastructure quickly, so that you can get back to sleep.
It's time to blow the dust off machine learning, and apply it to a dataset that I know.
I've just released the biggest, most exciting, update since the first public release of handWavey. The learning curve is dramatically reduced!
The story of where Achel came from, and where it's going.
My CV had gained so much weight that it was hard to do anything with it any more, and it was hard to read. So I did something about it...
Control your computer using a Leap Motion controller, but with an every-day-quality implementation.
If you want to share stable diffusion on your network without exposing any information to the outside world. This is one way to do it.
There's a trick for getting a big speed boost on old hardware that's so easy that I'm surprised I haven't heard people talking about it.
What began as 3 tripods on a hill, and hours per photo, ended with way better results in seconds, hand held.
I took some time to extend myself with some intellectual challenges. Here's what I've done so far.
I accidentally automated Javelining a plane into the ground. And I learnt a huge amount along the way.
How much of your phone's screen resolution can you actually see? This app helps you quantify it.
Over the last few years, there has been a lot of talk about whether you can make use of the full resolution on a phone with a 4K display. Let's dig in and actually understand this.
Group of posts about the Astro Slide 5G.
4 easy phone hacks to make your phone more useful and fun
My dream office.
An easy way to get a dark theme for kmail content, that reliably works on pretty much everything.
calibrate multiple touch and non touch screens on a single linux system." This is to address the issue on multi-display Linux desktops where the touch panel is automatically calibrated to operate over all of the displays, and even if you get the calibration right, it's then wrong again when you ...
DoneIt is a time tracker for working out where your time is going. For me there were a few things I wanted to achieve with it - Be able to say what I've been doing all day. - See how much time is lost to time ...
Well over a year ago I introduced mass which is a tool for managing large numbers of servers. It's come along way in that time, and it's well and truly time for another look. From the users' point of view, the most interesting things are probably that you can now ...
Achel is a programming language that I have been working on for the past 13 years (I said 12 in the video, it has been a long time!) There has been growing interest in the programs I have been writing using it, so I have now released it open source. ...
I've done a few software projects over the years. Here's a collection of some of them.
Home | About | Contact | Cookies | Site map