Pipelines
The build, content, and image pipelines — how raw inputs become deployable sites.
The Wheel of Heaven ecosystem runs three pipelines that turn raw inputs into deployable artifacts:
- Build pipeline — Git push → Zola build → Cloudflare deploy
- Content pipeline — author → review → validate → publish
- Image pipeline — source → process → optimize → CDN
This page covers each. For Cloudflare Pages specifics (DNS, custom domains, headers, build environment), see CI & Deploy. The per-page social-card renderer is its own thing — see OG Image Pipeline.
Build pipeline#
flowchart LR
push["Push<br/>(Git)"]
clone["Clone<br/>(with submodules)"]
build["Build<br/>(Zola)"]
deploy["Deploy<br/>(edge)"]
push --> clone --> build --> deploy
push -.-> hook["GitHub webhook"]
clone -.-> subs["submodules initialized"]
build -.-> html["static HTML/CSS"]
deploy -.-> cf["Cloudflare Pages CDN"]Trigger#
Every push to main triggers:
- GitHub webhook notifies Cloudflare
- Cloudflare Pages starts a build
- Build output deployed globally on success
Build steps#
1. Clone repository#
Cloudflare clones the repo including submodules:
git clone --recursive <repo-url>2. Download Zola#
Zola isn’t pre-installed on CF Pages runners, so the build command downloads it:
curl -sL https://github.com/getzola/zola/releases/download/v0.22.1/zola-v0.22.1-x86_64-unknown-linux-gnu.tar.gz -o zola.tar.gz
tar xzf zola.tar.gz3. Build site#
For www:
./zola buildFor api (with prebuild step):
python3 scripts/prebuild.py
./zola build4. Deploy#
The output directory (public/) is deployed to the Cloudflare edge.
Build commands by site#
www#
curl -sL <zola-url> -o zola.tar.gz && tar xzf zola.tar.gz && ./zola buildapi#
curl -sL <zola-url> -o zola.tar.gz && tar xzf zola.tar.gz && python3 scripts/prebuild.py && ./zola buildBuild output#
www: ~1,251 HTML pages, 86 sections, compiled CSS, JS bundles, search index (JSON), sitemap.
api: JSON endpoints, search index, CORS headers.
Build duration#
| Site | Typical build time |
|---|---|
| www | ~30 seconds |
| api | ~15 seconds |
Validation pipeline (data-content)#
data-content has its own CI workflow that runs on every push and PR:
# .github/workflows/validate.yml
name: Validate Content
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: python scripts/validate.pyDeployment flow#
flowchart TB
push["data-content<br/>(push)"]
wwwsub["www.wheelofheaven.io<br/>(submodule update)"]
apisub["api.wheelofheaven.io<br/>(submodule update)"]
wwwcf["Cloudflare Pages<br/>(www)"]
apicf["Cloudflare Pages<br/>(api)"]
wwwout["www deployed"]
apiout["api deployed"]
push --> wwwsub --> wwwcf --> wwwout
push --> apisub --> apicf --> apioutRollback#
Via Cloudflare dashboard#
Pages project → Deployments → find the previous successful deployment → “Rollback to this deploy.”
Via Git#
git revert HEAD
git pushNew deployment with the reverted changes.
Monitoring#
Build logs live in the Cloudflare dashboard: Pages → Project → Deployments → (click a deployment) for full logs.
Content pipeline#
flowchart LR
author["Author<br/>(write)"]
review["Review<br/>(PR)"]
validate["Validate<br/>(CI)"]
publish["Publish<br/>(deploy)"]
author --> review --> validate --> publishAuthoring#
1. Clone repository#
git clone [email protected]:wheelofheaven/data-content.git
cd data-content2. Create / edit content#
Example wiki entry:
cat > wiki/new-term.md << 'EOF'
+++
title = "New Term"
description = "Brief description of the term"
template = "wiki-page.html"
[extra]
claim_type = "direct"
category = "Category Name"
+++
Main content goes here.
## Section heading
More content with [internal links](/wiki/related-term/).
EOF3. Validate locally#
python scripts/validate.pyChecks: frontmatter fields, internal links, description length, title length.
4. Commit and push#
git add wiki/new-term.md
git commit -m "Add wiki entry: New Term"
git pushReview process#
- Create a feature branch
- Make changes
- Open a PR against
main - CI validation runs (frontmatter, links, translation coverage report)
- Review and merge
Translation workflow#
1. Identify missing translations#
python scripts/i18n_dashboard.py2. Create translation#
Mirror the English file structure:
mkdir -p de/wiki
cp wiki/elohim.md de/wiki/elohim.md
# edit with German translation3. Translation requirements#
- Translated
title - Translated
description - Translated body content
- Template is inherited from the English source
4. Use the glossary#
Check i18n/glossary.json for consistent term translations.
Publishing#
Automatic deployment#
Push to
mainindata-contentUpdate submodule in www:
cd www.wheelofheaven.io git submodule update --remote content git add content git commit -m "Update content" git pushCloudflare Pages deploys automatically.
Manual rebuild#
Cloudflare dashboard → Pages project → Deployments → “Retry deployment.”
Content-type checklists#
New wiki entry#
- Create
wiki/term-name.md - Required frontmatter (
title,description,template,claim_type) -
[extra]fields (category,see_also) - Body content with internal links and references
- Run validation
- Commit + push
- Bump www submodule
New timeline entry#
- Create
timeline/age-name.md - Frontmatter with
start_year,end_year,zodiac_sign - Include figure shortcodes for images
- Run validation
- Commit + push
New translation#
- Create matching file in
{lang}/{section}/ - Translate
title+description - Translate body
- Use glossary terms
- Run validation
Quality checklist#
- Title under 60 characters
- Description 150–160 characters
- Internal links working
- Images have alt text
- Six-source minimum (where applicable)
-
claim_typeset - Spelling / grammar checked
Image pipeline#
flowchart LR
src["Source images<br/><i>raw/</i>"]
proc["Process<br/>(Python)<br/><i>AVIF / WebP / JPG</i>"]
dep["Deploy<br/>(upload)<br/><i>assets.wheelofheaven.world</i>"]
cdn["CDN<br/>(edge)<br/><i>global cache</i>"]
src --> proc --> dep --> cdnRepository structure#
data-images/
├── sources/ # high-res originals (archive)
├── raw/ # images to process
├── processed/ # optimized output
│ ├── wiki/
│ ├── timeline/
│ └── resources/
├── scripts/
│ ├── process_images.py # optimization
│ └── deploy_to_cdn.py # upload
└── manifest.yaml # processing configProcessing script#
python scripts/process_images.py --input raw/ --output processed/Features:
- Format conversion (AVIF, WebP, JPG)
- Thumbnails at multiple sizes
- Quality / size balance
- Optional film-grain filter
Configuration in manifest.yaml:
processing:
quality:
avif: 75
webp: 80
jpg: 85
sizes:
full: 1920
large: 1280
medium: 800
thumb: 400
grain:
enabled: false
intensity: 0.05Output formats#
| Format | Use case | Compression |
|---|---|---|
| AVIF | Modern browsers | Best (~50% smaller) |
| WebP | Wide support | Good (~30% smaller) |
| JPG | Fallback | Baseline |
Deployment script#
python scripts/deploy_to_cdn.py --source processed/Scans the processed directory, uploads to assets.wheelofheaven.world, organizes by category, sets cache headers.
Using images in content#
{{ figure(src="wiki/elohim-creation", caption="The Elohim creating life") }}The shortcode prepends the CDN URL from config, generates <picture> with format sources, and handles lazy loading.
Workflow#
- Source image: add to
raw/ - Process: run the processing script
- Verify: check output in
processed/ - Deploy: run the deployment script
- Use: reference in content with the
figureshortcode
Naming convention#
{category}-{descriptive-name}.{ext}Examples:
wiki-elohim-symbol.jpg
timeline-age-of-aquarius-constellation.jpg
resources-book-cover-chariots.jpgImage guidelines#
- Resolution: minimum 800 px wide; recommended 1920 px; maximum 4000 px (will be resized)
- File size: processed output is optimized automatically; target under ~200 KB for full-size AVIF
- Content: clear, high-quality, relevant, properly licensed
Troubleshooting#
Build fails: “zola not found”#
Ensure the build command downloads the Zola binary.
Build fails: “submodule not found”#
- Check
.gitmodulesuses HTTPS URLs (required for CF Pages auth) - Verify the submodule is pushed to its remote
Build fails: “template error”#
- Check Tera syntax in templates
- Verify shortcodes exist
- Run
zola checklocally
Slow builds#
Zola is fast; slowness usually comes from large image processing or many external fetches. Consider pre-processing heavy assets.
“Image not found” on site#
- Check
cdn_urlinconfig.toml - Verify the image was actually deployed
- Check filename spelling (case-sensitive)