A lightweight Docker image and GitHub Actions for rendering Markdown to PDF and creating file dashboards. Built with Go and headless Chrome for high-quality, GitHub-like PDF output.
- Centralized rendering logic - Keep rendering in one place for easy maintenance
- Code reuse - Use across multiple projects as GitHub Actions
- Speed up CI/CD - Pre-built Docker images significantly faster than installing dependencies
- Compact image size - Optimized Docker image (~800 MB)
Renders Markdown files to PDF using headless Chrome with GitHub-flavored markdown support.
Features:
- β GitHub-flavored markdown rendering
- β Code blocks with syntax highlighting
- β Tables with proper formatting
- β Nested lists (bullets and numbered)
- β Embedded images with base64 encoding
- β Headings, paragraphs, blockquotes
- β Task lists and text formatting
- β Automatic source folder zipping
- β Ukrainian and international character support
Usage:
- name: Render Markdown to PDF
uses: kuzik/markdown-pdf-action/markdown-to-pdf@v1
with:
config: |
- source: "docs/**/*.md"
output: "output/docs/"
type: "subfolders"
- source: "README.md"
output: "output/README.pdf"
type: "single"Configuration (inline YAML):
# Render all README.md files in subdirectories separately
- source: "docs/**/*.md"
output: "output/docs/"
type: "subfolders"
# Combine multiple markdown files into a single PDF
- source: "guides/*.md"
output: "output/complete-guide.pdf"
type: "single"
# Combine all README.md files from subfolders into one PDF
- source: "projects/**/README.md"
output: "output/all-projects.pdf"
type: "combine"
# Render a single file
- source: "README.md"
output: "output/README.pdf"
type: "single"Types:
subfolders- Renders each matched README.md file separately to the output directory, named after the parent folder. If asrcfolder exists in the same directory as the markdown file, it will be automatically zipped.single- Combines all matched files into a single PDFcombine- Finds all README.md files matching the pattern and combines them into one PDF with folder names as section headers
Custom CSS (optional css field):
Each job may set a css field to override the built-in GitHub-like styles. The
CSS is injected after the base stylesheet, so it wins on any conflict. Works
with every type. The value is either a path to a .css file (relative to the
repository root) or a block of inline CSS:
# Inline CSS β e.g. justified text for lecture handouts
- source: "lectures/**/README.md"
output: "output/lectures/"
type: "subfolders"
css: |
body { text-align: justify; hyphens: auto; }
# Or point at a stylesheet file
- source: "lectures/**/README.md"
output: "output/lectures.pdf"
type: "combine"
css: "styles/lectures.css"Generate batches of PDFs by merging a Go template with JSON data. Perfect for creating personalized documents like exams, certificates, or reports.
Features:
- β Go text/template syntax support
- β HTML or Markdown templates
- β Batch generation from JSON data
- β Custom styling per document
- β Automatic PDF output
Usage:
- name: Hydrate Exam Templates
uses: kuzik/markdown-pdf-action/template-hydrator@v1
with:
template: "templates/exam.html"
data: "data/students.json"
output: "dist/exams"JSON Data Structure:
The input JSON must be a map where keys become output filenames:
{
"exam_student_001": {
"StudentName": "John Doe",
"Subject": "Advanced Physics",
"Date": "2024-05-20",
"Question1": "Explain entropy..."
},
"exam_student_002": {
"StudentName": "Jane Smith",
"Subject": "Advanced Physics",
"Date": "2024-05-20",
"Question1": "Discuss thermodynamics..."
}
}Template Example:
<h1>Exam: {{ .Subject }}</h1>
<p>Student: {{ .StudentName }}</p>
<p>Date: {{ .Date }}</p>
<hr>
<div>{{ .Question1 }}</div>Creates an HTML dashboard with links to download all generated files.
Features:
- β Lists all files in the output directory
- β Grouped by folders
- β Download links for each file
- β Shows source zip files when available
- β Clean, responsive HTML design
Usage:
- name: Create Files Dashboard
uses: kuzik/markdown-pdf-action/files-dashboard@v1
with:
source: "output/"
output: "output/index.html"
format: "markdown" # Options: html, markdown, both- Go 1.25 or later
- Docker (required for PDF rendering with Chrome)
# Install dependencies
go mod download
# Build all commands
go build -o bin/markdown-to-pdf ./cmd/markdown-to-pdf
go build -o bin/files-dashboard ./cmd/files-dashboard
go build -o bin/template-hydrator ./cmd/template-hydrator
# Build Docker image
docker build -t markdown-pdf-action:local .# Run the test scripts (uses Docker)
./example/test-render.sh
./example/test-dashboard.sh
./example/test-hydrator.sh
# View results
ls -lh example/output/The example includes:
- Code blocks (Python, JavaScript, Bash)
- Complex tables with GitHub styling
- Deeply nested lists
- Embedded images (logo, diagram, screenshot)
- Ukrainian text support
- Various markdown features
docker build -t markdown-pdf-action .The Dockerfile uses multi-stage builds:
- Builder stage - Compiles Go binaries
- Runtime stage - Small Debian base with Chromium and essential tools
# Render markdown with inline config
docker run -v $(pwd):/github/workspace markdown-pdf-action:local \
markdown --config='
- source: "example/input/**/*.md"
output: "example/output/"
type: "subfolders"
'
# Hydrate templates with data
docker run -v $(pwd):/github/workspace markdown-pdf-action:local \
hydrate --template=templates/exam.html --data=data/students.json --output=dist/exams
# Create dashboard
docker run -v $(pwd):/github/workspace markdown-pdf-action:local \
dashboard --source example/output --output example/output/index.html --format both.
βββ cmd/
β βββ markdown-to-pdf/ # Markdown to PDF renderer
β β βββ main.go
β β βββ template.html # HTML template for PDF styling
β βββ files-dashboard/ # HTML dashboard generator
β β βββ main.go
β β βββ dashboard.html # HTML template
β β βββ dashboard-github.md
β β βββ dashboard-relative.md
β βββ template-hydrator/ # Template hydration tool
β βββ main.go
β βββ template.html # HTML wrapper template
βββ internal/ # Shared packages
β βββ templates/ # Template loading utilities
β βββ markdown/ # Markdown to HTML conversion
β βββ images/ # Image embedding (base64)
β βββ pdf/ # PDF generation with Chrome
β βββ ziputil/ # Zip archive utilities
βββ markdown-to-pdf/
β βββ action.yml # GitHub Action definition
βββ files-dashboard/
β βββ action.yml # GitHub Action definition
βββ template-hydrator/
β βββ action.yml # GitHub Action definition
βββ example/
β βββ input/ # Example markdown files
β βββ output/ # Generated output
β βββ test-render.sh # Test script for rendering
β βββ test-dashboard.sh # Test script for dashboard
βββ Dockerfile # Multi-stage Docker build
βββ entrypoint.sh # Action entrypoint script
βββ go.mod # Go dependencies
βββ README.md # This file
The PDF renderer supports:
- β Syntax highlighting - Pygments-style formatting for code blocks
- β Images - Relative paths from markdown file directory
- β GitHub-style rendering - GFM (GitHub Flavored Markdown)
- β Tables - Full table support with borders and alignment
- β Lists - Nested lists with multiple levels
- β Typography - Headers, bold, italic, inline code
The HTML dashboard shows:
| Column | Description |
|---|---|
| File Name | Name of the generated file |
| Download | Direct download link |
| Source Zip | Link to zipped source code (if applicable) |
Each folder from the source directory is displayed as a separate section.
This project is designed for internal use and code reuse across multiple projects.
To add features:
- Implement in
cmd/markdown-to-pdf/main.goorcmd/files-dashboard/main.go - Test locally with the example
- Update documentation
- Submit PR
- Use glob patterns for flexible file matching:
docs/**/*.md - The
subfolderstype preserves directory structure - Images should be in the same directory or subdirectory as the markdown
- Test your render config with the example before using in CI/CD
PDF not generating:
- Check YAML config syntax
- Verify glob patterns match your files
- Ensure output directory is writable
Images not showing:
- Images must use relative paths
- Images should be in the same directory as the markdown
- Supported formats: PNG, JPEG, GIF
Build fails:
- Run
go mod tidyto update dependencies - Ensure Go 1.25 or later is installed
- Check for compile errors in
cmd/directories