Case study · Live
CV as code
My CV lives in one YAML file. A build renders the web version and an ATS-friendly PDF, and GitHub Actions deploys both to Cloudflare Pages. This site reads the same file.
- Role
- Personal
- Status
- Live
- Links
- cv.nguyendinhkha.com
Problem
A CV kept as a Word file, a PDF, a web page and a portfolio drifts: one gets updated and the others don't. Exporting a clean, ATS-friendly PDF by hand after every change is slow and easy to get wrong.
Approach
- 01
One source of truth. The whole CV lives in cv.yaml in the cv repo. Nothing else is edited by hand.
- 02
TypeScript renders it to HTML with print CSS; Playwright prints the PDF, with real selectable text for ATS parsers, page-break rules that keep each project header with its content, and a clickable footer link.
- 03
The build also writes cv.json, which this portfolio reads at build time, so experience and skills here always match the CV.
- 04
GitHub Actions on Node 24 and pnpm type-checks, builds, checks the outputs and deploys to Cloudflare Pages with wrangler. Security headers and the /cv.pdf short link are generated at build.
- 05
After a deploy, the workflow sends a cv-updated event to the portfolio repo so nguyendinhkha.com rebuilds too; a daily scheduled build is the fallback.
Result
One commit to cv.yaml updates cv.nguyendinhkha.com, the PDF and nguyendinhkha.com in a single pipeline run, with no manual export.
- TypeScript
- Playwright
- GitHub Actions
- Cloudflare Pages