My Resume Generator
Why I built it
For a long time I’ve been a reasonably satisfied user of the Europass platform for generating CVs, which has evolved over time. Although the common (especially HR-side) thinking is “EUROPASS SUCKS!”, I found it to be a fairly intelligent solution: your data is always synchronized, there’s a precise schema, the final PDF is pretty good, and there are different styles to choose from. I’d like to have a conversation to understand the real reasons for the hatred toward Europass CVs, but I think it’s dogmatic and I don’t like dogmas much.
Anyway, lately using the Europass site has become more problematic (it requires a “national login”, eID, wtf?, multilingual handling is clumsy, PDFs have gotten worse, etc.), so I decided to rework my CV in a smarter, more personal way. I researched and realized it was possible to do something more satisfying for a tinkerer like me. Also, I wanted to do it end-to-end with Claude Code, without touching a line of code, building a real solution.
The objective was simple:
- Versioned data - I want to track changes to the data like code, so JSON files
- Reproducible builds - the same input always yields the same output
- Multilanguage - I need both English and Italian
- Professional look - LaTeX quality, not Word/auto-generated PDF quality
- Automation - I want to easily generate new CVs, customize them for different purposes, and have them always available
Working with Claude Code
This project was a great way to test Claude Code capabilities.
But I started by writing something to Claude to get a better idea of some details. The first prompt was, simplified, something like:
Hey, i’d like to build up a github repository that can automatically create different versions of my resume. It should be based on CV data (say, JSON format), a Latex template and a github action that can be able to create a PDF artifacts in different languages for each branch. Can you help me with that? Do you need further details?
Claude replied with solutions and some follow-up questions to clarify a few doubts. After a few iterations I resolved its doubts and asked it to generate a useful specification for developing using Claude Code.
At that point I moved to VS Code and asked Claude Code to implement according to the spec. After just a few trial-and-error loops I had a working solution with:
- My multilingual JSON containing data taken from my existing PDF CV
- A LaTeX template inspired by Awesome CV
- A Python script to take the CV data and create a PDF using the LaTeX template
- A Dockerfile that installs on the base image all the tools needed to generate the PDF
- A GitHub Action to automatically generate the PDF on tags (with release) and on branches (with artifact)
Each step produced tangible results. Claude Code produced changes/suggestions, I reviewed them, and it automatically ran commands to test everything (CI/CD pipeline excluded).
The tech stack
Docker for reproducibility
LaTeX installations are complicated. Different machines have different TeX distributions, missing packages, font issues. It’s hard to get consistent output.
With Docker we try to solve this by making builds practically deterministic:
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
texlive-latex-base \
texlive-latex-extra \
texlive-xetex \
texlive-fonts-recommended \
texlive-fonts-extra \
python3 \
python3-yaml \
fonts-font-awesome \
wget
Note: I had to optimize performance by removing a bunch of very large packages (~5GB) that basically weren’t needed. Claude tends to be “generous” and overcomplicate things if you don’t specify otherwise. I still asked it to optimize: “can you make the builds less slow?” It suggested removing useless packages and using caching in the pipeline. Great!
Python for processing
The generator script is simple Python - no framework, just the standard library plus PyYAML. It does a few things:
- Reads the JSONResume format
- Formats dates
- Escapes LaTeX special characters
- Handles languages
- Runs the PDF generation command
Honestly Python isn’t my main language, but even if the code isn’t idiomatic it does what it needs to do, it’s very readable and it works. Obviously it’s code written to do what I need, not to handle other translations or edge cases I don’t have and probably will never have. Don’t solve problems you don’t have ;)
LaTeX for high-quality output
I chose the LaTeX template Awesome-CV. It has a clear, modern and professional look. I only made a few tweaks.
The template has placeholders that Python replaces:
\name{{{NAME}}}{}
\position{{{TITLE}}}
\email{{{EMAIL}}}
\linkedin{{{LINKEDIN}}}
GitHub Actions makes it automatic
Every time I push code, GitHub Actions:
- Builds the Docker image (with caching)
- Generates PDFs for all languages
- Uploads them as artifacts (kept for 90 days)
When I create a tag, it also creates a release with the PDFs attached. This is useful to manage different job applications - I can tag versions like v2024-company-X and keep track of what I sent.
The caching configuration makes builds fast:
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
tags: my-resume-generator:latest
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
I reuse Docker layers between runs. First build: ~2 minutes. Afterwards: 30-60 seconds. TeX packages remain cached.
Moving to JSONResume
At first, I simply created my own JSON structure. Something like:
{
"personal": { "name": "...", "email": "..." },
"experience": [...],
"skills": [...]
}
Ok, it worked. The script read the JSON, generated LaTeX, compiled the PDF.
But then I thought: “Hasn’t anyone invented a standard for this?” and I discovered JSONResume. It’s an open standard that already exists, with:
- A suitable schema that covers everything
- Validation tools
- Other themes and tools that can read it
- ISO 8601 dates (correct date format)
- A community that maintains it
So, in a few minutes, with Claude Code converting everything to the JSONResume format. The script needed updates to read the new structure (basics instead of personal, nested location object, etc.), but it’s all working fine.
If tomorrow I decide to use a different tool or theme, I can. The data isn’t locked to my generator, and there are other generators and templates I can use without problems.
The result
After a few hours of work I have:
- Version-controlled resume data in a standard format
- Professionally-looking PDFs that are consistent everywhere
- Automatic builds on every change
- Multilanguage support (English and Italian)
- Tagged releases for applications
- Fast builds thanks to Docker caching
All in a repository. To update my resume: I edit the JSON, push, optionally tag, wait for the pipeline, download the PDF. Done.
Final thoughts
In half a day I got something working and useful for my needs. I learned something new, had fun building it, and now I can generate resumes without feeling the need to cry. But then… how fun was it to tinker without worries!? ;)
The full code is here: my-resume-generator