Publish your Markdown docs on GitHub Pages
Tags: github, markdown, python, tutorial
Published: 2021-08-10 19:12:18

As a developer you've most likely switched machines, even OS several times. Maybe you've performed a specific set of operations that you'll have to repeat in the future? want to remember some command lines? share code snippets? capture online course notes? 

For all of the reasons above, and many more, having a personal documentation website is ideal. It lets you organize and synthesize all the information that is relevant to YOU in your own personal way, with the added benefit of being easily shareable. 

## What we'll do

A project containing all your [Markdown](https://github.github.com/gfm/#what-is-markdown-) files, hosted on [Github Pages](https://pages.github.com/).

**Requirements**
- [Python 3](https://www.python.org/download/releases/3.0/) (tested with version 3.9.5+)
- [Git](https://git-scm.com/) (2.12+)
- [Make](https://www.gnu.org/software/make/) 

## How we do it

### Install `mkdocs`

We'll use [`mkdocs`](https://www.mkdocs.org/) as static site generator, so let's install it:
```sh
pip install mkdocs
```

### Create your docs source project

Now create the basic project structure, in your projects folder:
```sh
mkdocs new GITHUB_USERNAME
cd GITHUB_USERNAME
```

Replace GITHUB_USERNAME with your GitHub username or whatever you prefer to name your project. We'll be referencing it in the next steps and it will become clearer why you might want to choose that project name. 

Check the created files in that folder, you'll see something like the following:
```
./
-- docs/
-- mkdocs.yml
```

Go ahead and try running `mkdocs serve` then open http://127.0.0.1:8000 in your browser. And voilà! You have a personal documentation local website. Any `.md` file added in `docs/` will appear as a new web page. Try to edit `docs/index.md` for example.

`mkdocs.yml` contains your project configuration. You can customize your site name, navigation layout and so on. A simple example would be:
```yaml
site_name: your site name
site_url: https://GITHUB_USERNAME.github.io
site_author: you
site_description: a short description of your website

nav:
  - About: index.md
```

You can also use the [readthedocs](https://www.mkdocs.org/user-guide/choosing-your-theme/#readthedocs) provided theme by adding the following:
```yaml
theme:
  name: readthedocs
  highlightjs: true
  hljs_languages:
    - yaml
```

For reference see https://www.mkdocs.org/user-guide/configuration/

At this point you should have something like this locally:
![mkdocs generated website with readthedocs theme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1rdwqhzk6r4tefd36ki4.png)

For a local documentation project that would be enough. But we'll go ahead and make this deployable to your GitHub page.

### Create the GitHub repositories

GitHub allows you to host a website in a specific repository matching GITHUB_USERNAME.github.io, with your actual GitHub username in place of GITHUB_USERNAME. 

Luckily `mkdocs` makes it easy to push your project build files to your GitHub page through the [`mkdocs gh-deploy`](https://www.mkdocs.org/user-guide/deploying-your-docs/) command. 

The downside of `mkdocs gh-deploy` is that it wipes out all the files in `GITHUB_USERNAME.github.io/` to generate the website files (html, css, js).

To overcome this constraint and simplify the deploy process you'll track your GitHub page repo, GITHUB_USERNAME.github.io, independently as a [git submodule](https://git-scm.com/docs/git-submodule) of your docs source project. Which will allow to run `mkdocs gh-deploy` from within the docs source project itself.

Ultimately you'll need two distinct repositories: one for your GitHub page, another one for your source files. 

#### Create the GitHub page repo

- Go to https://github.com/new and create your GitHub page repository
- Name it GITHUB_USERNAME.github.io (replace GITHUB_USERNAME with your actual GitHub username)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5ra3s973kly4u6uqaq8.png) 

> 📝 **Edit 16 Apr 2023** 
> If you did not choose your GitHub username in lieu of GITHUB_USERNAME then you might have to do a couple of extra steps:
>  - From the github.io repo, choose *Settings*
>  - Under *Code and automation*, click on *Pages*
>  - In *Source* > *Deploy from a branch*
>  - In *Branch* > choose `main` and save
>  - Type in the URL of your website "my-new-docs-website.github.io" and save
>
> Thanks for the feedback @konsoul!

#### Create the source repo

- Go to https://github.com/new and create your source repository
- Name it GITHUB_USERNAME (replace GITHUB_USERNAME with your actual GitHub username or anything else you chose to name the new `mkdocs` project previously created)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8b7js4mr84wv4lxbycly.png)

> 📝 A repository named as your username is a special repository on GitHub. Its `README.md` will appear on your public profile. So you can customize that as well.

At this point you should have two GitHub repositories:

|Git repo|Description|
|---|---|
|GITHUB_USERNAME.git (or anything else you chose to name it)|Source repo with markdown files|
|GITHUB_USERNAME.github.io.git|GitHub page repo with static web files|

### Initialize the git repositories

Back in the folder created previously:
```sh
git init

echo site/ > .gitignore

git add .
git commit -m "first commit"

git branch -M main
git remote add origin SOURCE_REPO # replace SOURCE_REPO with your source repo HTTPS or SSH adddress
git push -u origin main
```

Then initialize the GitHub page repo:
```sh
# still in your source folder

mkdir GITHUB_USERNAME.github.io
cd GITHUB_USERNAME.github.io

git init
git commit --allow-empty -m "first commit"

git branch -M main
git remote add origin GH_PAGE_REPO # replace GH_PAGE_REPO with your source repo HTTPS or SSH adddress
git push -u origin main

cd ..
```

You should now have two folders tracked in their respective GitHub repositories:
```
GITHUB_USERNAME/                  Source repo
-- GITHUB_USERNAME.github.io/     GitHub page repo
-- docs/
-- .gitignore
-- mkdocs.yml
```

Now delete `GITHUB_USERNAME.github.io/` since there's no need for it to exist independently:
```sh
rm -rvf ./GITHUB_USERNAME.github.io
```

Add your GitHub page repo as a submodule of your source repo instead:
```sh
git submodule add -b main GH_PAGE_REPO # replace GH_PAGE_REPO with your source repo HTTPS or SSH adddress
git add GITHUB_USERNAME.github.io
git commit -m "add github page submodule"
git push
```

### Deploy your docs to your GitHub page

Run:
```sh
cd GITHUB_USERNAME.github.io
mkdocs gh-deploy --config-file ../mkdocs.yml --remote-branch main
cd ..
```

Go ahead and open https://GITHUB_USERNAME.github.io in your browser. And voilà! Your very own personal documentation on your GitHub page. 

> 📝 You might have to wait a few seconds for your changes to be deployed.

### Add deploy command in Makefile

The `mkdocs gh-deploy` command in itself isn't very user friendly nor easy to remember so we'll add a simple `Makefile` to make (word choice on purpose) it easier to deploy changes.

Create a `Makefile` in your source folder with the following:
```makefile
SHELL := /bin/bash
GH_PAGE := GITHUB_USERNAME.github.io

.PHONY: deploy
deploy:
    cd $(GH_PAGE) && mkdocs gh-deploy --config-file ../mkdocs.yml --remote-branch main
```

> 📝 Ensure to paste `Makefile` indentation as Tabs or else you might get an error in the lines of `[...] *** missing separator.  Stop.`

Now you can simply run `make deploy` whenever you want to publish something.

### Keep both repositories in sync

You might also want to update latest commit version of your GitHub page submodule whenever a deploy is performed. Not technically required, but good for tracking purposes. Add the following in your `Makefile`:
```makefile
.PHONY: update-build-version
update-build-version:
    git submodule update --remote --merge
    git add $(GH_PAGE)
    git commit -m "ci: update build version"

.PHONY: publish
publish: deploy update-build-version
    git push
```

`make publish` will handle the deploy and update your submodule version.

A typical workflow would be:

1. make some changes to your markdown files or `mkdocs.yml`
2. `git commit -am "commit message"`
3. `git push` to your source repo
4. `make publish` to your GitHub page

### Why so complicated ?

***Can't I just run `mkdocs build` and use `site/` as my GitHub page?***
You can! See the [GitHub docs](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site). You will need to commit the build `site/` artifacts, or you could even use a different branch for the GitHub page altogether. IMHO I'd much rather track the actual source code and deploy the minimal amount of files in the public project, and having two completely unrelated branches in the same repository is kind of an overly obscure solution. Hence the submodule approach. But that is my biased opinion, use whichever solution works best for you!

## Just show me the code

Here is a sample project containing the output of everything described in this post: 
- https://github.com/ar2pi/sample-mkdocs-gh-project

## Similar articles

Here are a few other articles I could find before writing this post:

- [Create your own mkdocs with Github pages (by 
cosckoya)](https://dev.to/cosckoya/create-your-own-mkdocs-with-github-pages-a5c)
- [Custom GH Pages deploys made easy (by 
Michael)](https://dev.to/michaelcurrin/github-pages-deploys-made-easy-343o)
- [MkDocs : Static HTML sites and documentation preparation tool that you can host on GitHub pages (by Sandeep Balachandran)](https://dev.to/sandeepbalachandran/mkdocs-static-html-sites-and-documentation-preparation-tool-that-you-can-host-on-github-pages-262f)

## Summary

You just created a brand new GitHub page hosting all your desired markdown generated documentation. Which can also contain your cv, portfolio, todo list, random thoughts, cat pictures, etc. you name it. Go nuts, the web is limitless.

Happy hacking!