At Consensus, we spend a fair bit of our time building complex Drupal systems and the infrastructure that supports them. In that context, the simplicity of a tool like Hugo caught my attention. Hugo is a static site generator written in Go that has proven elegant and flexible for many situations where a simple (or even slightly complex) website is called for, and the venerable Drupal is overkill.
Perhaps one of the best uses we’ve found for this tool is to provide technical documentation for a development project. Hugo reduces the website to a handful of plain text files (some Markdown, some YAML, some HTML/CSS), so it’s easy to drop it into a sub-folder of the main project, meaning it lives alongside the code of the site/application itself. This makes it easier to maintain and refer to in the course of regular development workflow.
Here’s a quick rundown of how to get started incorporating a Hugo docs site into your existing development project.
Contents
Hugo Setup
Once you Install Hugo on your computer, creating a new site is easy: initialize a new site, grab a theme, and do some basic configuration. The Learn theme is a reasonable starting point for a docs site.
Assuming you are starting from an existing Git project at $PROJECT_ROOT
, do:
cd $PROJECT_ROOT
hugo new site docs
git submodule add https://github.com/matcornic/hugo-theme-learn.git docs/themes/learn
git add docs
git commit -m "Initialize docs site"
cd docs # Your docs site lives in here
Config.yml
TOML is the default format, but we prefer YAML. We will replace the default
config.toml
with a config.yaml
. The template below is a good starting
point, but note that you’ll need to replace the GROUP
, and PROJECT
parameters to match your GitLab project. You can also fill in various details
like Title, Description, Author, and various feature flags.
Note: If your project is in a GitLab sub-group, the baseURL and editURL
parameters below will need to include the slug
for the sub-group path. ie.
http://GROUP.gitlab.io/SUBGROUP/PROJECT
.
config.yml
:
baseUrl: "http://GROUP.gitlab.io/PROJECT/"
languageCode: "en-US"
defaultContentLanguage: "en"
title: "My Project Docs Site"
theme: "learn"
metaDataFormat: "yaml"
defaultContentLanguageInSubdir: true
params:
editURL: "https://gitlab.com/GROUP/PROJECT/tree/master/docs/content/"
description: "Description of project docs site"
author: "Consensus Enterprises"
showVisitedLinks: true
disableBreadcrumb: false
disableNextPrev: false
disableSearch: false
disableAssetsBusting: false
disableInlineCopyToClipBoard: false
disableShortcutsTitle: false
disableLanguageSwitchingButton: false
ordersectionsby: "weight" # or "title"
menu:
shortcuts:
- name: "<i class='fa fa-gitlab'></i> Gitlab repo"
url: "https://gitlab.com/GROUP/PROJECT"
weight: 10
- name: "<i class='fa fa-bullhorn'></i> Contributors"
url: "https://gitlab.com/GROUP/PROJECT/graphs/master"
weight: 30
# For search functionality
outputs:
home:
- "HTML"
- "RSS"
- "JSON"
Now, remove the original config.toml
and add the new config.yaml
to git:
git rm config.toml
git add config.yaml
git commit -m "Configure learn theme for docs site"
Add Search
One extra thing we’ll set up is to enable site-wide search of the docs. This is
accomplished by adding a JSON index of the entire site, described in the
layouts
folder. Create the file docs/layouts/index.json
:
[{{ range $index, $page := .Site.Pages }}
{{- if ne $page.Type "json" -}}
{{- if and $index (gt $index 0) -}},{{- end }}
{
"uri": "{{ $page.Permalink }}",
"title": "{{ htmlEscape $page.Title}}",
"tags": [{{ range $tindex, $tag := $page.Params.tags }}{{ if $tindex }}, {{ end }}"{{ $tag| htmlEscape }}"{{ end }}],
"description": "{{ htmlEscape .Description}}",
"content": {{$page.Plain | jsonify}}
}
{{- end -}}
{{- end -}}]
Once again, add this new file to git:
git add layouts/index.json
git commit -m "Add index.json layout for site-wide search index"
Create some content
Now you can start creating some content. Again, these are just simple text files in Markdown format, so you can create and edit them using whatever your favourite text editor may be.
The content/
folder within your Hugo site contains all content posts, and the
menu structure will mirror whatever folder hierarchy you develop. To get
started quickly, you can use hugo to initialize some posts for you:
hugo new _index.md
to create the front pagehugo new technical/_index.md
to create a sub-sectionhugo new technical/architecture.md
to create a sub page
This will set up the file for the post, which includes metadata (what Hugo calls the “front matter”), after which you simply edit it, and add the content. The front matter of a Hugo post is a section at the top of the file containing metadata such as title, date, etc.
By default, Hugo will create new posts in “draft” state, so at minimum you want
to edit each new file to “publish” it. Edit the files you created above
(content/_index.md
and content/technical/architecture.md
), adjust the title
as needed, set draft: false
, and add some content. For example:
---
title: "Technical Architecture"
date: 2019-11-06T16:58:13-05:00
draft: false
---
This page describes the technical architecture of the project.
Make sure your new content files are added to git:
git add content
git commit -m "Add some content"
Serve it up (locally)
Now you are ready to see the site in action. One of the great things about locally is it’s trivial to spin up the site on your local machine and see your draft updates live:
hugo serve
- point your browser to http://localhost:1313/PROJECT
By default, Hugo in this mode will auto-refresh your browser page any time you save a change to a content file, so you can actually see your revisions taking effect on an instance of the site as you type. This makes it much easier to manage updating documentation for a project in tandem with building out or making changes to the codebase itself.
Menu structure
As mentioned above, the menu structure for your docs site mirrors the folder
hierarchy within the content/
directory. For example, here’s a typical “tree”
of content for a docs site:
docs/content
├── _index.md
├── admin
│ ├── _index.md
│ ├── updating.md
│ └── workflows
│ ├── _index.md
│ ├── taxonomy.md
├── dev
│ ├── architecture
│ │ ├── _index.md
│ │ ├── search_system.md
│ │ └── static_content.md
│ ├── _index.md
│ └── setup
│ ├── dev-workflow.md
│ ├── _index.md
│ └── theme-dev.md
└── testing
├── demo.md
├── _index.md
└── writing_tests.md
This would result in a top-level menu like this:
- Home (/)
- Admin (/admin/)
- Development (/dev)
- Testing (/testing)
The menu titles themselves are determined by the frontmatter in the _index.md
files at each level. Typically this matches the title:
element, but sometimes
you need to override it, in which case you can specify the menuTitle
explicitly:
---
title: Development Workflows using GitLab
menuTitle: Dev Workflows
weight: 30
---
GitLab Pages
Our final step is to publish the site using GitLab
Pages. This is simply a matter
of adding a .gitlab-ci.yml
at the root of your project with a pages
stage that runs hugo
to generate static HTML from your new docs/
folder.
Essentially, you tell GitLab CI to use a Hugo container to render the docs/
folder into a set of static HTML/CSS/JS in the public
subfolder, and then
move that folder into a location from which GitLab Pages can serve it up.
Note: Before you push to CI, it’s important that you set at least the baseURL
in your docs/config.yaml
file, so it matches where GitLab Pages will place
it. See GitLab’s docs on Pages default domain
names
for details.
Here’s a simple example .gitlab-ci.yml
:
stages:
- publish
variables:
GIT_SUBMODULE_STRATEGY: recursive
pages:
stage: publish
image: jojomi/hugo
cache: {}
script:
- hugo -s docs # Render the Hugo site
- mv docs/public . # Move the public folder into place
artifacts:
paths:
- public # Tell GitLab Pages to serve the public folder
only:
- master
See the CI/CD for GitLab Pages docs for more information on this, or the full .gitlab-ci.yml reference for all the gory details.
Now you can push your changes up to GitLab, and you should immediately see a CI pipeline kick in to publish your Pages site.
git add .gitlab-ci.yml
git commit -m "Add .gitlab-ci.yml config to publish Pages site"
git push
Go to the project’s Settings > Pages, and you will see a URL where the
site is published under the *.gitlab.io
domain, and you can also attach your
own domain by clicking the New Domain link and following the instructions to
point a DNS entry to the GitLab Pages server.
Example repository
A co-worker of mine has created an example repository by following the steps in this post and committing regularly. You can find it at gitlab.com/mparker17/learn-hugo.
Conclusion
Documentation for a technical project is a crucial component for even the most simple cases. Hugo provides an excellent framework to provide documentation housed within the project’s codebase.
It’s easy for developers to edit and update in tandem with their regular development workflow, and review those changes locally before pushing them up alongside code updates. GitLab Pages provides an easy mechanism for publishing the resulting site, and GitLab CI will auto-publish updates with every push.
Check out the Hugo Documentation pages to find out more about how to use Hugo, manage content, tweak the theme templates, and more.
The article How to add a Hugo-based Docs site to your GitLab Project first appeared on the Consensus Enterprises blog.
We've disabled blog comments to prevent spam, but if you have questions or comments about this post, get in touch!