CI/CD Integration
Zenzic is automation-ready out of the box. The --format json flag and --save option expose machine-readable output that any CI/CD system can consume to drive dynamic badges, quality gates, and regression detection.
JSON Output
Every check command supports --format json:
# Aggregated report for all checks
zenzic check all --format json
# Individual checks
zenzic check links --format json
zenzic check references --format json
# Scoring and regression
zenzic score --format json
zenzic diff --format json
zenzic check all --format json
{
"links": ["guides/setup.md:12 — Link target 'install.md' not found"],
"orphans": ["old-page.md"],
"snippets": [{"file": "api/ref.md", "line": 5, "message": "Snippet target not found"}],
"placeholders": [{"file": "index.md", "line": 1, "issue": "TODO", "detail": "Fix this"}],
"unused_assets": ["images/old-logo.png"],
"references": [],
"nav_contract": []
}
zenzic score --format json
{
"project": "zenzic",
"score": 100,
"threshold": 0,
"status": "success",
"timestamp": "2026-03-24T12:00:00+00:00",
"categories": [
{"name": "links", "weight": 0.35, "issues": 0, "category_score": 1.0, "contribution": 0.35},
{"name": "orphans", "weight": 0.20, "issues": 0, "category_score": 1.0, "contribution": 0.20},
{"name": "snippets", "weight": 0.20, "issues": 0, "category_score": 1.0, "contribution": 0.20},
{"name": "placeholders", "weight": 0.15, "issues": 0, "category_score": 1.0, "contribution": 0.15},
{"name": "assets", "weight": 0.10, "issues": 0, "category_score": 1.0, "contribution": 0.10}
]
}
Individual commands (check links, check orphans, etc.)
Each individual check command returns a uniform findings structure:
{
"findings": [
{"rel_path": "guides/setup.md", "line_no": 42, "code": "BROKEN_LINK", "severity": "error", "message": "Link target 'install.md' not found"}
],
"summary": {
"errors": 1, "warnings": 0, "info": 0,
"security_incidents": 0, "security_breaches": 0,
"elapsed_seconds": 0.042
}
}
Exit codes are preserved in JSON mode: exit 0 when only warnings are found,
exit 1 on errors (or warnings under --strict), exit 2 on Shield breaches,
exit 3 on Blood Sentinel — the same contract as terminal output.
GitHub Actions: Zenzic Shield Gate
The simplest integration — fails the build on any documentation error.
- uvx (zero-setup)
- astral-sh/setup-uv (pinned version)
No Python setup required. uvx fetches and runs Zenzic in a throwaway
environment on every run. Ideal for documentation-only repositories or
teams that do not otherwise need a Python environment in their CI:
# .github/workflows/docs.yml
name: Documentation Quality
on:
push:
branches: [main]
paths: ['docs/**', 'mkdocs.yml']
jobs:
zenzic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Lint documentation
run: uvx --pre zenzic check all --strict
- name: Check references and run Shield
run: uvx --pre zenzic check references
Use astral-sh/setup-uv when you need a pinned Zenzic version,
faster installs on repeated runs (cached wheel), or when your project
already uses uv for dependency management:
# .github/workflows/docs.yml
name: Documentation Quality
on:
push:
branches: [main]
paths: ['docs/**', 'mkdocs.yml']
jobs:
zenzic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Lint documentation
run: uvx --pre zenzic check all --strict
- name: Check references and run Shield
run: uvx --pre zenzic check references
The enable-cache: true option caches the uv tool cache across runs,
eliminating the PyPI download on every push.
Exit code 2 means a credential was detected in a reference URL. Exit code 3 means a link resolves to an OS system path (Blood Sentinel). Both require immediate investigation — rotate any exposed credential and remove the offending link.
GitHub Actions: Dynamic Score Badge
This workflow reads the .zenzic-score.json snapshot and pushes the live score to a Shields.io endpoint via a GitHub Gist, keeping your badge current on every push.
# .github/workflows/zenzic-badge.yml
name: Zenzic Score Badge
on:
push:
branches: [main]
jobs:
score:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: 🛡️ Run Zenzic Score
id: zenzic_step
run: |
uvx --pre zenzic score --save # threshold read from fail_under in zenzic.toml
SCORE=$(jq '.score' .zenzic-score.json)
echo "SCORE=$SCORE" >> "$GITHUB_OUTPUT"
- name: 🔄 Update Gist for Badge
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: ${{ secrets.ZENZIC_GIST_ID }}
filename: zenzic-score.json
label: "🛡️ zenzic score"
message: "${{ steps.zenzic_step.outputs.SCORE }}/100"
valColorRange: ${{ steps.zenzic_step.outputs.SCORE }}
maxColorRange: 100
minColorRange: 0
Setup steps:
- Create a GitHub Gist (public or secret).
- Create a Personal Access Token with
gistscope and store it asGIST_SECRETin your repo secrets. - Store your Gist's ID (the long hex in the URL) as
ZENZIC_GIST_IDin your repo secrets. - Add the dynamic badge URL to your
README.md:
[](https://github.com/PythonWoods/zenzic)
Note:
valColorRange/maxColorRange/minColorRangeproduce a smooth green→yellow→red gradient based on the score value.jqis pre-installed on all GitHub-hosted runners.
Regression Detection
zenzic diff compares the current score against the saved .zenzic-score.json baseline:
- name: Detect score regression
run: |
uvx --pre zenzic score --save # update snapshot
uvx --pre zenzic diff --threshold 5 # fail if score drops > 5 points
Combine with branch protection rules to block merges that degrade documentation quality.
Exit Codes Reference
| Code | Meaning | Badge action |
|---|---|---|
0 | All checks passed | Keep badge green |
1 | One or more checks failed | Set badge to failing / ef4444 |
2 | Zenzic Shield: credential detected | Rotate credential immediately |
3 | Blood Sentinel: path traversal detected | Remove offending link immediately |
For the full badge copy-paste reference, see Official Badges.