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
11 changes: 11 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Closes #

- [ ] The pull request is for `main`.

### Main changes

-

### Pages that changed

-
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: pip
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
52 changes: 52 additions & 0 deletions .github/workflows/docs-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Docs build

# Builds the site with warnings as errors and checks it against the docs-site
# standard. The weekly run finds a change in a submodule, a dependency or an
# external page that nobody pushed.

on:
push:
branches: [main]
pull_request:
schedule:
- cron: "0 6 * * 1"
workflow_dispatch:

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v5
with:
submodules: recursive

- uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Install
run: python -m pip install -r requirements.txt

- name: Check the site against the standard
run: python scripts/check_site.py .

# Not yet blocking: 121 distinct names on the guide pages resolve to neither
# toolbox, and scripts/known_names.txt is empty. Remove continue-on-error
# after the names are checked and the real ones allow-listed.
- name: Check names on the guide pages
continue-on-error: true
run: python scripts/check_names.py

- name: Build with warnings as errors
run: sphinx-build -W --keep-going -b html docs docs/_build/html

# Not yet blocking: the state of the external links of this site was not
# known when the check was added. Remove continue-on-error after a run
# that is clean, or list the links that reject crawlers in linkcheck_ignore.
- name: Check external links
continue-on-error: true
run: sphinx-build -b linkcheck docs docs/_build/linkcheck
48 changes: 29 additions & 19 deletions .github/workflows/update-submodules.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
name: Update submodules

# Moves each submodule to the tip of the branch set in .gitmodules and opens a
# pull request. The pull request build checks that the new source still builds
# and that the documented examples still hold; nothing is pushed to main.

on:
schedule:
- cron: '0 4 * * *' # daily at 04:00 UTC
workflow_dispatch: {} # adds a "Run workflow" button for manual runs
- cron: "0 4 * * *"
workflow_dispatch: {}

permissions:
contents: write # needed to push the pointer commit
contents: write
pull-requests: write

concurrency: # avoid overlapping runs racing on a commit
concurrency:
group: update-submodules
cancel-in-progress: false

jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Checkout gecko-docs
uses: actions/checkout@v5
- uses: actions/checkout@v5
with:
submodules: true
fetch-depth: 1

- name: Bump submodules to their tracked branches
- name: Move submodules to their tracked branches
run: |
# Fetch each submodule's tracked branch (from .gitmodules) and check
# it out at its tip. Done explicitly because the submodules are
# `shallow = true`, so only the pinned commit is present and
# `git submodule update --remote` can't resolve the remote branch ref.
# (GECKO -> develop4, geckopy -> develop)
# The submodules are `shallow = true`: only the pinned commit is
# present, so `git submodule update --remote` cannot resolve the
# remote branch. Each branch tip is fetched explicitly.
git submodule sync --recursive
git submodule foreach '
branch=$(git config -f "$toplevel/.gitmodules" "submodule.$name.branch" || echo main)
Expand All @@ -37,14 +39,22 @@ jobs:
git checkout -B "$branch" FETCH_HEAD
'

- name: Commit & push if anything changed
- name: Open or update the pull request
env:
GH_TOKEN: ${{ github.token }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if git diff --quiet; then
echo "No submodule updates."
else
git add -A
git commit -m "chore: update submodules to latest tracked branches"
git push
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git switch -C chore/update-submodules
git add -A
git commit -m "chore: update submodules to latest tracked branches"
git push --force-with-lease origin chore/update-submodules
if [ -z "$(gh pr list --head chore/update-submodules --state open --json number --jq '.[].number')" ]; then
gh pr create --base main --head chore/update-submodules \
--title "chore: update submodules to latest tracked branches" \
--body "Moves each submodule to the tip of the branch set in .gitmodules."
fi
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ venv.bak/
# mkdocs documentation
/site

# Sphinx build: generated at build time by scripts/gen_api_pages_sphinx.py,
# Sphinx build: generated at build time by scripts/gen_api_pages.py,
# never checked in.
docs/api/matlab/*.md
docs/api/python/*.md
Expand Down
3 changes: 2 additions & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ build:

sphinx:
configuration: docs/conf.py
fail_on_warning: true

python:
install:
- requirements: requirements-sphinx.txt
- requirements: requirements.txt
54 changes: 20 additions & 34 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,25 @@
# Contributing to gecko-docs

## Documentation prose style
The standard for this site is the `docs-site` plugin of [CompMetEng/skills](https://github.com/CompMetEng/skills), and the intent of this site is in [DESIGN.md](DESIGN.md). Prose follows the `writing` skill of the `craft` plugin. The rules most often broken:

Many readers are not native English speakers. Prose must be direct and
literal, not just factually correct.
1. No em dashes anywhere: not in prose, headings, table cells or generated strings. Use the punctuation the sentence calls for: a comma where the clause continues, a semicolon where it is independent, a colon where what follows defines or lists what precedes it, parentheses around an aside.
2. Check every sentence while writing it:
- Can the subject perform the verb? A file, function or table can return, raise, contain or default to. It cannot want, know or prefer, and it does nothing quietly or conveniently.
- Does the sentence state a behaviour and its condition, or does it tell the reader what is worth doing? State the fact and leave the judgement to the reader.
- Is the sentence about the software or about the document? Remove "this page is about" and "as mentioned above".
- Does a word minimise something? Remove "just", "simply", "merely", "only", "of course" when the claim does not change.
- Would a non-native English reader parse it on the first read? No idioms, figures of speech or headlines that drop the subject or verb.
3. Established technical shorthand (a domain term, a standard abbreviation) stays.
4. No mention of AI tools or AI authorship in the repository: not in commits, pull requests, code comments or pages. Branch names do not contain a tool name.

1. No em dashes, anywhere: not in prose, headings, table cells, or generated
strings. Use the punctuation the sentence actually calls for: a comma
where the clause continues, a semicolon where it is independent, a colon
where what follows defines or enumerates what precedes it, parentheses
for a matched pair around an aside.
2. Apply these five checks to every sentence while writing it, not as a
later sweep:
- Can the subject perform the verb? A file, function, or table can
return, raise, contain, default to. It cannot want, know, prefer, or
do anything "quietly," "conveniently," or "simply." A manner adverb on
an inanimate subject is the most common violation.
- Does the sentence state a behavior and its condition, or does it tell
the reader what's "worth" doing? No "worth using," "the usual way,"
"best practice." State the fact; leave the judgment to the reader.
- Is the sentence about the software, or about the document? Cut "this
page is about," "as mentioned above," "the point of this example."
- Does a word minimize something? "just," "simply," "merely," "only,"
"of course." If removing it doesn't change the claim, remove it.
- Would a non-native English reader parse this correctly on first read?
No idioms, figures of speech, or headline-style fragments that drop
the subject or verb for effect (example of what to avoid: "same
verdicts, very different cost" as a heading). Rewrite as a complete,
literal sentence. This is not the same as dumbing content down: keep
the technical precision, drop only the cleverness.
3. No AI/Claude/Anthropic attribution anywhere in the repository: not in
commit messages, PR text, code comments, or the docs themselves.
## Building and checking

One exception worth stating explicitly: established, consistently-used
technical shorthand (a real domain term, a standard abbreviation) is not
the same as a one-off clever phrase, and doesn't need to be purged just
because it isn't perfectly literal. Reserve the rewrite effort for genuine
idiom and headline compression, especially on pages newcomers read first.
```bash
git submodule update --init --recursive
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python scripts/check_site.py .
sphinx-build -W -b html docs docs/_build/html
```

Pages are written as described in the `docs-page` skill. A guide example is run before it is committed, and the output under it is the output of that run.
68 changes: 68 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# GECKO documentation: design and content guide

This document is the source of intent. It describes how the site should be designed and what it should contain, not the current state of `main`. When the site and this document disagree, the site is brought in line, or the decision is recorded here first. New decisions go in the decisions log; resolved items leave "Open questions".

The site follows the `docs-site` standard of [CompMetEng/skills](https://github.com/CompMetEng/skills). Differences from the standard are listed in section 6.

Before any work in this repository, fetch all remotes of the repository and of every submodule. The submodules are updated often and automatically, so a local checkout becomes stale quickly:

```bash
git fetch --all --prune
git checkout main && git pull --ff-only
git submodule sync --recursive
git submodule update --init --recursive
```

## 1. Purpose

One documentation site for **GECKO**, the MATLAB toolbox, and **geckopy**, its Python port, for reconstructing, simulating and analyzing enzyme-constrained genome-scale metabolic models (ecModels). A user learns a task once and applies it in either language. Readers are researchers who build ecModels from a genome-scale model, and many of them are not native English speakers.

## 2. Principles

- **Both languages together.** MATLAB and Python are presented side by side: paired API entries and linked code tabs.
- **Single source of truth.** The API reference is generated from the function help in the toolbox sources, which are git submodules tracking the branches in `.gitmodules`.
- **Low maintenance.** What can be generated is generated at build time. Prose pages are short and have one job.
- **Curated content.** A small set of correct pages is preferred to a large set of stale ones.
- **Literal prose.** The writing rules of `CONTRIBUTING.md` apply to every page, because many readers are not native English speakers.

## 3. Technical foundation

- Sphinx, MyST Markdown, PyData theme, `sphinx-design`, `sphinx-copybutton`. Read the Docs builds with warnings as errors.
- Submodules: `GECKO` (branch `develop`) and `geckopy` (branch `develop`).
- The API reference is generated at build time by `scripts/gen_api_pages.py`, called from `docs/conf.py`. The scripts in `scripts/` are copies of `docs-site/scripts` in CompMetEng/skills; what is specific to this site is in `scripts/project.yml`, `scripts/curated_pairs.yml` and `scripts/known_names.txt`. A change to a script is made in the plugin first.
- `.github/workflows/docs-build.yml` runs `scripts/check_site.py`, the build with `-W`, the name check and the link check.

## 4. Site structure

- **Home** (`docs/index.md`): one sentence on GECKO and geckopy, the install tabs for both languages, key features, quick start, links to the papers.
- **Installation** (`docs/installation/`).
- **Guide** (`docs/guide/`): *Start* (Introduction, Installation), *Guide* (ten task pages from getting started to enzyme usage and bottlenecks), *Background* (GECKO light, the YAML format, the files of the data folder, the model adapter parameters). The Introduction and the Background pages explain a concept or a format and are marked `<!-- docs-site: concept-page -->`, so they need no function table.
- **Migration**: `migrate.md`, `gecko3-to-gecko4.md` and `gecko-to-geckopy.md`.
- **API reference** (`docs/api/`): a hand-written index, then generated MATLAB pages by folder and Python pages by package, and `docs/matlab-vs-python.md`.
- **References** (`docs/references.md`): the papers to cite.

## 5. Conventions

- Code tabs are labelled MATLAB and Python, linked, with MATLAB first.
- MATLAB `camelCase` and Python `snake_case` are paired by normalised name; pairs that differ are in `scripts/curated_pairs.yml`.
- Functions that resolve to cobrapy are marked with the cobrapy badge.
- Prose follows `CONTRIBUTING.md`. No mention of AI tools.

## 6. Differences from the standard

| Difference | Reason |
| --- | --- |
| `sphinx.ext.mathjax` in `extensions` | the Introduction contains equations |

## 7. Open questions

- **Unresolved names.** `scripts/check_names.py` reports 121 distinct names on the guide pages that are neither a top-level function or class of the toolboxes nor in `scripts/known_names.txt`. Most are keyword arguments, struct fields, YAML keys and variables of the examples; four (`cap_usage`, `from_folder`, `shadow_price`, `upper_bound`) are methods or properties. Someone who knows the pages has to sort them, and allow-list the ones that are not functions, before the check can block CI.
- **Link check.** `docs-build.yml` does not fail on the link check yet; the state of the external links was not known when the check was added.
- **Guide page sections.** Most guide pages have no "What can go wrong" section and no function table; `scripts/check_site.py` reports this as a warning.

## 8. Decisions log

- 2026-10-24: the site follows the `docs-site` standard. `requirements-sphinx.txt` is `requirements.txt` and `gen_api_pages_sphinx.py` is `gen_api_pages.py`. The brand colours are `#43684B` for tabs and borders and `#2D6E3B` for links, with `#5F936A` and `#87d398` in dark mode, set as `--pst-color-primary`, `--pst-color-link` and `--site-accent`. Logos and the favicon are in `docs/_static` only, with a white logo for dark mode. The API pages use the numpydoc rendering and show signatures.
- 2026-10-24: Read the Docs and CI build with `-W`. The submodule update opens a pull request instead of pushing to `main`.
- 2026-10-24: `docs/_static/gecko-logo-white.png`, the logo for dark mode, was made from `gecko-logo.png` by making every pixel white and keeping the alpha channel, the way `gecko-icon-white.png` was made. The maintainer accepted it. The unused `gecko-icon-color.png` and `gecko-icon-white.png` of the former `docs/assets` folder were removed and are in the history.
- 2026-10-24: the language of the code tabs (MATLAB or Python) is remembered in `localStorage` by `docs/_static/language-sync.js`, so it applies to every tab on a page, to the next pages, to new browser tabs and to later visits, and install tabs follow it. sphinx-design alone keeps the choice in `sessionStorage` for one browser tab.
Loading
Loading