Open-source CV builder. YAML in, PDF out.
→ Template showcase: cvmake.codevena.dev · Live editor: cveditor.codevena.dev
One YAML file. Four templates. Switch by changing one line.
![]() academic |
![]() bauhaus |
![]() classic-serif |
![]() corporate |
![]() creative-accent |
![]() editorial |
![]() magazine |
![]() modern-minimal |
![]() monochrome-dark |
![]() noir |
![]() swiss |
![]() tech-dev |
You have a CV. You want to keep it in version control, render it to multiple templates, maintain it in two languages, and not pay $9/month forever for the privilege. cvmake does that.
- One YAML file is the truth — diff in git, search with grep, copy between machines. No Word document mystery formatting.
- 12 polished templates — academic, bauhaus, classic-serif, corporate, creative-accent, editorial, magazine, modern-minimal, monochrome-dark, noir, swiss, tech-dev — each with multiple color palettes. Same content, instant restyle.
- Multilingual by default — author
cv.de.yaml+cv.en.yamlside-by-side from one schema, switch via CLI flag. - High-fidelity PDF output — the browser preview and the PDF run the same React components, the same template CSS and the same vendored fonts, so what you arrange is what you get. They are not byte-identical: the preview is a live DOM, the PDF is a Puppeteer print render, and only the second one paginates.
- CLI or browser —
npx @codevena/cvmake-cli build cv.yamlfor terminal users, the live editor for everyone else. - MIT, no lockin — your data stays on your machine. Fork the schema, fork a template, add your own. The project is small enough to actually read.
# 1. Scaffold a commented starter CV (cv.yaml)
npx @codevena/cvmake-cli init cv.yaml
# 2. Edit cv.yaml with your data, then render it
npx @codevena/cvmake-cli build cv.yamlNo clone required. init drops a ready-to-edit, schema-valid cv.yaml
(use --lang de for German). The first build downloads Chromium
(~150 MB, one-time) which is needed for high-fidelity PDF rendering.
Convert an existing jsonresume.org file and get all 12 templates instantly:
npx @codevena/cvmake-cli import resume.json cv.yaml
npx @codevena/cvmake-cli build cv.yamlgit clone https://github.com/Codevena/cvmake
cd cvmake
pnpm install
pnpm build # builds the workspace packages once
# Copy the example to your local-only CV (cv.*.yaml is gitignored)
cp data/cvs/example.de.yaml data/cvs/cv.de.yaml
# Render a PDF
pnpm cvmake build data/cvs/cv.de.yamlOutput PDF lands in out/cv.pdf by default.
| ID | Style |
|---|---|
academic |
Serif, two-column publication-style layout |
bauhaus |
Geometric shapes, primary palette, Futura |
classic-serif |
Traditional resume with serif typography |
corporate |
Restrained corporate single-column |
creative-accent |
Colored accent block, modern sans-serif |
editorial |
Magazine-style with strong typography |
magazine |
Display serif, italic, two-column body — Vogue-style |
modern-minimal |
Minimal, lots of whitespace |
monochrome-dark |
Dark theme, high contrast |
noir |
Cinematic dark, cream serif, gold accent, prose entries |
swiss |
Strict grid, Helvetica, red accent — pure information design |
tech-dev |
Developer-focused with code-style accents |
Each template ships with two to four color palettes — 34 across the twelve. List them all:
pnpm cvmake list-templates- Monorepo — pnpm 9 workspaces + Turbo
- Schema — Zod
- Rendering — React 18 + Puppeteer (headless Chrome → PDF)
- Web UI — Next.js 16 (App Router) + Tailwind CSS 4
- CLI — Commander 12
- Testing — Vitest, Playwright (e2e), visual regression via pixelmatch
(Maintainers only.) All 4 published packages bump in lockstep:
pnpm -r --filter "@codevena/cvmake-{cli,core,schema,templates}" \
exec pnpm version <major|minor|patch>
git add -p # review the version bumps
git commit -m "release: vX.Y.Z"
git tag "v$(node -p "require('./apps/cli/package.json').version")"
git push origin main --tagsThe tag push triggers .github/workflows/release.yml, which first verifies on
Node 20 and 22 (build, unit tests, integration tests, and a pack/install smoke
test against the real tarballs), then publishes all 4 packages.
Authentication is npm Trusted Publishing via OIDC — there is no NPM_TOKEN.
All 4 packages have a trusted publisher configured (verified 2026-09-09). The
settings live at https://www.npmjs.com/package/<pkg>/access:
| Field | Value |
|---|---|
| Organisation/User | Codevena |
| Repository | cvmake |
| Workflow filename | release.yml |
| Environment | (leave empty — this workflow declares none) |
The publisher must also permit direct npm publish — a newly created one
defaults to staged publishing, which this workflow does not use. All four
currently permit both npm publish and npm stage publish.
Two constraints worth knowing before changing the publish step: pnpm publish
cannot do OIDC (support landed in pnpm 10; this repo pins 9.12.0), and
npm pack does not rewrite the workspace:* protocol, which npm then rejects
on the consumer side. The workflow therefore packs with pnpm and publishes the
resulting tarballs with npm — and it packs exactly once: the pack job
produces the four tarballs, verify smoke-tests those files, and publish
pushes the same bytes. scripts/publish-packages.mjs checks the tag against all
four manifests, validates every supplied tarball (name, version, no leaked
workspace: protocol) before publishing anything, and skips packages already in
the registry, so a partial failure is resumed by re-running the workflow for the
same tag. (It can also pack for itself when run locally without --tarball-dir.)
See CONTRIBUTING.md. Bug reports, template ideas, and pull requests welcome.
See ROADMAP.md for what's planned and CHANGELOG.md for what has shipped.
MIT — see LICENSE.











