How to create a static website¶
Most websites that Beautiful Canoe creates for clients are dynamic sites with some sort of datastore, usually based on the Laravel framework. Like the (static) company website these are bespoke, and each site has its own styling, according to the needs of the client.
However, we have a small number of static sites for internal use, such as this site and our brand guidelines. Less frequently, we sometimes need to create static sites for clients (most likely to publicise a piece of research software) such as the Traffic3D site.
For these static sites, we do not use raw HTML (like the company website does), partly to save time, but also to avoid inconsistencies in styling and content. Instead, we have standardised our static sites on the mkdocs package for Python, with the mkdocs material theme which we use because it is clean, fully responsive and based on Google's material design guidelines. In the past we have been able to put together and deploy a whole new site within a couple of hours.
Starting a new repository: tweaks to our usual advice¶
Each static website should be in its own repository, even if the intention of the site is to publicise a specific software product. This helps us to separate out issues and merge requests for each piece of work we do, and to easily use CI/CD to test documentation and code separately. However, it does mean that sometimes you will need to change some software, then raise a separate issue in another repository to document that change.
Tip
Your repository should normally have the same name as its public URL.
For example, this repository is beautifulcanoe/peopleops/docs.beautifulcanoe.com
.
If you are starting a new repository for a static site, please read through and follow the start a new project HOWTO and related documentation.
For static sites, though, there are a few important tweaks to our usual advice:
- You will not need a
develop
branch in your static website repository - When you create a
.gitignore
file, use the standard Python .gitignore file but add these lines to the file:
# Virtual environment.
venv/*
# Files generated by mkdocs.
site/*
- When you create a Slack integration for the new repository, use one of the
bc-SUBGROUP-gitlab
channels if you are creating a site for internal Beautiful Canoe use, and use thePROJECT-gitlab
channel if you are creating a client site for an existing client.
Warning
The rest of this HOWTO assumes that you have created an issue (to add the initial version of the site), started an MR and that you are working in the feature branch for that MR.
Hopefully, if you have followed the instructions above, you will already be in a feature branch, where you have committed your .gitignore
file and README.md
.
Creating a new URL¶
By the time you get to the end of this HOWTO, your site will be public, so ask the CTO to create a new URL for you as soon as possible.
If your static site will be for internal company use, the URL will be SOMETHING.beautifulcanoe.com
, otherwise this will depend on your project and client.
Writing Markdown¶
The contents of your site will be in Markdown, not HTML. This will save you a lot of time in terms of styling and formatting, but it is important that you are familiar with Markdown syntax.
Tip
All Beautiful Canoe Markdown should have a newline at the end of every sentence. This means that when you raise a merge request, the diff will only consist of sentences that have changed. If this seems confusing, have a look at the way documentation for this site is formatted.
Using mkdocs-material
¶
We do not recommend that you install Python packages globally on your development system, because you may need different versions of the same package for different projects. However, there are several ways to install Python packages locally. In this section, we cover two development workflows:
- Developing with a
mkdocs
running in a Docker container (recommended) - Installing
mkdocs-material
locally with a Pythonvirtualenv
.
Using Docker means that you will be able to use exactly the same environment and dependencies on your development machine and your production deployment environment. It also means that you only need to install Docker locally.
mkdocs-material
with Docker¶
If you are working on Ubuntu, this should work for you:
sudo apt-get install docker docker.io docker-compose
It is also a good idea to add yourself to the docker
group, so that you can avoid using sudo
:
sudo usermod -aG docker ${USER}
Before usermod
takes effect, you will need to log out and log back in again.
After that, please check that this has worked by running id -nG
and checking that docker
is listed in your groups.
For other operating systems, or for more details about Docker on Ubuntu, see the official Docker documentation:
Create a new, empty site with this command:
$ docker run --rm -it --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd -v ${PWD}:/docs squidfunk/mkdocs-material new
INFO - Writing config file: ./mkdocs.yml
INFO - Writing initial docs: ./docs/index.md
(venv) $
Note that, by default, the Docker container will create files as the root
user, rather than the user currently logged in.
In the line above, we use the /etc/passwd
file on disk and the current user name $(id -n)
and primary group $(id -g)
to ensure that the files have the right ownership.
If you are not on a UNIX based system, you can remove these options, but you should check the ownership of the files in site/
:
You can now build and serve the site with mkdocs serve
running on your container:
$ docker run --rm -it -p 8888:8000 -v ${PWD}:/docs squidfunk/mkdocs-material
INFO - Building documentation...
WARNING - Config value: 'dev_addr'. Warning: 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 - Cleaning site directory
INFO - Documentation built in 0.56 seconds
[I 210315 13:12:02 server:335] Serving on http://0.0.0.0:8000
INFO - Serving on http://0.0.0.0:8000
[I 210315 13:12:02 handlers:62] Start watching changes
INFO - Start watching changes
[I 210315 13:12:02 handlers:64] Start detecting changes
INFO - Start detecting changes
[I 210315 13:12:14 handlers:135] Browser Connected: http://127.0.0.1:8888/#bibliography
INFO - Browser Connected: http://127.0.0.1:8888/#bibliography
...
And open your browser at http://127.0.0.1:8888.
Note that you may need to change -p 8888:8000
in the invocation above if you have changed the dev_addr
in your mkdocs.yml
file.
It is unlikely that you will need to build the site without also viewing it in a browser. However, if you do, and you are using Linux, MacOS or another UNIX system you can do this via Docker:
docker run --rm -it --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd -v ${PWD}:/docs squidfunk/mkdocs-material build
This will build the HTML files and write them to a directory called site
.
If you wish to use a different directory name, add the switch -d DIRECTORY_NAME
at the end of that line.
docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material build
mkdocs-material
installed natively¶
To run mkdocs directly from your OS, you will need to install virtualenv and create a new virtual environment. This is a new environment with its own version of Python and its own package manager, so that you can install Python packages in a subdirectory of your repository:
sudo apt-get install python-virtualenv
virtualenv --python=python3 venv
. venv/bin/activate
Your command line prompt should now look like this:
(venv) $
If you need to get out of the virtual environment, run the deactivate
command.
Next, create a file called requirements.txt
containing the dependencies that you need:
pip install mkdocs-material
pip freeze >requirements.txt
At this stage, it is a good idea to add requirements.txt
and commit it.
You will need to run pip freeze
and re-commit requirements.txt
whenever you add a new dependency to the repository.
Create a new, empty site with this command:
(venv) $ mkdocs new .
INFO - Writing config file: ./mkdocs.yml
INFO - Writing initial docs: ./docs/index.md
(venv) $
Now you can build the HTML, and serve it locally:
(venv) $ mkdocs build
INFO - Cleaning site directory
INFO - Building documentation to directory: .../REPO/site
(venv) $ mkdocs serve
INFO - Building documentation...
INFO - Cleaning site directory
[I 191119 15:13:16 server:296] Serving on http://127.0.0.1:8000
[I 191119 15:13:16 handlers:62] Start watching changes
[I 191119 15:13:16 handlers:64] Start detecting changes
[I 191119 15:13:19 handlers:135] Browser Connected: http://127.0.0.1:8000/
And open your browser at http://127.0.0.1:8000 to see something like this:
At this point you should commit the new mkdocs.yml
and docs/index.md
files.
Standard mkdocs
configuration¶
Next, you will want to reconfigure the new site.
You will need to tell mkdocs
to use the Material theme, set some details about the site and add a logo, a favicon, and so on.
It would be a good idea to start with a mkdocs.yml
file from one of the existing repositories, and adapt it to your needs.
In general, it is a good idea to have these extensions:
markdown_extensions:
- admonition
- pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
- pymdownx.highlight:
css_class: codehilite
extend_pygments_lang:
- name: php-inline
lang: php
options:
startinline: true
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format
- pymdownx.inlinehilite:
- toc:
permalink: true
These enable:
- Admonitions - like the Note below
- Diagrams
- Emojis
- Source code highlighting
The permalink: true
setting in toc
ensures that every section heading has a permalink that the reader can copy.
Note
If your static site is for internal Beautiful Canoe use, please do not use any of the default colour schemes.
Instead, please add an extra CSS file called brand.css
which should be a copy of the one in this site.
You will want to change the social media links to something appropriate and use logos and favicons from our brand guidelines site.
Again, you can take this customisation from the mkdocs.yml
file in this repository.
Test your new configuration manually, and then commit it.
Linting and testing your new site¶
To make sure that your Markdown is valid, please use the mdl Markdown lint.
You should use our standard mdl
configuration.
To check for broken links, we use LinkChecker In general, we only use LinkChecker in GitLab CI pipelines, but you may sometimes wish to run it in your development environment, in which case the instructions here should be enough to start you off. However, we would generally recommend just reading the output of the LinkChecker tool in the pipeline logs on GitLab.
First, build the site using mkdocs build
.
This will generate HTML files in a directory called site/
.
Then run LinkChecker from Docker:
docker run --rm -it -v "$PWD"/site:/mnt ghcr.io/linkchecker/linkchecker index.html
You should see something like this:
$ docker run --rm -it -v "$PWD"/site:/mnt ghcr.io/linkchecker/linkchecker index.html
WARNING linkcheck.check 2021-03-15 16:11:31,107 MainThread Running as root user; dropping privileges by changing user to nobody.
INFO linkcheck.cmdline 2021-03-15 16:11:31,107 MainThread Checking intern URLs only; use --check-extern to check extern URLs.
LinkChecker 9.4.0 Copyright (C) 2000-2014 Bastian Kleineidam
LinkChecker comes with ABSOLUTELY NO WARRANTY!
This is free software, and you are welcome to redistribute it
under certain conditions. Look at the file `LICENSE' within this
distribution.
Get the newest version at https://linkchecker.github.io/linkchecker/
Write comments and bugs to https://github.com/linkchecker/linkchecker/issues
Start checking at 2021-03-15 16:11:31+000
Statistics:
Downloaded: 367.56KB.
Content types: 50 image, 16 text, 0 video, 0 audio, 43 application, 0 mail and 93 other.
URL lengths: min=8, max=899, avg=79.
That's it. 202 links in 202 URLs checked. 0 warnings found. 0 errors found.
Stopped checking at 2021-03-15 16:11:31+000 (0.72 seconds)
$
Or, if you have a broken link:
$ docker run --rm -it -v "$PWD"/site:/mnt linkchecker/linkchecker index.html
WARNING linkcheck.check 2021-03-15 16:10:20,326 MainThread Running as root user; dropping privileges by changing user to nobody.
INFO linkcheck.cmdline 2021-03-15 16:10:20,326 MainThread Checking intern URLs only; use --check-extern to check extern URLs.
LinkChecker 9.4.0 Copyright (C) 2000-2014 Bastian Kleineidam
LinkChecker comes with ABSOLUTELY NO WARRANTY!
This is free software, and you are welcome to redistribute it
under certain conditions. Look at the file `LICENSE' within this
distribution.
Get the newest version at https://linkchecker.github.io/linkchecker/
Write comments and bugs to https://github.com/linkchecker/linkchecker/issues
Start checking at 2021-03-15 16:10:20+000
URL `assets/images/sdadaFrontPageImage.jpg'
Name `Traffic3D Front Page Image'
Parent URL file:///mnt/index.html, line 618, col 4
Real URL file:///mnt/assets/images/sdadaFrontPageImage.jpg
Check time 0.000 seconds
Result Error: URLError: <urlopen error [Errno 2] No such file or directory: '/mnt/assets/images/sdadaFrontPageImage.jpg'>
Statistics:
Downloaded: 367.56KB.
Content types: 50 image, 16 text, 0 video, 0 audio, 43 application, 0 mail and 94 other.
URL lengths: min=8, max=899, avg=79.
That's it. 203 links in 203 URLs checked. 0 warnings found. 1 error found.
Stopped checking at 2021-03-15 16:10:21+000 (0.67 seconds)
$
Create git hooks¶
To prevent developers from committing and pushing documentation that will fail the CI/CD pipeline, it is a good idea to run the lint automatically from Git. Git uses hooks to automate these processes, and we want to make sure that standard hooks that we want all developers to use are checked into source control.
You should use our standard bin/create-hook-symlinks
script, and add your hooks to a new hooks/
directory.
For mkdocs-material
sites we usually have these hooks:
- A
pre-commit
hook that runsmdl
to lint all Markdown files in the repository. - A
pre-push
hook that checks that the site builds without error.
We usually do not run LinkChecker in a hook, as it runs quite slowly.
You should start by copying Git hooks from one of our existing mkdocs-material
projects and add them to the repository in a one or more commits.
Add a CONTRIBUTING.md file¶
The CONTRIBUTING.md
file should be in the root of your repository, and should describe how a new developer can start contributing to your documentation.
You should describe how to clone your repository, how to serve the site locally, and how to install and run the lint and link checker.
See the CONTRIBUTING.md
in this repository for an example.
Be sure to commit your file once you are done.
Deploying with GitLab pages¶
Typically, we deploy all static sites on GitLab Pages, rather than our own servers.
We also use GitLab servers to run a CI/CD pipeline to check Markdown formatting and broken links.
It would be a good idea to start by coping the .gitlab-ci.yml
file from one of our existing mkdocs-material
sites.
Rather than creating a custom Docker
container with all the required packages, each job or stage should use the Docker configuration it requires.
Wherever possible, this should mean using the "official" Docker images relating to the necessary packaeges.
For example, there is an official package for mkdocs-material
and LinkChecker.
Once you have a working .gitlab-ci.yml
file and your pipeline succeeds, put your MR up for review.
Monitoring¶
Please add your new site to the company dashboard on uptimerobot for live monitoring, and ensure that alerts go to alert@beautifulcanoe.com
and to the relevant -project
Slack channel (or bc-online
if your project is internal).
For the Slack alert, you will need to create a new webhook in our Slack app.
If you don't have access to our uptimerobot account, or our Slack app, please ask the CTO.
If your site has an SSL certificate, please also raise an issue and an MR to add it to our SSL validation script in the cron repository.
Further reading¶
- Beautiful Canoe brand guidelines (example docs site)
- Beautiful Canoe brand guidelines repo (example configuration)
- Beautiful Canoe HOWTOs (this site)
- Docker
- Font Awesome brand icons
- Getting [meta] with GitLab CI/CD: Building build images
- Git hooks
- GitLab Container Registry
- GitLab Pages
- Google's material design
- LinkChecker
- Markdown guide
- Markdown lint
- mkdocs
- mkdocs admonitions
- mkdocs material theme
- Python package manager
- Traffic3D (example docs site)