Skip to main content
P821 BLOGFAX
POST

My Automatic Resume Engine

I want to share my workflow for maintaining a resume in a repository hosted on GitHub. I will cover my motivation for doing it this way, my process, the important design decisions I have made up to this point, and finally some improvements that could be interesting projects to work on.

The Motivation

When applying for jobs, it's common to receive advice along the lines of "tailor your resume to the job posting." While good advice, it can be quite a chore maintaining multiple versions, tracking edits, and keeping various details in sync. Similarly, a resume that you might use for an online application might not be suitable for a real, live human.

For the longest time, I maintained my resume using Microsoft Word. It worked. I was able to edit the document and print out or save it as a PDF. As time went on, I began to find that my resume (more importantly, the latest copy) always seemed to be on the desktop or laptop I wasn't sitting at. Not a huge deal, I would make a note and come back to it later.

As the Internet and portable devices became more ubiquitous, I migrated from Microsoft Word to Google Docs. Now as long as I didn't find myself in the wilderness, up a mountain, or in a moon base, I could update my resume from any mobile device, desktop, or refrigerator with an Internet connection. Life was good, or so I thought...

Remember that nonsense about tracking edits, and multiple versions? Don't worry, I won't spend too many more pixels on it. The major problem with a resume in Google Docs is version control. That's not to say it doesn't exist in Google Docs, because it most certainly does. The problem rears its ugly head when you have multiple versions and want to track changes to a set of documents at once.

In Google Docs, each document has its own history. Not so handy when you want to quickly see how many different versions of your resume have the wrong verb tense because some copy pasta went bad.

To bring a long story to an end, my motivation came down to growing tired of the inflexibility of a word processor, the need to present my resume in different forms, and I was looking for the opportunity to skill up with some new tech 😁.

The Requirements

Having been sufficiently motivated, I came up with a list of requirements. They are:

The Process

I spent quite some time on designing the system I have put in place. I knew for sure that I would use Git for version control. It's a tool that I (like many) am familiar with. It is usable on really any platform as well, so it seems like a good fit. Git is rather flexible too. Having been brought into this world to support the Linux project, Git has a great track record for supporting numerous use cases.

With version control out of the way, I moved onto tackling the separating the content from the document structure/layout. For a while I thought that LaTeX would be the ideal solution, while I hoped it would be GitHub flavored Markdown. LaTeX has several obstacles, however. It isn't super easy to set up, requires substantial compute resources (namely disk space), and you have to mix your data with the layout. Thankfully, .tex files are plain text documents so a working LaTeX installation wouldn't be a requirement to edit, however. Markdown was another option I originally considered. It put up similar obstacles. While there is no real set up or (direct) installation required to use Markdown, you still have to mix data and layout. Additionally, Markdown is not really suited to represent complex data structures. There isn't a straightforward way to represent a skill or work detail in a way other than using headers or tables to group data together. At this point, I knew I would need some tool that would let me specify or otherwise "mark up" objects. This left me with 3 options, in my mind. JSON, TOML, YAML. I explored JSON (JSON Resume, specifically) quite extensively. JSON Resume was nice in the fact that JSON supports the specification of a schema, but it falls short when trying to include comments in the JSON itself. I eventually settled on TOML. So far, TOML has worked pretty well. The TOML specification is clear and easy to understand, meaning that getting comfortable with the syntax was a 🍰.

Now that the data is separated from the layout, I turned my attention to protecting the data. The choice here was obvious to me. I would use Ansible vault. If you are unfamiliar with the tool, it takes a file and a password as input, and outputs a file with an AES encrypted blob as its content. This allows me to commit encrypted versions of my resume data to my repository and decrypt them as needed to make edits or generate resumes based on the decrypted data. No spam for me, hehe 😁! Encrypting these files in this way does have the drawback of only being able to edit the files in places where Ansible runs (in order to decrypt the files), sadly, but for platforms where Ansible isn't available directly, SSH is generally an option.

The last requirement is automation. This was another easy choice. I am committing everything in an encrypted form, to a Git repository, so I went with GitHub Actions. GitHub Actions let me automatically run a series of tasks when I change any of my TOML files. I also have a custom action that sends me a copy of the generated resumes via email, so I don't have to host the PDFs, in decrypted form, in the repository itself.

With the basics in place, I can begin walking through the editing process.

Step 1: Edit resume

This part is straight forward. A single command is all you need: ansible-vault edit ./resume.vault.toml --ask-vault-pass. Upon executing the command, you are prompted for the decryption password (I've saved a nice long password in my password manager), the file is decrypted, and then opened in the default editor. After making my desired edits (ex: Adding Ansible to my list of skills), I save the file and close the editor. After the editor closes, Ansible re-encrypts the file's contents automatically. The only thing left to do is commit the edits, and push them to GitHub.

Step 2: Wait... ⏱️

I know the heading of this section makes it sound like this is a HUGE time sink. It isn't. This is the automation step. I have configured a GitHub Actions workflow to be triggered by any push to my repository that contains changes to a file with the .toml extension. Once triggered, the following steps are taken:

  1. The repository is cloned to the GitHub Action runner.
  2. Using an Ansible vault password stored in a GitHub repository secret, each resume is decrypted and converted from TOML to JSON using yj.
  3. The JSON files are then converted to PDFs using the JSON Resume project's resume-cli tool.
  4. The resulting PDFs are shipped off (to me) using my QuickSend File Transfer action, via email.
  5. Finally, the JSON files are encrypted with Ansible vault and committed to the repository.

You likely noticed that I mentioned that I convert the TOML to JSON in the workflow outlined above. I skipped over this before, but the purpose for this is simple. TOML is easier to both read and edit compared to JSON, but there is a considerable amount of work and tooling done to make JSON Resume relevent. In this case, I am able to use the best of both worlds by writing my TOML files in a way that converts to a valid JSON Resume, and then use tooling for that community to style and generate my resume. Lastly, I commit the JSON representation of my resume, so I can trigger the GitHub Action workflow manually when I need a new copy of my resume. This saves me from trying to hunt down the files from a previous generation.

Step 3: Get Hired!

Okay, this really isn't a step, but only two steps felt too easy!

Future Plans

Throughout this process, I focussed on getting a complete solution in order to fight the dreaded feature creep. There are definitely some features that would be nice to have:

These might be interesting features to work on in the future, as well as, provide good opportunities to skill up!

Thanks for reading! If you have any thoughts or questions about this post, feel free to start a discussion.