If “untested code is broken code” then “undocumented code is no code at all”. You may have created the most useful tool but if you have no documentation explaining how to use it, then it is almost as if you had not written any code.
Here I will show how to get started with several documentation frameworks:
Table of content
- Relevant links
- Jekyll + GitHub pages
- Mkdocs + read the docs
- Intermission: dead links and spelling
- Sphinx + read the docs
- Jupyter book + GitHub pages
Relevant links
- The awesome Write the docs. Make sure to check out their slack workspace!
Jekyll + GitHub pages
Jekyll is a static website generator based on ruby. Static websites: HTML + CSS but no database.
- Use case:
- Serve the markdown content of repo as a website.
- Advantages:
- integrate REALLY easily with github
- easy way to get started with html / css
- lots of templates to choose from for personal website, events…
- can use the liquid templating language
- Not covered:
- local set up: installing ruby + jekyll and serving the website
Setting up jekyll
This post explains how to set up jekyll if you already have conda on your computer.
- Examples:
- most of the brainhack local site events
- the website you are currently readinbg
Ingredients
- have a github repo with some markdown in it
Don’t know what to write?
Note you can mix markdown and HTML.
Recipe
-
add a
config.yml
file in the root of the repo with the theme for your website with the following content:theme: jekyll-theme-minimal
The themes supported by github pages are listed on this page. You can also preview what they will look like.
-
Got to the
Settings
tab of the repo, then to thePages
menu, then under theBranch
section choose the default branch of your repo and then save. This should trigger the build and deploy of the website. -
Browse your website at this URL: https://
your-github-username
.github.io/your-repo-name
.The github action to build the website will be triggered by any new push to the default branch.
You can always monitor the github action in the
Actions
tab of the repo.
Outcome
Reuse templates
There are a LOT of available jekyll templates:
Create your own academic site in less than 5 minutes
- Create a new repo using the template https://github.com/academicpages/academicpages.github.io but name it
YOUR-GITHUHB-USERNAME.github.io
. - Deploy using github pages.
- Start editing the
.config.yml
.
Relevant links
Mkdocs + read the docs
Mkdocs is a python package to help create documentation website from markdown files.
- Use case:
- Create some official looking documentation website from markdown.
- Advantages:
- integrate easily with github
- integrate easily with read the docs (allow easy preview on pull requests)
- easy to work with locally
- many extensions and plugins
- the use of macros makes it a very powerful framework
- Inconvenients:
- less flewibility in terms of looks
- Examples:
- BIDS specification
- neurobagel documentation
Ingredients
- have a github repo with some markdown in it
- python to serve locally
Recipe
-
Clone your repo.
-
Install mkdocs.
pip install mkdocs
-
Create minimal config.
mkdocs new .
Will create a
mkdocs.yml
in the root of the repo and adoc/index.md
which will be the landing page of the website. -
Serve locally.
mkdocs serve
You can then browse the documentation as a website at
http://127.0.0.1:8000/
Updating an markdown file, should automatically update the website.
Kill the serve with
ctrl + c
. -
Change the default theme.
Install mkdocs-material
pip install mkdocs-material
Add it to the
mkdocs.yml
that should now look like this.site_name: My Docs theme: name: material
Serve the website again and check the difference.
mkdocs serve
See the next section to see how to use Read The Docs to deploy your doc.
Serve to Read The Docs
Read the docs is a service that allows you to host your documentation.
-
Add a
requirements.txt
in the root of the repo.mkdocs mkdocs-material
-
Add a
.readthedocs.yaml
in the root of the repo.version: 2 build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation with Mkdocs mkdocs: configuration: mkdocs.yml # Dependencies required to build your docs python: install: - requirements: requirements.txt
-
Commit and push.
git add --all git commit -m 'add requirements and RTD config' git push
-
Log in to read the docs: https://readthedocs.org/accounts/login.
-
Find the repo below
Import a Repository
or go for theImport Manually
option.If the latter, fill in the
Name
,Repository URL
andDefault branch
fields and clickNext
-
Click on
Build Version
.Once the build is completed, you should be able to see the website at:
https://
REPO_NAME
.readthedocs.io/en/latest/ -
Make sure a preview of the doc is done on every pull request.
Go to the
Admin
tab, tick the box belowBuild pull requests for this project:
and clickSave
Outcome
Intermission: dead links and spelling
Quick tips to avoid dead links and spelling mistakes in your documentation.
pre-commit and codespell
You can use the pre-commit git-hook framework to automatically run codespell to check the content of your repository for spelling mistakes.
-
Create a
.pre-commit-config.yaml
in your repository:# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: # Checks for spelling errors - repo: https://github.com/codespell-project/codespell rev: v2.3.0 hooks: - id: codespell
-
Install pre-commit and install the git hooks
pip install pre-commit pre-commit install
This will make sure that codespell is run every time you commit to make you only commit “clean” content.
You may need to add a config file for codespell that looks like this:
[codespell]
skip = .git
ignore-words-list = list,of,words,separated,by,commas
builtin = clear,rare
markdown check links
If the framework you use cannot easily check for dead link links in your documentation, you can use a github action that runs markdown-link-check on your markdown files.
Create a .github/workflows/check_md_links.yml
with the following content
name: Check markdown links
on:
push:
branches: [main]
jobs:
markdown-link-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
use-quiet-mode: yes
use-verbose-mode: yes
# extra config file to ignore some links
config-file: md_link_check_config.json
# folder containing your doc
folder-path: docs
# extra files to validate
file-path: ./README.md
The configuration file would look like this.
{
"ignorePatterns": [
{
"pattern": "^https://doi.org\/"
},
],
"timeout": "20s",
"retryOn429": true
}
Sphinx + read the docs
Sphinx is a python package to help create documentation website from files written using restructured text / markdown.
- Use case:
- Create a documentation website for a python code base.
- Advantages:
- autodoc functionality renders the doc strings of python code
- possible to work with markdown files using the MyST plugin.
- integrate easily with read the docs (allow easy preview on pull requests)
- support other languages than python (e.g Matlab / Octave).
- easy to work with locally
- many extensions and plugins
- a decent selection of themes
- Inconvenients:
- the restructured text syntax is less forgiving than that of markdown
- Examples:
- pretty much the doc website of any python package
Ingredients
- have a github repo with some python code in it
- python to serve locally
Recipe
-
Install sphinx.
pip install sphinx
-
Initial set up.
mkdir docs cd docs sphinx-quickstart
Respond to the questions in the prompt.
├── docs │ ├── make.bat │ ├── Makefile │ └── source │ ├── conf.py │ ├── index.rst │ ├── _static │ └── _templates └── README.md
-
Build the doc.
make html
-
View the doc.
Open
docs/build/html/index.html
-
Use MySt to render a markdown file.
Install myst.
pip install myst-parser
Add
"myst_parser"
to the list of extensions indocs/source/conf.py
.extensions = ["myst_parser"]
Add a markdown file in the
source
directory of the doc.├── docs │ ├── make.bat │ ├── Makefile │ └── source │ ├── conf.py │ ├── index.rst │ ├── markdown_is_possible.md <--- │ ├── _static │ └── _templates └── README.md
And add it into the
docs/source/index.rst
file... toctree:: :maxdepth: 2 :caption: Contents: markdown_is_possible.md
-
Change the theme.
Install the furo theme.
pip install furo
Change html_theme in
docs/source/conf.py
.html_theme = "furo"
Rebuild the doc to check the new theme.
-
Auto-document your python code.
Add a python module
src/some_python_code.py
├── docs │ ├── make.bat │ ├── Makefile │ └── source │ ├── conf.py │ ├── index.rst │ ├── markdown_is_possible.md │ ├── modules │ │ └── api.rst │ ├── _static │ └── _templates ├── src │ └── some_python_code.py <--- └── README.md
Add some code in it with a few functions.
def foo() -> None: """Public function should appear in the doc. Return ``None``. """ return None def _bar(): """Private function should not appear in the doc.""" ...
Modify
docs/source/conf.py
so that the python code is in its path when ti runs, by adding the following lines:import os import sys sys.path.insert(0, os.path.abspath("../.."))
Also add the
"sphinx.ext.autodoc"
extension to autodocument code to list of active extensions indocs/source/conf.py
.extensions = ["myst_parser", "sphinx.ext.autodoc",]
Now create a
.rst
file in thedoc/source
where the doc of your code will be rendered.├── docs │ ├── make.bat │ ├── Makefile │ └── source │ ├── conf.py │ ├── index.rst │ ├── markdown_is_possible.md │ ├── modules │ │ └── api.rst <--- │ ├── _static │ └── _templates ├── src │ └── some_python_code.py └── README.md
And add in it the sphinx directive to autodocument the code:
api === .. sphinx directives to auto doc code .. automodule:: src.some_python_code :members: :undoc-members: :show-inheritance:
Rebuild the doc to check the new theme.
Serve to Read The Docs
-
Add a
requirements.txt
in the root of the repo.sphinx furo myst-parser
-
Add a
.readthedocs.yaml
in the root of the repo.version: 2 build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation with sphinx sphinx: configuration: docs/source/conf.py # Dependencies required to build your docs python: install: - requirements: requirements.txt
-
The rest of the process is the same as for MkDocs.
Outcome
Jupyter book + GitHub pages
- Use cases:
- Create a documentation website for project containing Markdown documents and Jupyter notebooks.
- Advantages
- easy to work with locally
- easy way to integrate executable code in your documentation
- Examples:
Ingredients
- have a github repo with some markdown in it
- python to serve locally
Recipe
-
Install jupyter book.
pip install -U jupyter-book
-
Create a book template
jupyter book jupyter-book-primer
-
Move into the directory that was created and build the book
cd jupyter-book-primer jupyter book build .
-
Browse the book that was created by opening the landing page
_build/html/index.html
. -
Turn into a repository, gitignore the
_build
and commit all the created contentecho _build > .gitignore git add --all git commit -m 'initial commit'
-
Add a GitHub repo as remote and push to github
git remote add origin <URL_OF_REMOTE> git push --set-upstream origin main
Serve via GitHub pages
-
Add a github workflow to serve the book via github pages
On your repository go to
Settings
->Pages
, and forSource
chooseGitHub Actions
.Create a file
.github/workflows/deploy.yml
with the following content.name: deploy-book # Run this when the master or main branch changes on: push: branches: - main # This job installs dependencies, builds the book, and pushes it to `gh-pages` jobs: deploy-book: runs-on: ubuntu-latest permissions: pages: write id-token: write steps: - uses: actions/checkout@v3 # Install dependencies - name: Set up Python 3.11 uses: actions/setup-python@v4 with: python-version: 3.11 - name: Install dependencies run: | pip install -r requirements.txt # Build the book - name: Build the book run: | jupyter-book build . # Upload the book's HTML as an artifact - name: Upload artifact uses: actions/upload-pages-artifact@v2 with: path: "_build/html" # Deploy the book's HTML to GitHub Pages - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v2
-
Commit and push the file:
git add .github git commit -m "add build and deploy workflow" git push
GitHub should then take over and serve your documentation.
Relevant links
- More info in the jupyter book documentation.