Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Documentation

on:
pull_request:
push:
branches: [main, master]
schedule:
- cron: '23 6 * * *'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: docs-${{ github.ref }}
cancel-in-progress: true

jobs:
website:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
MPLBACKEND: Agg
POSTGKYL_REQUIRE_GKEYLL: '1'
VTK_DEFAULT_OPENGL_WINDOW: vtkEGLRenderWindow
LIBGL_ALWAYS_SOFTWARE: '1'
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
cache-dependency-path: source/requirements.txt
- name: Install host documentation tools
run: |
sudo apt-get update
sudo apt-get install -y libegl1 libgl1-mesa-dri
python -m pip install -r source/requirements.txt
- name: Verify main tracking and local preview behavior
run: python -m unittest discover -s tests -v
- name: Fetch Postgkyl main and prepare its documentation
run: python scripts/prepare_postgkyl.py
- name: Test upstream documentation and examples
working-directory: external/postgkyl
run: |
python -m pip install --no-build-isolation -e '.[docs,test]'
python -m pytest tests/test_documentation.py tests/test_examples.py tests/test_docs_build.py
- name: Build the complete website
run: python -m sphinx -W --keep-going -b html source build/html
- uses: actions/upload-artifact@v4
with:
name: gkeyll-website
path: build/html
if-no-files-found: error
- name: Refresh hosted docs after the daily upstream check
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
env:
READTHEDOCS_TOKEN: ${{ secrets.READTHEDOCS_TOKEN }}
run: |
python - <<'PY'
import os
import urllib.request
token = os.environ.get("READTHEDOCS_TOKEN")
if not token:
print("READTHEDOCS_TOKEN is unset; preview built, hosted refresh skipped.")
else:
request = urllib.request.Request(
"https://app.readthedocs.org/api/v3/projects/gkeyll/versions/latest/builds/",
method="POST", headers={"Authorization": f"Token {token}"})
with urllib.request.urlopen(request, timeout=30) as response:
if response.status != 202:
raise RuntimeError(f"Build trigger returned {response.status}")
print("Requested a Read the Docs rebuild from Postgkyl main.")
PY
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
*~
build/*
*.bp
*.bp
/external/
/source/postgkyl/
__pycache__/
/.venv/
9 changes: 8 additions & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,19 @@ version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
apt_packages:
- libegl1
- libgl1-mesa-dri
tools:
python: "3.8"
python: "3.12"
jobs:
post_install:
- VTK_DEFAULT_OPENGL_WINDOW=vtkEGLRenderWindow LIBGL_ALWAYS_SOFTWARE=1 python scripts/prepare_postgkyl.py

# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: source/conf.py
fail_on_warning: true

# We recommend specifying your dependencies to enable reproducible builds:
# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
Expand Down
14 changes: 11 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -msphinx
PYTHON ?= python
SPHINXBUILD = $(PYTHON) -msphinx
POSTGKYL_PREPARE_ARGS ?=
SPHINXPROJ = gkyl
SOURCEDIR = source
BUILDDIR = build
Expand All @@ -12,9 +14,15 @@ BUILDDIR = build
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile
.PHONY: help Makefile postgkyl html

postgkyl:
$(PYTHON) scripts/prepare_postgkyl.py $(POSTGKYL_PREPARE_ARGS)

html: postgkyl
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
74 changes: 51 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,62 @@
This is the documentation and tutorials for the
[gkyl](https://github.com/ammarhakim/gkyl) project
# Gkeyll documentation

This repository hosts the Gkeyll website, including documentation generated
from [Postgkyl main](https://github.com/gkeyllorg/postgkyl/tree/main).

In order to build the docs locally, one needs
[sphinx](https://www.sphinx-doc.org/en/master/) and the
[furo](https://github.com/pradyunsg/furo) theme.
Use **Python 3.12**, Git, Make, and a C compiler. From this repository:

We recommend creating a virtual environment[^1] and installing the dependencies
through [conda](https://conda.io/miniconda.html):
```bash
conda env create -f environment.yml
python -m venv .venv
source .venv/bin/activate
python -m pip install -r source/requirements.txt
make html SPHINXOPTS="-W --keep-going"
```

The environment is then activated with
```bash
conda activate gkyl-doc
```
Open `build/html/index.html`. `make html` fetches Postgkyl **main** into
`external/postgkyl`, installs it with its documentation dependencies and native
Gkeyll bridge, executes its examples, and stages its documentation in
`source/postgkyl/`. The first build needs network access and can take several
minutes. Both directories are ignored build inputs/outputs; edit Postgkyl
content in its own repository. A later build fetches main again. A dirty
managed checkout is refused rather than overwritten.

For a local Postgkyl change before it reaches main:

However, one can also attempt to install the dependencies directly to current
`conda` environment using:
```bash
conda install --file source/requirements.txt
make html POSTGKYL_PREPARE_ARGS="--checkout /path/to/postgkyl" SPHINXOPTS="-W --keep-going"
```

With the dependencies installed, the documentation is simply built with `make
html` from the `gkyl-doc` directory. The desired HTML file is than in the
`build` directory.
Add `--no-install` only when that exact checkout is already installed with
`pip install --no-build-isolation -e '.[docs]'` in the active environment.
The generator checks the imported package location and requires its native
bridge. Its documentation, examples, and test data all come from the same
checkout. The resulting pages record the source commit for traceability;
that record does not pin subsequent builds.

Read the Docs runs the same preparation script after installing host
requirements. `.readthedocs.yaml` selects Python 3.12 and treats Sphinx warnings
as errors. Merge the Postgkyl documentation implementation into its main branch
before enabling this host change, since the host requires its build script.

GitHub Actions tests pull requests, pushes, and a daily checkout of Postgkyl
main. It runs Postgkyl's documentation and example tests, validates the
standalone build and downloaded examples, then builds the complete website
with warnings as errors and uploads an HTML preview artifact.

For automatic hosted refreshes after successful daily checks, configure the
GitHub Actions repository secret `READTHEDOCS_TOKEN` with a token authorized to
trigger builds of the `gkeyll` Read the Docs project. Without it the scheduled
checks and preview artifacts still run; hosting updates on normal Read the
Docs builds. The manual Actions workflow also requests a hosted refresh when
the secret is configured. Tokens are never used on pull requests.

The gallery now runs both the Python scripts and their paired CLI pipelines,
checks raster pixels/GIF timings or Plotly trace data/layout, and publishes
both results. The Python API is generated as one page per callable/property.
Interactive Plotly HTML is copied beside the referring pages by Postgkyl's
shared Sphinx extension.

[^1]: Note that `conda` needs to be initialized before environments can be used.
This is the last step of the `conda` installation, but the current default
behavior is _not_ to perform the initialization. It can be done afterwards
using `conda init [shell name]`, e.g., `conda init fish` with the fantastic
[fish](https://fishshell.com/) shell.
PyVista screenshots require OpenGL. CI and Read the Docs install `libegl1`
and `libgl1-mesa-dri` and select `VTK_DEFAULT_OPENGL_WINDOW=vtkEGLRenderWindow`
with `LIBGL_ALWAYS_SOFTWARE=1` for headless Linux. Local machines with a working
OpenGL display can use their usual renderer.
4 changes: 2 additions & 2 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: gkyl-doc
channels:
- defaults
dependencies:
- python>=3.11
- python=3.12
- pip
- sphinx>=5.0.2
- furo>=2024.8.6
- pip:
# add here only pip-packages that are not available in conda/conda-forge! E.g.:
- sphinx-immaterial>=0.11.8
- sphinx-immaterial>=0.11.8
63 changes: 63 additions & 0 deletions scripts/prepare_postgkyl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Fetch Postgkyl main, install it, and generate this website's Postgkyl section.

An explicit --checkout uses a local working tree for cross-repository previews.
Normal builds always fetch main; they never reuse a pinned source revision.
"""

from __future__ import annotations

import argparse
from pathlib import Path
import subprocess
import sys


def prepare(site: Path, checkout: Path | None, no_install: bool) -> None:
if checkout is None:
checkout = site / "external/postgkyl"
if not checkout.exists():
checkout.parent.mkdir(parents=True, exist_ok=True)
subprocess.run([
"git", "clone", "--depth", "1", "--branch", "main",
"https://github.com/gkeyllorg/postgkyl.git", str(checkout),
], check=True)
else:
dirty = subprocess.check_output([
"git", "-C", str(checkout), "status", "--porcelain",
], text=True)
if dirty.strip():
raise RuntimeError(f"Refusing to change dirty checkout: {checkout}")
subprocess.run([
"git", "-C", str(checkout), "fetch", "--depth", "1",
"origin", "main",
], check=True)
subprocess.run([
"git", "-C", str(checkout), "checkout", "--detach", "FETCH_HEAD",
], check=True)
checkout = checkout.resolve()
generator = checkout / "scripts/build_docs.py"
if not generator.is_file():
raise RuntimeError(
"Postgkyl main must contain scripts/build_docs.py. Merge the "
"Postgkyl documentation implementation before deploying this host.")
if not no_install:
subprocess.run([
sys.executable, "-m", "pip", "install", "numpy>=2.2.6",
"setuptools", "wheel",
], check=True)
subprocess.run([
sys.executable, "-m", "pip", "install", "--no-build-isolation",
"-e", f"{checkout}[docs]",
], check=True)
subprocess.run([
sys.executable, str(generator), "--output", str(site / "source/postgkyl"),
], cwd=checkout, check=True)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--checkout", type=Path, help="Use a local Postgkyl checkout")
parser.add_argument("--no-install", action="store_true",
help="Use dependencies already installed in this environment")
args = parser.parse_args()
prepare(Path(__file__).resolve().parents[1], args.checkout, args.no_install)
Loading
Loading