Fastest way to set up and manage an Mkdocs Material project

If I wouldn’t be able to control myself, I would create a new website every week. Creating a website about … any topic really, helps me structure the knowledge I have or am in the process of collecting about it. The tools to create a new website have changed through the years. There was my Blogger period, my WordPress period, my Jekyll period. And now I am in my Mkdocs period.

Not the vanilla mkdocs, mind you. I am using the Material for MkDocs theme. It’s a beautiful theme, with a lot of plugins and extensions, and it’s very easy to use. It felt like an upgrade from Jekyll: auto-reload upon edit, very fast and it just works. The times I’ve struggled to get ‘jekyll serve’ or ‘bundle exec jekyll serve’ running …

The process of setting up a new Mkdocs project was still too slow and cumbersome. So I decided to make a bash script to make things easier and faster: introducing pforret/mkdox.

1. create new bashew script

I start by creating a new script with bashew script. To make things very portable (I don’t want to worry about which version of Python is installed on my system), I started looking into using a Docker-based approach. Luckily there was already a basic Docker image for Mkdocs available on Docker Hub. I used this as a basis and I added more default plugins and settings to it.

This means I require Docker to be installed and running on my system. I added a check for this at the start of my script:

docker -v &>/dev/null || IO:die "Docker is not installed or not yet started"
docker ps &>/dev/null || IO:die "Docker is not yet started"

I added some plugins to the Docker image with

pip install --no-cache-dir \
  weasyprint \
  mkdocs-material[recommended] \
  mkdocs-material[imaging] \
  mkdocs-awesome-pages-plugin \
  mkdocs-with-pdf \
  markdown-include ;

2. add basic commands

mkdox new

⏳  Create new Mkdocs Material project in example
Run 'mkdocs new' via Docker
INFO    -  Writing config file: example/mkdocs.yml
INFO    -  Writing initial docs: example/docs/index.md
Create mkdocs.yml ...
Create docs/about/extensions.md ...
Create docs/about/index.post ...
Create docs/about/plugins.md ...
Create docs/about/index.pre ...
Create docs/news/posts/2024-02-post2.md ...
Create docs/news/posts/2024-01-post1.md ...
Create docs/news/index.md ...
Create docs/index.md ...
Create .gitignore ...
✅  New Mkdocs Material project created in 'example'
# took 2s to create

mkdox serve

WARNING -  Config value 'dev_addr': The use of the IP address '0.0.0.0' suggests a production environment or the use of a proxy to connect to the MkDocs server. However, the MkDocs' server is intended for local development purposes only. Please use a third party production-ready server instead.
INFO    -  Building documentation...
WARNING -  without generate PDF(set environment variable ENABLE_PDF_EXPORT to 1 to enable)
INFO    -  Cleaning site directory
INFO    -  Documentation built in 0.39 seconds                                             
INFO    -  [15:56:01] Watching paths for changes: 'docs', 'mkdocs.yml'
INFO    -  [15:56:01] Serving on http://0.0.0.0:8000/
Open http://localhost:8000 in browser (macOS)
INFO    -  [15:56:08] Browser connected: http://localhost:8000/

This is what that looks like:

img.png

mkdox build

The purpose of a website is that it becomes available online at some point. So I added a mkdox build command that builds the website to a site folder, and then checks this in. This is the folder you can then serve from your web server.

# in the beginning this was just 
mkdocs build
git add docs site
git commit -m "$MESSAGE"
git push

But this became a much more intelligent command later on.

3. add more features

mkdox post

I use the blog plugin for Mkdocs Material, so I created a fast way to create a new blog post.

❯ mkdox post
Post date (2024-03-09) >
Post title (New post) > My new blog on waffles
Post categories (blog,post) > news,waffles
✅  New post created: docs/news/posts/2024-03-09-my-new-blog-.md

mkdox toc and index.pre/index.post

I often thought it would be handy to have index.md files that were automatically built with the links to the subpages. So I developed a system to do this.

Configuration

So I can now start up a new website in seconds. Creating a GitHub/BitBucket repo to store it, setting up new a Forge website that automatically publishes after a git push: maybe another 5 minutes.

Then there’s still the mkdocs.yml file that I need to adjust for each new project. That’s something I do in a text editor.

site_name: "{SITE_NAME}"
site_description: "{SITE_NAME} by {USERNAME}"
theme:
    name: material
    palette:
        primary: red
    font:
        text: Nunito
copyright: "© {CREATION_YEAR} {SITE_NAME} • {USERNAME}"

Usage

Version : v0.4.0 (Mar  9 13:17:55 2024)
Purpose : easy wrapper for Material Mkdocs in Docker mode
Usage   : mkdox [-h] [-q] [-v] [-f] [-G] [-I] [-Q] [-R] [-T] [-X] [-l <log_dir>] [-t <tmp_dir>] [-D <DOCKER>] [-E <TITLE>] [-H <HISTORY>] [-L <LENGTH>] [-P <PORT>] [-S <SECS>] <action> <input?> <output?>
Flags, options and parameters:
-h|--help        : [flag] show usage [default: off]
-q|--quiet       : [flag] no output [default: off]
-v|--verbose     : [flag] also show debug messages [default: off]
-f|--force       : [flag] do not ask for confirmation (always yes) [default: off]
-G|--GITPUSH     : [flag] push to git after commit [default: off]
-I|--INDEX       : [flag] build index.md if index.pre/.post present (for mkdox build) [default: off]
-Q|--SHORT       : [flag] include short contents of page (for mkdox toc) [default: off]
-R|--RECURSIVE   : [flag] also list subfolders (for mkdox toc) [default: off]
-T|--TREE        : [flag] list as tree (for mkdox toc) [default: off]
-X|--EXPORT      : [flag] export to PDF (for mkdox build) [default: off]
-l|--log_dir <?> : [option] folder for log files   [default: /Users/pforret/log/mkdox]
    -t|--tmp_dir <?> : [option] folder for temp files  [default: /tmp/mkdox]
-D|--DOCKER <?>  : [option] docker image to use  [default: pforret/mkdox-material]
    -E|--TITLE <?>   : [option] set site title
-H|--HISTORY <?> : [option] days to take into account for mkdox recent  [default: 7]
    -L|--LENGTH <?>  : [option] max commit message length  [default: 99]
-P|--PORT <?>    : [option] http port for serve  [default: 8000]
    -S|--SECS <?>    : [option] seconds to wait for launching a browser  [default: 10]
<action>         : [choice] action to perform  [options: new,serve,post,build,recent,toc,check,env,update]
<input>          : [parameter] input folder name (optional)
<output>         : [parameter] output file name (optional)
💬 repo 🏷 bash 🏷 bashew 🏷 docker 🏷 material 🏷 mkdocs 🏷 static