CI/CD

Docs as Code: Build a CI/CD Pipeline for Your Documentation

Your code has CI/CD. Your docs don’t.

Every modern engineering team has automated builds, tests, and deployments for their code. But documentation? That’s still someone manually exporting a PDF, uploading it to Confluence, and hoping it’s the latest version.

This post shows you how to treat documentation like code: version-controlled Markdown in a Git repo, automatically rendered to branded PDFs on every push. No manual steps, no stale documents.

The stack

PaperQuire gives you three tools that work together:

  1. .paperquire.yml — project config that locks in your template, branding, and document options
  2. CLIpaperquire render and paperquire batch for scripting and local builds
  3. GitHub Actionpaperquire/render-action for automated builds in CI

Each one builds on the previous. The config file means no one has to remember flags. The CLI means you can test locally. The action means it happens automatically.

Step 1: Add a project config

Drop a .paperquire.yml in your repo root. Every render — GUI, CLI, and CI — picks up these settings automatically:

template: corporate
toc: true
toc-depth: 3
h1-page-break: true
cover:
  title: "Project Documentation"
  author: "Engineering Team"
branding:
  primary-color: "#2563eb"

This is your single source of truth for how documents look. Change it once, and every PDF across every environment updates.

Step 2: Test locally with the CLI

Before committing, verify your docs render correctly:

# Render a single file
paperquire docs/architecture.md -o out/architecture.pdf

# Batch render the entire docs directory
paperquire batch ./docs -o ./out

# Dry run — validate without producing output
paperquire batch ./docs --dry-run

The CLI reads .paperquire.yml automatically. The output is identical to what CI will produce.

Step 3: Automate with the GitHub Action

Add one workflow file and your docs build themselves:

# .github/workflows/docs.yml
name: Build Documentation

on:
  push:
    paths:
      - 'docs/**/*.md'
      - '.paperquire.yml'

jobs:
  render:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: paperquire/render-action@v1
        with:
          files: 'docs/*.md'
          output: build/pdfs

      - uses: actions/upload-artifact@v4
        with:
          name: documentation
          path: build/pdfs/

Every push that touches a Markdown file or the config triggers a fresh build. PDFs are available as downloadable artifacts from the Actions tab.

Real-world patterns

Gate docs in pull requests

Catch formatting issues before they hit main. Reviewers can download the rendered PDFs directly from the PR:

on:
  pull_request:
    paths: ['docs/**', '.paperquire.yml']

jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: paperquire/render-action@v1
        with:
          files: 'docs/*.md'
          output: preview/

      - uses: actions/upload-artifact@v4
        with:
          name: pdf-preview
          path: preview/

Ship docs with releases

Attach the latest PDFs to every GitHub release automatically:

on:
  release:
    types: [published]

jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: paperquire/render-action@v1
        with:
          files: 'docs/*.md'
          output: dist/

      - name: Attach to release
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh release upload ${{ github.event.release.tag_name }} dist/*.pdf

Customers downloading your release get up-to-date documentation bundled right in.

Nightly builds for large doc sets

For repos with dozens of documents where you don’t want to rebuild on every push:

on:
  schedule:
    - cron: '0 6 * * 1-5'  # Weekdays at 6 AM UTC
  workflow_dispatch:        # Plus manual trigger

jobs:
  render:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: paperquire/render-action@v1
        with:
          files: 'docs/**/*.md'
          output: build/docs

      - uses: actions/upload-artifact@v4
        with:
          name: nightly-docs
          path: build/docs/
          retention-days: 14

Non-GitHub CI

The CLI works in any CI system. GitLab CI, Jenkins, CircleCI, Azure Pipelines — anywhere you can run a shell command:

# GitLab CI example
build-docs:
  image: node:20
  script:
    - npm install -g paperquire
    - paperquire batch ./docs -o ./out --continue-on-error
  artifacts:
    paths:
      - out/*.pdf

Project structure

Here’s what a well-organized docs-as-code repo looks like:

my-project/
├── .paperquire.yml          # Shared doc config
├── .github/
│   └── workflows/
│       └── docs.yml         # CI pipeline
├── docs/
│   ├── architecture.md
│   ├── api-reference.md
│   ├── onboarding.md
│   └── runbook.md
└── src/
    └── ...

Markdown lives alongside code. The config file and workflow are checked in. Anyone cloning the repo can render docs locally with paperquire batch ./docs, and CI handles the rest.

Why this matters

Your documentation pipeline should be as reliable as your deployment pipeline. With PaperQuire, it takes one config file and one workflow to get there.

Get started

← All posts