On this page
Copy-paste-ready YAML workflows to run DocsCI on every pull request. Pick the template that fits your project structure.
DOCSCI_TOKEN: Settings → Secrets and variables → Actions → New repository secret..github/workflows/ in your repository if it doesn't already exist.The simplest workflow — runs DocsCI on every push to main and every pull request. Fails the CI check if any snippet is broken or any finding exceeds threshold.
# .github/workflows/docsci.yml
name: DocsCI
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
paths:
- 'docs/**'
- '*.md'
- '**/*.mdx'
jobs:
docs-ci:
name: Verify documentation
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Archive docs
run: tar czf docs.tar.gz docs/ *.md 2>/dev/null || tar czf docs.tar.gz docs/
- name: Run DocsCI
id: docsci
run: |
set -euo pipefail
RESULT=$(curl -sf -X POST https://snippetci.com/api/runs/queue \
-H "Authorization: Bearer ${{ secrets.DOCSCI_TOKEN }}" \
-F "docs_archive=@docs.tar.gz" \
-w "\n%{http_code}" 2>/dev/null)
HTTP_CODE=$(echo "$RESULT" | tail -1)
BODY=$(echo "$RESULT" | head -n -1)
echo "body=$BODY" >> $GITHUB_OUTPUT
[ "$HTTP_CODE" -eq 200 ] || exit 1
- name: Check result
run: |
STATUS=$(echo '${{ steps.docsci.outputs.body }}' | jq -r '.status')
echo "DocsCI status: $STATUS"
[ "$STATUS" = "passed" ] || exit 1paths: ['docs/**', '*.md'] to the PR trigger to skip the check when only non-docs files change. This reduces unnecessary CI minutes.This workflow adds OpenAPI drift detection and posts a summary comment on every pull request. Requires pull-requests: write permission and the GITHUB_TOKEN (available automatically).
# .github/workflows/docsci-advanced.yml
name: DocsCI (Advanced)
on:
pull_request:
branches: [main, master]
jobs:
docs-ci:
name: Verify documentation
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Archive docs with OpenAPI spec
run: |
tar czf docs.tar.gz docs/ *.md **/*.mdx 2>/dev/null || \
tar czf docs.tar.gz docs/
- name: Run DocsCI
id: docsci
env:
DOCSCI_TOKEN: ${{ secrets.DOCSCI_TOKEN }}
run: |
set -euo pipefail
RESULT=$(curl -sf -X POST https://snippetci.com/api/runs/queue \
-H "Authorization: Bearer $DOCSCI_TOKEN" \
-F "docs_archive=@docs.tar.gz" \
-F "openapi_url=https://api.example.com/openapi.json" \
-F "branch=${{ github.head_ref }}" \
-F "commit_sha=${{ github.sha }}" \
-F "pr_number=${{ github.event.number }}" \
-F "repo=${{ github.repository }}")
echo "result=$RESULT" >> $GITHUB_OUTPUT
echo "$RESULT" | jq -r '.status' | grep -q "passed" || exit 1
- name: Post PR summary
if: always()
uses: actions/github-script@v7
with:
script: |
const result = JSON.parse('${{ steps.docsci.outputs.result }}' || '{}');
const status = result.status || 'unknown';
const findings = result.finding_count || 0;
const duration = ((result.duration_ms || 0) / 1000).toFixed(1);
const icon = status === 'passed' ? '✅' : '❌';
const body = [
`## ${icon} DocsCI: ${status}`,
`- **Findings:** ${findings}`,
`- **Duration:** ${duration}s`,
`- [View full report](https://snippetci.com/runs/${result.run_id})`,
].join('\n');
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});Replace api.example.com/openapi.json with your actual OpenAPI spec URL. Remove the openapi_url line if you don't have an OpenAPI spec.
For monorepos, detect which packages changed and run DocsCI only for those packages in parallel. Uses a matrix strategy for concurrent per-package execution.
# .github/workflows/docsci-monorepo.yml
name: DocsCI (Monorepo)
on:
pull_request:
paths:
- 'packages/*/docs/**'
- 'packages/*/README.md'
- 'docs/**'
jobs:
detect-changed-packages:
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.changed.outputs.packages }}
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- id: changed
run: |
PKGS=$(git diff --name-only HEAD~1..HEAD \
| grep -E '^packages/[^/]+/' \
| awk -F/ '{print $2}' | sort -u | jq -Rcn '[inputs]')
echo "packages=$PKGS" >> $GITHUB_OUTPUT
docs-ci:
needs: detect-changed-packages
if: ${{ needs.detect-changed-packages.outputs.packages != '[]' }}
runs-on: ubuntu-latest
strategy:
matrix:
package: ${{ fromJson(needs.detect-changed-packages.outputs.packages) }}
name: DocsCI — ${{ matrix.package }}
steps:
- uses: actions/checkout@v4
- name: Run DocsCI for ${{ matrix.package }}
run: |
cd packages/${{ matrix.package }}
tar czf /tmp/docs.tar.gz docs/ *.md 2>/dev/null || \
tar czf /tmp/docs.tar.gz docs/
curl -sf -X POST https://snippetci.com/api/runs/queue \
-H "Authorization: Bearer ${{ secrets.DOCSCI_TOKEN }}" \
-F "docs_archive=@/tmp/docs.tar.gz" \
-F "project=${{ matrix.package }}" \
| jq -e '.status == "passed"'Adjust the packages/*/docs/** glob to match your monorepo structure.
Run a full DocsCI pass nightly to catch drift that wasn't introduced by a PR (e.g., your API changed externally, or a runtime version updated).
# .github/workflows/docsci-nightly.yml
name: DocsCI Nightly
on:
schedule:
- cron: '0 2 * * *' # 2 AM UTC daily
workflow_dispatch: # allow manual trigger
jobs:
nightly-docs-check:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Archive all docs
run: find . -name '*.md' -o -name '*.mdx' | \
tar czf docs.tar.gz --files-from=-
- name: Run full DocsCI suite
run: |
RESULT=$(curl -sf -X POST https://snippetci.com/api/runs/queue \
-H "Authorization: Bearer ${{ secrets.DOCSCI_TOKEN }}" \
-F "docs_archive=@docs.tar.gz" \
-F "full_suite=true")
echo "$RESULT" | jq .
echo "$RESULT" | jq -e '.status == "passed"' || \
(echo "::error::DocsCI found $(echo $RESULT | jq .finding_count) issues" && exit 1)| Secret / Variable | Required | Description |
|---|---|---|
| DOCSCI_TOKEN | Required | DocsCI API token. Generate from Settings → Tokens. |
| GITHUB_TOKEN | Auto-injected | GitHub's built-in token — used for posting PR comments. No setup needed. |
| DOCSCI_PROJECT_ID | Optional | Pin runs to a specific project. Otherwise uses the default project for the token. |
The workflow fails with 401 Unauthorized
Check that the DOCSCI_TOKEN secret is set in Settings → Secrets. The token must match the project you're sending docs to. Tokens expire after 1 year by default.
The tar command fails — no docs directory found
Update the tar command to match your docs directory structure. Use `find . -name '*.md'` to list files before archiving. The docs archive must contain at least one .md or .mdx file.
Runs time out on large doc sets
Increase timeout-minutes to 20-30. For very large repos, use the paths filter to only trigger on documentation changes. Consider the monorepo workflow to parallelize across packages.
I want to block merges on findings
DocsCI exits with code 1 when status is 'failed'. The `jq -e '.status == "passed"'` check already blocks the CI step. GitHub will block the PR merge if you configure DocsCI as a required status check in branch protection rules.