Skip to content

Contributing and deployment

Local setup

git clone https://github.com/nocturnaljojo/energy-operations-research.git
cd energy-operations-research
uv sync                       # Python 3.12 env with dev + docs groups
uv run pytest                 # mathematical tests
uv run mkdocs serve           # live preview at http://127.0.0.1:8000
Task Command
Lint / format uv run ruff check . && uv run ruff format .
Types uv run mypy
Regenerate figures uv run python scripts/build_figures.py
Regenerate notebook 1 uv run python scripts/make_notebook_01.py
Execute notebooks uv run python scripts/run_notebooks.py
Render an animation uv run --group anim manim -qm animations/foundations/feasible_region.py SharedConnectionLP

Manim needs Cairo, Pango and FFmpeg system libraries (apt install libcairo2-dev libpango1.0-dev ffmpeg pkg-config on Debian/Ubuntu). No LaTeX is required: scenes use Pango text.

Hosting: Cloudflare Pages

The book is a static site built by MkDocs and served from Cloudflare Pages. The docs GitHub Actions workflow builds the site and uploads it with Wrangler:

Event Result
Push to main Production deploy at https://energy-operations-research.pages.dev
Pull request Preview deploy at https://<branch>.energy-operations-research.pages.dev
Secrets missing (e.g. a fork) Site is built and link-checked, not deployed

One-time setup

You do this once per repository. The token keeps working until you revoke it; redo the steps only if you rotate the token, move the book to another repository, or add a deploy target that needs other permissions (for example R2 storage for the data layers).

1 · Create an API token. In the Cloudflare dashboard, click the person icon (top right) → Profile → API Tokens → Create Token. Use Create Custom Token → Get started, not one of the templates: none of them grants Pages access alone.

Create API Token page: Create Custom Token above the list of templates

2 · Give it one permission. Fill in the form:

Field Value
Token name energy-or-pages-deploy (any name)
Permissions Account → Cloudflare Pages → Edit (the third box is easy to miss)
Account Resources Include → your account
Client IP Address Filtering leave empty (GitHub's runners have no fixed IP)
TTL leave empty, or the deploy stops when the token expires

Custom token form with Account, Cloudflare Pages, Edit

Click Continue to summary → Create Token and copy the token at once: Cloudflare shows it only once. Paste it only into GitHub (step 4), never into chat, code or a commit.

3 · Find your Account ID. It is the 32-character hex string in the dashboard address bar (dash.cloudflare.com/<account-id>/home), also under Account home → ⋯ → Copy account ID. It is an identifier, not a secret, but keep it in a secret anyway so it stays out of workflow logs.

4 · Add both to GitHub. In the repository, open Settings → Secrets and variables → Actions → New repository secret and add:

  • CLOUDFLARE_API_TOKEN
  • CLOUDFLARE_ACCOUNT_ID

5 · Optional settings. Add the repository variable CLOUDFLARE_PAGES_PROJECT to use a project name other than energy-operations-research. To use a custom domain, attach it under Workers & Pages → your project → Custom domains, then update site_url in mkdocs.yml.

6 · Deploy. Push to main (or open a pull request for a preview). The workflow creates the Pages project on its first run and deploys. Check the run under the repository's Actions tab: a missing secret shows as the notice CLOUDFLARE_API_TOKEN not set; skipping deploy.

Screenshots taken October 2026; Cloudflare redesigns its dashboard often, so trust the field names above over the pictures.

docs/_headers sets security and cache headers; Cloudflare Pages applies it automatically.

Why Cloudflare

The book is static, so any CDN would do. Cloudflare also has services the later production chapters need: R2 object storage for the raw / bronze / silver / gold data layers, Workers and Cron Triggers for lightweight scheduled jobs, and Containers for running Python optimisation services. Those are introduced only when a chapter needs them.

Chapter checklist

A chapter is done only when it is:

  • Readable: follows the 15-step template, intuition before mathematics
  • Executable: has a Colab notebook that runs top to bottom in CI
  • Visual: at least one figure generated from solver output; an animation where it explains something
  • Tested: hand-derivable optima and physical limits asserted in tests/
  • Reproducible: seeded randomness, documented data provenance, synthetic data labelled
  • Defensible: units explicit; approximations of AEMO processes stated as such
  • Connected: links forward and back to the rest of the book