GitHub Actions for documentation: changelog generation and keeping docs in sync
May 2025
GitHub Actions can do more for documentation than just build and deploy docs sites. This is a guide to the documentation-related Actions worth adding to your pipeline: secret scanning before commits ship, changelog generation from commit history, and keeping your Markdown docs in sync with code changes.
Secret scanning: before docs go public
Documentation often contains examples that reference real systems — API endpoints, environment variable names, configuration structures. It's common for examples to accidentally contain real credentials that were copy-pasted from a working setup.
gitleaks catches this before it reaches the remote:
- name: Scan for secrets in docs
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Configure it to scan your docs directory specifically if you want faster runs on documentation-only PRs:
- name: Scan docs
run: gitleaks detect --source ./docs --verbose
Keeping Markdown in sync with code
The hardest documentation problem in GitHub Actions is keeping your Markdown files accurate as code changes. Tests, lint, and type checking all verify that code is correct — nothing verifies that your docs describe the code correctly.
The approach that works: run a documentation maintenance job after every merge to main. The job extracts the diff from the merged PR, sends it along with your current documentation files to an LLM, and opens a draft PR with proposed updates.
DocDr implements this pattern:
name: Documentation maintenance
on:
push:
branches: [main]
jobs:
docdr:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: ianjamesburke/docdr@v1
DocDr strips noise from the diff (lock files, minified bundles, generated code) before processing, so the LLM sees a clean representation of what actually changed.
Changelog generation
If you use conventional commits (feat:, fix:, chore:), automatic changelog generation is a straightforward win. git-cliff produces clean changelogs from commit history:
name: Release
on:
push:
tags: ['v*']
jobs:
changelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: orhun/git-cliff-action@v2
with:
args: --latest --strip header
env:
OUTPUT: CHANGES.md
- name: Create release
uses: softprops/action-gh-release@v1
with:
body_path: CHANGES.md
Docs site deployment
If you have a docs site (MkDocs, Docusaurus, Sphinx), wire deployment to pushes on main:
name: Deploy docs
on:
push:
branches: [main]
paths: ['docs/**', 'mkdocs.yml']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.x'
- run: pip install mkdocs-material
- run: mkdocs gh-deploy --force
The paths filter is important — don't rebuild your docs site on every code push, only when docs actually change.
Link checking
Documentation links rot. Add a scheduled job to check for broken links:
name: Check docs links
on:
schedule:
- cron: '0 9 * * 1' # Monday mornings
jobs:
linkcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: lycheeverse/lychee-action@v1
with:
args: --verbose --no-progress './docs/**/*.md'
The complete picture
Secret scanning runs on every PR and blocks merge if credentials are found. DocDr runs after every merge to main and opens draft PRs for documentation updates. Changelog generation runs on release tags. Link checking runs weekly. Docs site deploys when docs change.
None of these require ongoing maintenance once they're set up, and together they keep documentation from becoming the liability it usually is.