Troubleshooting Deploying Documentation Using MkDocs

Recently, I was developing a package for my job. One of the requirements was to make sure that it was properly documented. Being a big fan of Markdown, MkDocs was a natural choice. Not only is it easy to pick up and customize, but Material for MkDocs offers some fantastic functionality on top of it. With very little effort, you can add admonitions (the nice colorful boxes with handy information), tables , site search , and many others.

After spending time and effort in writing the documentation per se, it was finally time to go live and deploy it. You can do this manually using GitHub Pages. However, in my case it was much handier to automate the documentation deployment after every push. GitHub Actions allow you to do this very easily. Unfortunately, this solution didn’t really work out of the box for me. In this post, I will share the problems I ran into and how I fixed them. Maybe this will be useful for someone else.

0. Package file structure

To understand the issues that I was having (and to see if you would run in the same ones), let’s see how the package was organized:

├── .github                <- Contains GitHub enhancements, including workflows for CI.
│
├── dissemination          <- Reports, presentations, posters, etc.
│
├── docs                   <- The most important part for this post.
│   ├── docs               <- The actual documentation files.
│   │   ├── api_reference
│   │   │   ├── index.md
│   │   │   ├── foo.md     
│   │   │   ├── bar.md  
│   │   │   └── ...
│   │   │
│   │   ├── stylesheets
│   │   │   └── extra.css  <- For further customization of the theme (https://squidfunk.github.io/mkdocs-material/customization/)
│   │   │
│   │   └── index.md       <- Index page of the documentation.
│   │
│   ├── site               <- Site files built for local hosting of the documentation (i.e., when using mkdocs serve)
│   └── mkdocs.yml         <- MkDocs configuration file
│
├── examples               <- Jupyter notebooks that serve as examples/tutorials.
│
├── cool_package           <- The actual scripts of cool_package.
│   ├── __init.py__
│   ├── foo.py
│   ├── bar.py
│   └── ..
│
├── multimedia             <- Multimedia files (e.g., logos).
│
├── .gitignore             <- Good old .gitignore.
│
├── LICENSE                <- It's always good to have a license for your package.
│
├── README.md              <- cool_package's README.
│
└── setup.py               <- Metadata of cool_project.

1. Config file 'mkdocs.yml' does not exist

This was the first error I ran into. This is because since the CI is being run from the package’s root directory, it cannot find the file mkdocs.yml, since it is one level deeper (see the project structure above).

The solution was to set a working-directory for a specific step by updating ci.yml as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  name: ci
  on:
    push:
      branches:
        - master
        - main
  permissions:
    contents: write
  jobs:
    deploy:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
        - uses: actions/setup-python@v4
            with:
              python-version: 3.x
        - run: echo "cache_id=\$(date --utc '+%V')" >> \$GITHUB_ENV
        - uses: actions/cache@v3
            with:
              key: mkdocs-material-\$
              path: .cache
              restore-keys: |
                mkdocs-material-
        - run: pip install mkdocs-material
        - working-directory: ./docs
          run: mkdocs gh-deploy --force

As you can see, in line 25, we are telling GitHub actions “hey, execute this step starting in ./docs”. This way, it can find mkdocs.yml.

2. Config value 'plugins': The "XXX" plugin is not installed

I ran into this issue with the mkdocstrings and git-revision-date-localized plugins. This is because although you might have them installed in the local machine, they are not in the environment that is created for deploying the documentation.

To fix it, update ci.yml as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  name: ci
  on:
    push:
      branches:
        - master
        - main
  permissions:
    contents: write
  jobs:
    deploy:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
        - uses: actions/setup-python@v4
            with:
              python-version: 3.x
        - run: echo "cache_id=\$(date --utc '+%V')" >> \$GITHUB_ENV
        - uses: actions/cache@v3
            with:
              key: mkdocs-material-\$
              path: .cache
              restore-keys: |
                mkdocs-material-
        - run: pip install mkdocs-material
        - run: pip install mkdocstrings
        - run: pip3 install mkdocs-git-revision-date-localized-plugin
        - working-directory: ./docs
          run: mkdocs gh-deploy --force

In lines 25 and 26 we install the required plugins. You will have to do this for any additional plugins that required you installing something in your local machine.

3. ModuleNotFoundError: No module named 'mkdocstrings_handlers'

(Earlier in the log, you might also see ERROR - Error reading page 'api_reference/foo.md': No module named 'mkdocstrings_handlers')

Wait, what? I just told it to install mkdocstrings! What’s going on?

Well, this is a known issue (or I should say “issue”). Since MkDocs is language agnostic, newer versions need a little bit more information when installing mkdocstrings.

To solve this, you need to install mkdocstrings-python by updating ci.yml as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  name: ci
  on:
    push:
      branches:
        - master
        - main
  permissions:
    contents: write
  jobs:
    deploy:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
        - uses: actions/setup-python@v4
            with:
              python-version: 3.x
        - run: echo "cache_id=\$(date --utc '+%V')" >> \$GITHUB_ENV
        - uses: actions/cache@v3
            with:
              key: mkdocs-material-\$
              path: .cache
              restore-keys: |
                mkdocs-material-
        - run: pip install mkdocs-material
        - run: pip install mkdocstrings[python]>=0.18
        - run: pip3 install mkdocs-git-revision-date-localized-plugin
        - working-directory: ./docs
          run: mkdocs gh-deploy --force

As you can see, in line 25 we are providing the proper dependency to mkdocstrings by telling it to use the Python version.

4. ERROR - mkdocstrings: No module named 'cool_package'

You might have your package installed in your local machine, but it is not installed in the created virtual environment.

To do so, just update ci.yml as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  name: ci
  on:
    push:
      branches:
        - master
        - main
  permissions:
    contents: write
  jobs:
    deploy:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
        - uses: actions/setup-python@v4
            with:
              python-version: 3.x
        - run: echo "cache_id=\$(date --utc '+%V')" >> \$GITHUB_ENV
        - uses: actions/cache@v3
            with:
              key: mkdocs-material-$
              path: .cache
              restore-keys: |
                mkdocs-material-
        - run: pip install mkdocs-material
        - run: pip install mkdocstrings[python]>=0.18
        - run: pip3 install mkdocs-git-revision-date-localized-plugin
        - run: pip3 install --editable .
        - working-directory: ./docs
          run: mkdocs gh-deploy --force

As you can see in line 27, we are now telling it to install cool_package. Given my file structure (see Sec. 0 above), . points to where cool_package’s, setup.py is located. Of course, if your file organization is different, you might have to adapt this (probably using a trick similar to the one showed in Sec. 1).

That’s it! After this, I was able to easily update and deploy cool_package’s documentation website after every push using GitHub Actions. I hope this posts helps you if you run into similar issues.


If you have any comments, questions or feedback, leave them in the comments below or drop me a line on Twitter (@amoncadatorres). Moreover, if you found this useful, fun, or just want to show your appreciation, you can always buy me a cookie. Cheers!