Cloudflare Pages Deployment - Design Documentation¶
Workflow:
docs-cloudflare.ymlPurpose: Build MkDocs documentation and deploy to Cloudflare Pages Status: ✅ Production Ready
Architecture Overview¶
┌─────────────────────────────────────────────────────────────────┐
│ TRIGGER CONDITIONS │
├─────────────────────────────────────────────────────────────────┤
│ • Push to main (*.md, docs config, workflow file) │
│ • Manual workflow_dispatch │
│ • Weekly schedule (Sunday 00:00 UTC) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ JOB 1: BUILD DOCUMENTATION │
├─────────────────────────────────────────────────────────────────┤
│ Runner: ubuntu-latest │
│ │
│ Steps: │
│ 1. Configure Git (symlinks=false for Windows compat) │
│ 2. Checkout repo (full history for git plugins) │
│ 3. Setup Python 3.11 + pip cache │
│ 4. Install MkDocs + plugins │
│ 5. Configure Git user (for git-authors plugin) │
│ 6. Build docs → ABSOLUTE PATH ★ │
│ mkdocs build --site-dir /workspace/var/docs/user │
│ 7. Verify build output ★ │
│ • Check directory exists │
│ • Count HTML files > 0 │
│ • Exit 1 if validation fails │
│ 8. Generate build summary (GitHub step summary) │
│ 9. Upload artifact: "documentation" │
│ • Path: var/docs/user │
│ • Retention: 1 day │
│ • Fail if no files found │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ JOB 2: DEPLOY TO CLOUDFLARE PAGES │
├─────────────────────────────────────────────────────────────────┤
│ Runner: ubuntu-latest │
│ Depends on: build │
│ │
│ Steps: │
│ 1. Download artifact: "documentation" → dist/ │
│ 2. Deploy to Cloudflare Pages │
│ • Project: audiolab │
│ • Directory: dist/ │
│ • Branch: main │
│ 3. Generate deployment summary │
└─────────────────────────────────────────────────────────────────┘
↓
https://audiolab.pages.dev
★ = Critical design elements (see Critical Design Decisions)
Critical Design Decisions¶
1. Absolute Path for site_dir¶
Decision: Override MkDocs site_dir config with --site-dir CLI flag using absolute path
Rationale:
- Relative paths (../../../../var/docs/user) don't resolve consistently in CI
- ${{ github.workspace }} is guaranteed correct by GitHub Actions
- Eliminates path ambiguity and silent failures
Implementation:
- name: Build documentation
run: |
cd 03_INFRA/03_11_documentation_platform/03_11_01_user_documentation
mkdocs build --verbose --site-dir "${{ github.workspace }}/var/docs/user"
Alternative Considered: Fix relative path in mkdocs.yml
- ❌ Rejected: Would break local development builds
- ✅ Current solution: Works in both CI and local environments
2. Explicit Build Output Verification¶
Decision: Add dedicated verification step after build that FAILS if no HTML files generated
Rationale:
- MkDocs with strict: false can succeed without creating files
- Prevents silent failures that manifest later in deploy job
- Provides clear error messages at correct failure point
Implementation:
- name: Verify build output exists
run: |
cd ${{ github.workspace }}
if [ ! -d "var/docs/user" ]; then
echo "❌ ERROR: Build directory NOT found"
exit 1
fi
HTML_COUNT=$(find var/docs/user -name "*.html" | wc -l)
if [ "$HTML_COUNT" -eq 0 ]; then
echo "❌ ERROR: No HTML files generated!"
exit 1
fi
echo "✅ Build successful: $HTML_COUNT HTML files"
Why This Matters: - Build job fails EARLY if output missing - Deploy job never tries to download non-existent artifact - Error location matches actual problem location
3. Git Symlinks Disabled¶
Decision: Configure git config --global core.symlinks false before checkout
Rationale: - Repository contains symlinks (used in local Windows development) - GitHub Actions runners don't need symlinks - Prevents potential checkout issues on different platforms
Implementation:
- name: Configure Git to use regular files instead of symlinks
run: git config --global core.symlinks false
- name: Checkout repository
uses: actions/checkout@v4
Impact: Symlinks become regular files in CI (acceptable for documentation build)
4. Full Git History Required¶
Decision: Checkout with fetch-depth: 0
Rationale:
- git-revision-date plugin needs full history for "last updated" dates
- git-authors plugin needs history for contributor information
- Shallow clone would break these features
Implementation:
Trade-off: Slower checkout vs. accurate metadata (worth it)
5. Separate Build/Deploy Jobs¶
Decision: Two jobs instead of one monolithic job
Rationale: - Separation of concerns: Build vs. Deploy - Artifact as contract: Clean interface between jobs - Reusability: Deploy job could be reused for manual deploys - Debugging: Easier to identify which phase failed
Job Dependencies:
jobs:
build:
runs-on: ubuntu-latest
# ... build steps ...
deploy:
runs-on: ubuntu-latest
needs: build # Won't run if build fails
# ... deploy steps ...
Alternative Considered: Single job - ❌ Rejected: Harder to debug, less modular, can't reuse deploy step
MkDocs Configuration Integration¶
Key Config Settings¶
# mkdocs.yml
site_dir: ../../../../var/docs/user # Used for local builds
strict: false # Allow warnings (for dev)
plugins:
- gen-files: # Generates nav from repo structure
scripts:
- scripts/gen_nav.py
- literate-nav: # Reads generated SUMMARY.md
- section-index # README.md as section index
- search # Full-text search
How gen-files Plugin Works¶
- Build starts: MkDocs initializes plugins
- gen-files runs: Executes
scripts/gen_nav.py - Virtual files created: Script walks repo, creates virtual
.mdfiles in memory - Navigation generated: Writes
SUMMARY.mdwith literate-nav syntax - MkDocs processes: Converts virtual files to HTML
- Output written: All HTML written to
site_dir
Critical Insight: If site_dir doesn't get created, steps 1-5 succeed but step 6 fails silently with strict: false
Workflow Variables¶
GitHub Context Variables Used¶
| Variable | Value | Usage |
|---|---|---|
${{ github.workspace }} |
/home/runner/work/AudioLab/AudioLab |
Root workspace path |
${{ github.sha }} |
96038819... |
Commit SHA for summaries |
${{ github.ref_name }} |
main |
Branch name for Cloudflare |
${{ github.event_name }} |
push / workflow_dispatch |
Trigger type |
${{ secrets.CLOUDFLARE_API_TOKEN }} |
*** |
Cloudflare authentication |
${{ secrets.CLOUDFLARE_ACCOUNT_ID }} |
*** |
Cloudflare account |
${{ secrets.GITHUB_TOKEN }} |
Auto-generated | For git-revision-date plugin |
Computed Paths¶
# Working directory during build
WORK_DIR=/home/runner/work/AudioLab/AudioLab/03_INFRA/03_11_documentation_platform/03_11_01_user_documentation
# Output directory (absolute)
SITE_DIR=/home/runner/work/AudioLab/AudioLab/var/docs/user
# Artifact path (relative to workspace)
ARTIFACT_PATH=var/docs/user
Error Handling Strategy¶
Build Phase¶
# Step 1: Build (allows warnings)
mkdocs build --verbose --site-dir "${{ github.workspace }}/var/docs/user"
# Step 2: Verify (strict validation)
if [ ! -d "var/docs/user" ]; then
echo "❌ ERROR: Build directory NOT found"
ls -la # Show what exists
exit 1 # FAIL FAST
fi
# Step 3: Count files (quality check)
HTML_COUNT=$(find var/docs/user -name "*.html" | wc -l)
if [ "$HTML_COUNT" -eq 0 ]; then
echo "❌ ERROR: No HTML files generated!"
exit 1 # FAIL FAST
fi
Deploy Phase¶
# Step 1: Download artifact
- uses: actions/download-artifact@v4
with:
name: documentation
path: dist
# Step 2: Deploy (fails if API issues)
- uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: audiolab
directory: dist
Error Propagation: - Build verification fails → Build job fails → Deploy job skipped - Artifact download fails → Deploy job fails (shouldn't happen if build passed) - Cloudflare deploy fails → Deploy job fails, artifact retained for debugging
Performance Characteristics¶
Build Job¶
| Phase | Duration | Cacheable |
|---|---|---|
| Git checkout (full history) | ~10s | ❌ No |
| Python setup | ~5s | ✅ Yes (pip cache) |
| Install dependencies | ~15s | ✅ Yes (pip cache) |
| MkDocs build | ~30s | ❌ No |
| Verify output | ~2s | ❌ No |
| Upload artifact | ~5s | ❌ No |
| Total | ~67s | - |
Deploy Job¶
| Phase | Duration | Cacheable |
|---|---|---|
| Download artifact | ~3s | ❌ No |
| Cloudflare deploy | ~60-90s | ❌ No |
| Total | ~63-93s | - |
Total Workflow Duration: ~130-160 seconds
Optimization Opportunities¶
- ✅ Implemented: Python pip cache
- ✅ Implemented: Parallel jobs (build/deploy are sequential by design)
- ⚠️ Possible: Cache MkDocs plugins install
- ⚠️ Possible: Incremental builds (only changed files)
- ❌ Not viable: Cache full git history (defeats purpose of git plugins)
Monitoring & Observability¶
Success Indicators¶
- ✅ Build job completes with exit code 0
- ✅ HTML file count > 0 in verification step
- ✅ Artifact uploaded successfully
- ✅ Deploy job downloads artifact
- ✅ Cloudflare Pages deployment succeeds
- ✅ Site accessible at https://audiolab.pages.dev
Failure Detection¶
# GitHub Step Summary (visible in workflow UI)
echo "## 📚 Documentation Build Summary" >> $GITHUB_STEP_SUMMARY
echo "- **Files generated**: $FILE_COUNT" >> $GITHUB_STEP_SUMMARY
# Error annotation
echo "::error title=Build Failed::No HTML files generated"
# Warning annotation
echo "::warning title=Large Build::Generated $FILE_COUNT files"
# Notice annotation
echo "::notice::Build completed in 30s"
Debugging Information Logged¶
-
Build context:
-
Build results:
-
Deployment info:
Maintenance Procedures¶
Updating MkDocs Version¶
# Update requirements.txt
cd 03_INFRA/03_11_documentation_platform/03_11_01_user_documentation
pip install --upgrade mkdocs mkdocs-material
pip freeze > requirements.txt
# Test locally
mkdocs build --site-dir ../../../../var/docs/user
# Commit and push (workflow will test in CI)
git add requirements.txt
git commit -m "chore(docs): update MkDocs to vX.Y.Z"
Changing Site Directory¶
Option 1: Local only (recommended)
# mkdocs.yml - for local dev
site_dir: ../../../../var/docs/user
# workflow - override in CI
mkdocs build --site-dir "${{ github.workspace }}/new/path"
Option 2: Global change
# Update mkdocs.yml
site_dir: ../../../../new/path
# Update workflow
mkdocs build --site-dir "${{ github.workspace }}/new/path"
# Update artifact upload
path: new/path
Adding New Plugins¶
# 1. Install locally
cd 03_INFRA/03_11_documentation_platform/03_11_01_user_documentation
pip install mkdocs-plugin-name
# 2. Update requirements.txt
pip freeze > requirements.txt
# 3. Add to mkdocs.yml
# plugins:
# - plugin-name
# 4. Test build
mkdocs build --site-dir ../../../../var/docs/user
# 5. Commit changes
git add requirements.txt mkdocs.yml
git commit -m "feat(docs): add plugin-name for feature X"
Security Considerations¶
Secrets Management¶
| Secret | Purpose | Scope | Rotation |
|---|---|---|---|
CLOUDFLARE_API_TOKEN |
Deploy to Cloudflare Pages | Repo | Manual |
CLOUDFLARE_ACCOUNT_ID |
Identify Cloudflare account | Repo | Static |
GITHUB_TOKEN |
Git metadata for plugins | Auto-generated per run | Per-run |
Permissions¶
Principle: Minimal permissions required for workflow operation
Content Security¶
- ✅ Source: Trusted (repository markdown files)
- ✅ Build: Deterministic (MkDocs generates static HTML)
- ✅ Deploy: Isolated (Cloudflare Pages sandbox)
- ⚠️ External links: User-provided (in markdown)
- ⚠️ JavaScript: MkDocs theme + plugins only
Rollback Procedures¶
Rollback via Git¶
# Find previous successful deployment
git log --oneline | grep "deploy"
# Revert to previous commit
git revert HEAD
git push
# Or force push previous commit (DANGEROUS)
git reset --hard <previous-commit>
git push --force
Rollback via Cloudflare Dashboard¶
- Go to Cloudflare Pages dashboard
- Select "audiolab" project
- View deployments
- Click "Rollback" on previous successful deployment
Emergency: Manual Build & Deploy¶
# 1. Build locally
cd 03_INFRA/03_11_documentation_platform/03_11_01_user_documentation
mkdocs build --site-dir ../../../../var/docs/user
# 2. Deploy manually
cd ../../../../var/docs/user
npx wrangler pages publish . --project-name=audiolab
Related Documentation¶
- CLOUDFLARE_PAGES_TROUBLESHOOTING.md - Detailed troubleshooting guide
- MkDocs Configuration
- Navigation Generator
- Cloudflare Pages Action Docs
Changelog¶
| Date | Change | Reason |
|---|---|---|
| 2025-10-17 | Use absolute path for site_dir |
Fix silent build failures |
| 2025-10-17 | Add build output verification | Fail fast on empty builds |
| 2025-10-17 | Fix artifact upload path | Use relative path from workspace |
| 2025-10-15 | Initial implementation | Deploy docs to Cloudflare Pages |
Document Status: ✅ Complete Last Updated: 2025-10-17 Maintained By: AudioLab Infrastructure Team