Table of contents:
The typical workflow for contributing to training content is as follows:
- Make a fork of the GitHub repository to your own account
- Develop locally (see below) and make your changes
- Commit and push to your forked repository
- Open a pull-request against the main repo, which can be reviewed and merged
The training website is built using Material for MkDocs. It's a static site generator designed for documentation websites which is fast and lightweight and comes with a lot of nice features. We use the open-source version of the tool (not any of the "insiders" features, currently).
To make changes, you should run the website locally so that you can preview changes.
See the mkdocs material docs for full installation instructions. A short version for this training site is below.
If you are used to using Docker and don't want to mess around with Python, you can run the following command to preview the site:
docker run --rm -it -p 8000:8000 -v ${PWD}:/docs \
-w /docs/docs/en \
ghcr.io/nextflow-io/training-mkdocs:latestThis uses a custom image with all required mkdocs plugins. You should get some output that looks like this:
INFO - Documentation built in 27.56 seconds
INFO - [21:52:17] Watching paths for changes: 'docs', 'mkdocs.yml'
INFO - [21:52:17] Serving on http://0.0.0.0:8000/Note: The working directory is
docs/enso paths are relative to that.
Visit http://0.0.0.0:8000/ in your web browser to view the site. Pages will automatically refresh when you save changes in your editor.
If you have Python installed, we recommend using uv to manage dependencies. Install uv, then run:
uv run _scripts/docs.py liveThis will automatically install all dependencies and start a local preview server. The log output will show a URL, probably http://127.0.0.1:8008/ - open this in your browser to view the site. Pages will automatically refresh when you save changes in your editor.
To build all languages:
uv run _scripts/docs.py build-allTraining materials are available in multiple languages.
Warning
All translations are generated and maintained by AI. Do not submit manual translations - they will be overwritten by automated updates.
If you find a translation error:
- Check if it's a glossary issue (wrong term used consistently)
- Open an issue describing: language, file, current text, expected text
- A maintainer will update the translation prompt and regenerate
To improve translations, update the LLM prompts - not the translated files directly:
- Language-specific rules: Edit
docs/<lang>/llm-prompt.md - General formatting rules: Edit
_scripts/general-llm-prompt.md
Then use the GitHub Actions workflow to regenerate:
- Go to Actions → Translate → Run workflow
- Select the language and
synccommand - The workflow will regenerate the translation with your prompt changes
Translation runs are triggered manually by maintainers, typically ahead of a new release. Resulting translation PRs should be merged promptly to avoid them stacking up or becoming outdated.
Manual translations don't scale and aren't maintainable:
- They get overwritten when English content changes
- There's no way to track intentional vs accidental differences
- The same errors reappear in new content
- Quality varies between contributors
By using AI with carefully tuned prompts, we ensure:
- Consistent terminology across all pages
- Automatic updates when English changes
- Reproducible, auditable translations
- Easy fixes via prompt improvements
For detailed information, see TRANSLATING.md.
If there is an announcement banner, you can enable and customise it using the following config in docs/en/mkdocs.yml:
extra:
# Announcement banner for upcoming training
announcement:
active: false
date_text: March 5-6, 2024
register_url: https://nf-co.re/events/2024/training-foundational-marchIf you need more customisation, edit docs/en/overrides/main.html
Graphics should be drawn using Excalidraw.
Please use the VSCode extension and give files a .excalidraw.svg filename suffix.
Files will continue to be editable by others using this method.
Excalidraw SVGs should be embedded as follows:
<figure class="excalidraw">
--8<-- "docs/en/docs/hello_nextflow/img/hello-pipeline-channel.excalidraw.svg"
</figure>Note: The file path is from the root of the repo, not the markdown file!
We inline the SVG into the content like this to make remotely loaded fonts work, as well as dark-mode compatibility.
All training content must be written as markdown.
Please make sure that you have Prettier installed and working locally: https://prettier.io/ (ideally via the VSCode plugin or similar, formatting on save).
There is a GitHub action that checks pull-requests for valid formatting.
For paragraphs containing multiple sentences, please start each sentence on a new line. Doing that produces cleaner diffs for future code reviews. The sentences will still be rendered as a continuous paragraph. For an example, have a look at the code for this paragraph.
This repository uses nextflow lint to check Nextflow scripts for errors and warnings.
The linter runs automatically in CI and posts results as a PR comment.
To run locally:
nextflow lint .This repository includes a Python tool to validate markdown heading numbering consistency across training materials.
The check_headings.py script ensures:
- Sequential numbering at each level (1., 1.1., 1.2., etc.)
- Trailing periods after heading numbers
- Heading levels match numbering depth (## for 1., ### for 1.1.)
The easiest way to run it is with uv, which handles dependencies for you automatically:
# Check files for issues
uv run .github/check_headings.py docs/**/*.md# Auto-fix detected issues
uv run .github/check_headings.py --fix docs/**/*.mdOtherwise, run uv run .github/check_headings.py which handles dependencies automatically.
The script runs automatically in CI on markdown file changes via GitHub Actions, and will cause a CI failure if any incorrect headings are found.
We use admonitions extensively to make certain pieces of content stand out. Please see the official docs for an explanation.
- Note that we have two custom admonitions:
exerciseandresult(aliassolution). !!!does a regular admonition,???makes it collapsed (click to expand).- Indentation is important! Make sure you check the rendered site, as it's easy to make a mistake.
Course and module index pages can use a special template system that generates Material for MkDocs grid cards from structured frontmatter data. This keeps the index pages consistent and makes them easier to maintain.
To use the template, add page_type: index_page to your frontmatter and include the <!-- additional_information --> marker in your content.
---
title: Course Title
hide:
- toc
page_type: index_page
index_type: course
additional_information:
technical_requirements: true
learning_objectives:
- First objective
- Second objective
audience_prerequisites:
- "**Audience:** Description of target audience"
- "**Skills:** Required skills"
videos_playlist: https://www.youtube.com/playlist?list=...
---
# Course Title
Summary paragraph describing the course.
This content appears in the "Course summary" card.
<!-- additional_information -->
## Rest of page
Content after the marker appears below the grid cards.| Field | Type | Required | Description |
|---|---|---|---|
page_type |
"index_page" |
Yes | Enables the index page template |
index_type |
string | No | Badge label displayed in top-right (e.g., "course", "module") |
additional_information |
object | No | Container for the collapsible admonitions |
All fields within additional_information are optional:
| Field | Type | Description |
|---|---|---|
technical_requirements |
true or string |
If true, uses default text about GitHub/local installation. If a string, uses that custom text. |
learning_objectives |
list of strings | Rendered as a bulleted list. Must be a list, not true. |
audience_prerequisites |
list of strings | Rendered as a bulleted list. Supports markdown formatting. Must be a list, not true. |
videos_playlist |
URL string | Uses default video description text plus a link to the playlist. |
videos |
string | Custom video description text (no link). Mutually exclusive with videos_playlist. |
When technical_requirements: true is set:
You will need a GitHub account OR a local installation of Nextflow. See Environment options for more details.
When videos_playlist is set, the following text precedes the link:
Videos are available for each chapter, featuring an instructor working through the exercises. The video for each part of the course is embedded at the top of the corresponding page.
- The page must have an H1 heading (
# Title) - The page must include the
<!-- additional_information -->marker learning_objectivesandaudience_prerequisitesmust be lists (nottrue)videosandvideos_playlistare mutually exclusive
The build will fail with a descriptive error if these requirements are not met.
There are a couple of known limitations that I haven't figured out how to get around yet
Mkdocs Material uses // code comments to anchor the annotations. That's great, until you want an annotation in the middle of a large multi-line string (say, like a script block).
There will hopefully be a way to add annotations at arbitrary line numbers in the future. See the footnote on the mkdocs material docs:
Code annotations [are] currently not compatible with [..] languages that do not have comments in their grammar. However, we're actively working on supporting alternate ways of defining code annotations, allowing to always place code annotations at the end of lines.
See this GitHub discussions thread for updates.
Note also that annotations cannot be added to script blocks generated by importing an external file.
Code blocks can have lines highlighted with hl_lines in the code block header. However, specific words / characters can not have additional focus (as in a GitHub diff, for example).
Please use TODO comments when you see something that needs coming back to.
I recommend the Todo Tree VSCode extension to find these comments easily.
A list of key ones also included here:
- Remove plugin install from Phil's GitHub fork in
.github/mkdocs.Dockerfileand_scripts/docs.pywhen this PR is merged
The preview_release.py script serves the training docs locally at https://training.nextflow.io/ with the current branch appearing as a specified version release.
This is useful for recording videos or previewing how a release will look before it's published.
- Fetches existing released versions from the
gh-pagesbranch - Builds the current branch's docs using Docker
- Updates
versions.jsonto show your version as "latest" - Generates trusted TLS certificates using mkcert
- Adds a temporary entry to
/etc/hoststo redirect the domain locally - Serves the site using Caddy on port 443
- Cleans up the hosts entry when you stop the server (Ctrl+C)
Install the mkcert root CA (one-time, as your regular user):
mkcert -installThis adds a local certificate authority to your system keychain so browsers trust the generated certificates. Restart your browser after running this command.
The easiest way to run the script is with uv, which handles dependencies automatically:
# Serve current branch as version 3.0
sudo uv run ./preview_release.py --version 3.0
# Check current status
uv run ./preview_release.py status
# Clean up work directory on exit (default keeps it for faster restarts)
sudo uv run ./preview_release.py --version 3.0 --cleanThe script requires sudo because it needs to:
- Modify
/etc/hoststo redirecttraining.nextflow.ioto localhost - Bind to port 443 for HTTPS
The script caches:
- Downloaded gh-pages content (existing released versions)
- Built docs for your version
- Generated TLS certificates
A hash of source files (docs/en/) is computed to detect changes.
If you modify source files, the docs will be rebuilt automatically on the next run.
Use --clean to delete the work directory on exit, or manually remove .preview-release/ to force a fresh build.