No description
  • Lua 47.3%
  • CSS 18.9%
  • Shell 17.1%
  • HTML 8.9%
  • Dockerfile 7.8%
Find a file
Mark Gardner 1e4450256f Repository moved to Forgejo
This repository has moved to:
https://forgejo.phoenixtrap.com/mjg/resume-remixer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 00:41:40 -05:00
eg commenting out Indeed profile while it's hidden 2025-12-31 13:57:19 -06:00
scripts Sanitize filenames by replacing problematic characters 2025-12-31 11:20:14 -06:00
share/pandoc Remove explicit HTML ID from project names in template 2025-12-31 11:14:32 -06:00
.dockerignore containerized 2025-10-12 20:22:17 -05:00
.gitignore default save_pdf basename to ignored out directory 2025-01-26 20:06:19 -06:00
.plugin-versions asdf updates 2025-01-20 13:45:56 -06:00
.tool-versions remove asdf-plugin-manager; moving to mise 2025-02-14 09:19:09 -06:00
CLAUDE.md containerized 2025-10-12 20:22:17 -05:00
docker-compose.yaml containerized 2025-10-12 20:22:17 -05:00
Dockerfile containerized 2025-10-12 20:22:17 -05:00
LICENSE Initial commit 2025-01-20 18:59:11 +00:00
README.md Repository moved to Forgejo 2026-04-18 00:41:40 -05:00

resume-remixer

Warning

This repository has moved to forgejo.phoenixtrap.com/mjg/resume-remixer. This Codeberg repository is archived and no longer maintained. Please update your bookmarks and remotes.

Note

This project is hosted at forgejo.phoenixtrap.com/mjg/resume-remixer. The Codeberg repository is archived. Please open issues and submit contributions there.

A Pandoc-based resume generation system that maintains a single source of truth while producing customized outputs for different job applications and platforms.

The Problem

Job hunting as a software engineer requires:

  • Multiple resume versions customized for different roles and companies
  • Multiple output formats: print-ready PDFs, web-friendly HTML, plain text for ATSes
  • Content that varies by audience: hide irrelevant projects, emphasize different skills, limit bullet points for brevity
  • Version control: track what you sent to each company, revert changes when needed

Manually maintaining these variations leads to:

  • Copy-paste errors between versions
  • Outdated information in some formats but not others
  • Lost history of what you told each employer
  • Hours spent reformatting instead of applying to jobs

The Solution

resume-remixer uses a single JSON Resume schema-compliant YAML file as your source of truth, then transforms it through a configurable Pandoc filter pipeline to generate exactly the resume you need for each situation.

Key Features

  • Single source, multiple outputs: PDF, HTML, Markdown, plain text - all from one YAML file
  • Selective visibility: Mark entries with x-hidden: true to exclude them without deleting
  • Date-based filtering: Automatically hide work older than X years or projects started more than Y years ago
  • Bullet point limiting: Control highlights per section (e.g., max 2 per job, 1 per project)
  • Automatic section titles: "Projects" becomes "Selected Projects" when entries are hidden, "Recent Work Experience" when date-filtered, "Selected Recent Projects" when both
  • Version controllable: Branch per company, diff your changes, track what you sent where
  • Standards compliant: JSON Resume schema with proper x- prefixed extensions

Requirements

  • Pandoc - Universal document converter
  • WeasyPrint - PDF rendering engine
  • yq - YAML processor (for filename generation in save_pdf.sh)

Installation

macOS (Homebrew)

brew install pandoc weasyprint yq

Linux (Debian/Ubuntu)

sudo apt install pandoc weasyprint
# yq installation varies; see https://github.com/mikefarah/yq#install

Other platforms

See Pandoc installation and WeasyPrint installation for your platform.

Docker (all platforms)

No local installation required - just Docker:

# Build the image
docker build -t resume-remixer .

# Or use docker compose
docker compose build

Supports both amd64 and arm64 (Apple Silicon) architectures.

Quick Start

  1. Clone this repository:

    git clone https://codeberg.org/mjgardner/resume-remixer.git
    cd resume-remixer
    
  2. Edit eg/mjgardner_resume.yaml (or create your own) following the JSON Resume schema

  3. Generate your resume:

    Using native installation:

    # Generate PDF (saves to out/ directory)
    ./scripts/save_pdf.sh eg/mjgardner_resume.yaml
    
    # Generate HTML to stdout
    ./scripts/stdout_html.sh eg/mjgardner_resume.yaml > resume.html
    
    # Generate plain text (for ATS copy-paste)
    ./scripts/stdout_plain.sh eg/mjgardner_resume.yaml
    

    Using Docker:

    # Generate PDF using Docker
    docker run --rm \
      -v "$(pwd)/eg:/app/eg:ro" \
      -v "$(pwd)/out:/app/out" \
      resume-remixer \
      ./scripts/save_pdf.sh eg/mjgardner_resume.yaml
    
    # Or with docker compose
    docker compose run --rm resume-remixer \
      ./scripts/save_pdf.sh eg/mjgardner_resume.yaml
    

Usage

Basic Workflow

All generation scripts follow the pattern: script_name.sh path/to/resume.yaml

Available scripts (scripts/ directory):

  • save_pdf.sh - Generates PDF and saves to out/ with auto-generated filename
  • stdout_pdf.sh - Outputs PDF to stdout (for piping)
  • stdout_html.sh - Generates standalone HTML
  • stdout_markdown.sh - Outputs processed Markdown
  • stdout_plain.sh - Generates plain text with wrapping
  • stdout_unwrapped.sh - Plain text without line wrapping
  • stdout_unwrapped_full.sh - Full content plain text without wrapping

Customizing for Different Jobs

# Create branch for Company X focusing on DevOps
git checkout -b acme-corp-devops
# Hide irrelevant projects
vim eg/mjgardner_resume.yaml  # Add x-hidden: true to projects
# Adjust highlights limit for conciseness
vim share/pandoc/metadata/highlights_limit.yaml
./scripts/save_pdf.sh eg/mjgardner_resume.yaml
git commit -am "Customize for Acme Corp DevOps role"

# Later, for Company Y full-stack role
git checkout main
git checkout -b bigco-fullstack
# Different customizations...

Option 2: Multiple YAML files

cp eg/mjgardner_resume.yaml eg/resume_devops.yaml
# Edit resume_devops.yaml with role-specific changes
./scripts/save_pdf.sh eg/resume_devops.yaml

Using Docker for Customization

The Docker setup mounts your local files, so you can customize configs and resume data without rebuilding:

# Edit your resume or metadata configs locally
vim eg/mjgardner_resume.yaml
vim share/pandoc/metadata/highlights_limit.yaml

# Generate with Docker (picks up your changes)
docker compose run --rm resume-remixer \
  ./scripts/save_pdf.sh eg/mjgardner_resume.yaml

# Or using docker run
docker run --rm \
  -v "$(pwd)/eg:/app/eg:ro" \
  -v "$(pwd)/share/pandoc/metadata:/app/share/pandoc/metadata:ro" \
  -v "$(pwd)/out:/app/out" \
  resume-remixer \
  ./scripts/save_pdf.sh eg/mjgardner_resume.yaml

Volume mounts explained:

  • /app/eg - Your resume YAML files (read-only)
  • /app/share/pandoc/metadata - Filter configs (read-only)
  • /app/out - Generated files (read-write)

All scripts work the same in Docker as native - just prefix commands with the docker run/compose wrapper.

Configuration

Hiding Individual Entries

Add x-hidden: true to any entry to exclude it from output:

projects:
  - name: Relevant Project
    startDate: 2024-01-01
    highlights:
      - This appears in the resume

  - name: Outdated Project
    startDate: 2015-01-01
    x-hidden: true  # This won't appear
    highlights:
      - Hidden but preserved in source

When entries are hidden, section titles automatically get "Selected" prefix (e.g., "Selected Projects").

Date-Based Filtering

Edit share/pandoc/metadata/date_past.yaml:

x-date-past:
  work: { endDate: { year: 16 } }      # Hide jobs ended >16 years ago
  projects: { startDate: { year: 12 } }  # Hide projects started >12 years ago

When date filtering is active, section titles get "Recent" prefix (e.g., "Recent Work Experience").

Limiting Bullet Points

Edit share/pandoc/metadata/highlights_limit.yaml:

x-highlights-limit:
  work: 2        # Max 2 highlights per job
  projects: 1    # Max 1 highlight per project

Keeps the first N highlights, discards the rest. Ensure your most important highlights come first in your YAML.

Removing Metadata Fields

Edit share/pandoc/metadata/meta_remove.yaml:

x-meta-remove:
  certificates: credits  # Remove 'credits' field from certificates
  education: score       # Remove 'score' (GPA) from education
  publications: summary  # Remove summary from publications
  projects:              # Remove entire projects section (empty value)

Date Formatting

Edit share/pandoc/metadata/date_formats.yaml to customize date display using strftime patterns:

x-date-formats:
  work:
    startDate: { template: "%b %Y" }  # "Jan 2024"
    endDate: { template: "%b %Y" }

How It Works

resume-remixer uses a two-pass Pandoc architecture:

  1. First pass: YAML → Markdown with Lua filters applied

    • Remove unwanted fields (meta_remove.lua)
    • Hide marked entries (entry_hidden.lua)
    • Filter by date (date_past.lua)
    • Limit bullet points (highlights_limit.lua)
    • Format dates (date_formats.lua)
  2. Second pass: Markdown → final format (PDF/HTML/etc.)

    • Apply templates
    • Generate format-specific output

Each filter operates on Pandoc's AST (Abstract Syntax Tree) and can be configured independently. Filter order matters and is specified in share/pandoc/defaults/markdown.yaml.

For implementation details, see CLAUDE.md.

Project Structure

resume-remixer/
├── eg/                              # Example resume files
│   └── mjgardner_resume.yaml
├── scripts/                         # Generation scripts
│   ├── save_pdf.sh
│   ├── stdout_html.sh
│   └── ...
├── share/pandoc/
│   ├── defaults/                    # Pandoc configuration
│   │   ├── common.yaml
│   │   ├── markdown.yaml
│   │   └── html_pdf.yaml
│   ├── filters/                     # Lua filters
│   │   ├── entry_hidden.lua
│   │   ├── date_past.lua
│   │   ├── highlights_limit.lua
│   │   └── ...
│   ├── metadata/                    # Filter configuration
│   │   ├── date_past.yaml
│   │   ├── highlights_limit.yaml
│   │   └── ...
│   └── templates/                   # Output templates
│       ├── resume.html
│       ├── resume.markdown
│       └── resume.plain
└── out/                             # Generated files (gitignored)

Tips for Job Hunting

  1. Keep your source complete: Don't delete anything from your YAML. Use x-hidden: true instead.

  2. Branch per application: Helps you remember what you told each company.

  3. Commit before customizing: Easy to revert to your "standard" resume.

  4. Use multiple formats:

    • PDF for email/upload
    • HTML for portfolio website
    • Plain text for ATS copy-paste
    • Markdown for processing into other tools
  5. Order matters in YAML: Highlights are limited by taking the first N, so put your strongest bullets first.

  6. Test your output: Run ./scripts/save_pdf.sh after changes to ensure filters work as expected.

Contributing

This is primarily a personal tool, but if you find it useful and want to contribute improvements, feel free to open issues or pull requests on Codeberg.

License

See LICENSE file for details.

Author

Mark Gardner - phoenixtrap.com

Built during a 10-month job search when maintaining multiple resume versions manually became untenable.