This directory contains the professional resume managed as code using the JSON Resume format.
The resume is stored in a structured JSON format that serves as the single source of truth for professional information across multiple platforms.
The repository supports multiple resume variants optimized for different positions or roles:
resume.<lang>.json- Default resume (e.g.,resume.en.json,resume.fr.json)resume.<lang>.<position>.json- Position-specific resume (e.g.,resume.en.cto.json,resume.fr.lead-dev.json)
resume.common.json- Shared resume content inherited by all variantsresume.en.json- English version of the resumeresume.en.lead-dev.json- English lead developer versionresume.fr.json- French version of the resumeresume.fr.lead-dev.json- French lead developer versionpdf/- Directory containing generated PDF versions- PDFs are auto-generated for each resume file
- Position-specific resumes generate PDFs like
resume.en.cto.pdf - See PDF Generation below
You can create multiple resume variants optimized for different positions (CTO, Lead Developer, Engineering Manager, etc.). Each variant can:
- Emphasize different aspects of the same experience
- Highlight role-specific skills and achievements
- Tailor the content for specific job applications
Example:
resume.en.cto.json- CTO-focused version emphasizing leadership and strategyresume.en.lead-dev.json- Technical leadership version emphasizing hands-on developmentresume.en.staff-engineer.json- Staff engineer version emphasizing technical expertise
All resume files are automatically discovered and processed by the CI/CD pipeline.
The resume follows the standard JSON Resume schema v1.0.0 without any custom extensions.
We support bilingual resumes with the following language codes:
en- Englishfr- French (Français)
Both base and position-specific files follow the exact same schema structure from jsonresume.org/schema.
Shared contact details and other reusable data live in resume.common.json.
Language-specific files declare "extends": "./resume.common.json" and only
override localized content. Work and project entries carry an "id" so shared
fields can be merged across languages.
- Edit your resume JSON files (e.g.,
resume.en.json,resume.fr.lead-dev.json) - Run
make humanize-resumeto normalize punctuation and whitespace withhumanize-ai-lib - Keep the structure consistent between language variants of the same position
- Commit the changes to trigger automated updates
- Copy an existing resume file (e.g.,
resume.en.json) - Rename it following the pattern
resume.<lang>.<position>.json(e.g.,resume.en.cto.json) - Edit the content to emphasize the specific position
- Run
make validate-resumeto ensure the file is valid - Run
make generate-pdfsto generate the PDF locally - Commit the new file - the CI/CD pipeline will automatically discover and process it
Supported position slugs: Use lowercase with hyphens for multi-word positions (e.g., cto, lead-dev, staff-engineer, engineering-manager)
To keep the content recruiter-friendly, we automatically strip smart quotes, non-breaking spaces, em dashes, and other AI artifacts using humanize-ai-lib.
Run the cleanup before validating or generating PDFs:
make humanize-resumeThe command runs the script in resume/scripts/humanize-resume. Pass custom files or limit output to ASCII characters with:
cd resume/scripts/humanize-resume
npm run humanize -- --keyboard-only ../../resume.en.json ../../resume.fr.jsonThe script rewrites the JSON files only when changes are detected and prints a short summary of normalized symbols.
The resume data follows the JSON Resume schema v1.0.0. You can validate it using:
npm install -g resume-cli
# Resolve inheritance before validating manually
node ./resolve-resume.js ./resume.en.json > /tmp/resume.en.resolved.json
node ./resolve-resume.js ./resume.fr.json > /tmp/resume.fr.resolved.json
resume validate /tmp/resume.en.resolved.json
resume validate /tmp/resume.fr.resolved.jsonOr simply validate JSON structure with jq:
jq empty resume.en.json && echo "✅ Valid"
jq empty resume.fr.json && echo "✅ Valid"Important Note: LinkedIn's Profile Edit API is restricted to approved LinkedIn Partner Program applications. Direct automated profile updates require special partnership approval from LinkedIn.
- LinkedIn API does not allow general developers to programmatically update profile details (headline, summary, etc.)
- Only approved partners can use the Profile Edit API
- Most automation tools can only post content, not edit profile information
Until LinkedIn API access is granted:
- Manual Updates: Use the resume files as a reference when updating LinkedIn profile manually
- Apply for Partnership: If automated updates are critical, apply for the LinkedIn Partner Program
- Alternative Automation: Consider using the workflow as a reminder/notification system when resume files change
The repository uses a modular CI/CD workflow structure:
Main Workflows:
-
main-ci.yml- Runs on push to main branch- Validates resumes using composite actions
- Generates PDFs
- Commits PDFs to repository
- Creates LinkedIn update reminder issue
-
pull-request-ci.yml- Runs on pull requests- Validates resumes
- Generates PDF previews as artifacts
- Comments on PR with download link
-
shared-ci.yml- Reusable workflow for common CI tasks- Validates both resume files
- Generates PDFs
Composite Actions:
.github/actions/validate-resume- Validates JSON Resume files.github/actions/generate-resume-pdf- Generates PDFs from JSON Resume
Additional Workflows:
greetings.yml- Welcomes new contributorssemantic-pull-request.yml- Enforces semantic PR titlesstale.yml- Manages stale issues and PRs
For future enhancement when API access is available, the workflow can be extended to use the LinkedIn API directly.
PDFs are automatically generated from the JSON Resume files using a custom ESCEMI theme that follows CV Coach best practices for senior technical profiles.
The custom theme (jsonresume-theme-escemi) is optimized for:
- Two-column layout: Contact info and skills on the left, professional experience on the right
- Visual hierarchy: Clear distinction between sections, positions, and achievements
- ATS-friendly: Simple formatting that passes automated screening systems
- ESCEMI branding: Source Sans Pro font with official ESCEMI colors (dark blue #1c3144, gold #ecb807)
- Metric emphasis: Highlights quantifiable achievements and business impact
- 6-second scannability: Recruiters can instantly identify key information
- Print optimization: A4 format with stable page breaks and margins
See themes/escemi/README.md for complete theme documentation.
- On main branch: PDFs are generated and committed to
resume/pdf/directory - On pull requests: PDFs are generated as artifacts for preview before merging
To generate all PDFs locally using the custom theme:
make generate-pdfsTo generate a single PDF (useful for debugging), run the generator directly:
cd .github/actions/generate-resume-pdf
npm install
# Example: English
npm run generate-pdf -- ../../resume/resume.en.json ../../resume/pdf/resume.en.pdf
# Example: English (position-specific)
npm run generate-pdf -- ../../resume/resume.en.cto.json ../../resume/pdf/resume.en.cto.pdfThe resume uses the following main sections from JSON Resume schema:
- basics - Personal information, contact details, profiles
- work - Work experience
- education - Educational background
- skills - Technical and professional skills
- languages - Language proficiencies
- projects - Notable projects (optional)
# Pretty print the English resume
cat resume.en.json | jq .
# Pretty print the French resume
cat resume.fr.json | jq .
# Extract English summary
cat resume.en.json | jq -r '.basics.summary'
# Extract French label
cat resume.fr.json | jq -r '.basics.label'
# List all skills from English resume
cat resume.en.json | jq -r '.skills[].name'
# Compare names in both files (should be the same)
echo "English: $(jq -r '.basics.name' resume.en.json)"
echo "French: $(jq -r '.basics.name' resume.fr.json)"