# `mdbook-template`
John Simpson `` 2022-05-30
Last updated 2022-10-07
This repo contains a template that I use when creating new "books" using [mdbook](https://rust-lang.github.io/mdBook/), so I don't have to manually copy and edit a bunch of files. It contains an empty "book" generated by mdbook, with the following customizations:
* Horizontal bars of varying thickness above `H1`, `H2`, and `H3` headers. (See the [generated output](https://kg4zow.github.io/mdbook-template/) for an example.)
I find this makes it easier to quickly spot the beginnings of the sections when the lines are used as separators between the sections. I also think it makes more sense to have the bars *above* the section headers instead of below them.
* Automatic git commit and version information at the bottom of the navigation panel on the left.
* A `Makefile` with rules to publish the HTML and other files that make up the book, either to a web server or to GitHub Pages.
[`https://kg4zow.github.io/mdbook-template/`](https://kg4zow.github.io/mdbook-template/) contains the output generated from this repo, as published by "`make gh-pages`" in the `Makefile`.
# TL;DR
## Start a new book using this repo as a template
Clone the template repo.
```
$ cd ~/git/
$ git clone https://github.com/kg4zow/mdbook-template newbook
$ cd newbook/
```
⚠️ **REMOVE THE `.git/` DIRECTORY**, so the directory on your workstation isn't "linked" to my git repository.
```
$ rm -rf .git/
```
Remove or edit the following files as needed:
* `book.toml`
* `LICENSE.txt` - see [`choosealicense.com`](https://choosealicense.com/) if needed
* `README.md`
* `src/introduction.md`
```
$ nano book.toml
$ rm LICENSE.txt
$ nano README.md
$ sed -i -e '2,$d' src/introduction.md
```
If your text editor (or operating system) creates temp files, backup files, or other files which need to *not* be added to the repo, update the `.gitignore` file as needed.
```
$ nano .gitignore
```
If the generated HTML files are going to be hosted on a web server, or with a service like GitHub Pages or [Keybase Sites](https://book.keybase.io/sites), edit `Makefile` as needed. There are comments in the `Makefile` in the repo which may be useful, please let me know if you have any ideas to improve the file.
```
$ nano Makefile
```
### The `git2rss` script
The template's `Makefile` is set up to use my [`git2rss`](https://github.com/kg4zow/git2rss) script to generate an RSS feed containing the commits in the repo's `main` branch.
**If you plan to use this feature**, edit the `.git2rss` file and customize the RSS feed's title, link, and other attributes as needed. When you publish the finished site, it will have a `commits.xml` file containing the feed. Whatever the URL is for the finished site, you can point an RSS reader to this file and "subscribe" to get notified whenever new commits are made to the book's source files.
For example, you can subscribe to [`https://kg4zow.github.io/mdbook-template/commits.xml`](https://kg4zow.github.io/mdbook-template/commits.xml) to watch for updates to the `kg4zow/mdbook-template` repo.
Note that the file itself contains an XML list of the repo's commit history. If the repo is public, there's no information in the file that somebody couldn't get on their own by cloning a copy of the repo, or by visiting the repo's web interface (assuming it's hosted on a git server which *has* a web interface, of course).
**If you don't *want* this feature**, delete the `git2rss` script and the `.git2rss` file.
You *can* also remove the line in the `Makefile` which mentions `git2rss`. If you don't, the `Makefile` checks whether the script and config file both exist before trying to run the script, so it won't hurt anything if you *don't* remove the line.
### Initialize a new git repo
Also create and tag the initial commit.
```
$ git init -b main
$ git add .
$ git commit
$ git tag initial
```
### GitHub Pages
**IF** you plan to publish your book using GitHub Pages, run the `setup-gh-pages` script. This will create a `gh-pages` branch with a *totally separate commit history* from the `main` branch.
```
$ bash setup-gh-pages
```
> ⚠️ The script requires `git` version 2.0.7 or later.
Once you've done this, or if you don't need to do it, you can remove the script from your repo.
```
$ git rm setup-gh-pages
$ git commit -m 'Removed setup-gh-pages script'
```
### Create a GitHub repo and push the new repo to it
```
$ gh repo create --public kg4zow/newbook
$ cd ~/git/newbook/
$ git remote add origin [email protected]:kg4zow/newbook
$ git push --all -u
$ git push --tags
```
> ℹ️ **The `gh` command is the [GitHub CLI](https://cli.github.com/).**
>
> If you don't have it and don't want to install it, you can also use the GitHub web interface to create a repo by hand.
If you're publishing to GitHub Pages, be aware that it might take a few minutes before the page is published.
## Working on the book's content
### In window 1 ...
```
$ cd ~/git/newbook/
$ make serve
```
This will generate the book's HTML, run a small web server, and open a browser window pointing to the rendered HTML.
For now, leave this window alone. Whenever you make a change to one of the files which make up the site, you will see log entries scroll by in this window when it re-generates the HTML.
### In window 2 ...
```
$ cd ~/git/newbook/
$ bbedit .
```
For me, this opens [BBEdit](https://www.barebones.com/), my normal text editor. Obviously, feel free to use whatever text editor you like.
### Edit the book
Do your thing.
As you save changes, you should see the browser window that `make serve` opened, update themselves with your changes. This lets you see the results of your changes right away (and for me, it lets me spot errors *before* I commit and publish them.)
## Finished editing
When you reach a point where you're happy with your changes, commit them.
```
$ git add .
$ git commit
```
If the git repo is hosted with Github (or any other `git` host), push the changes.
```
$ git push
```
Make sure the "`push:`" target in `Makefile` is correct, then ...
```
$ make push
```
If you're totally finished, go back to "Window 1" where the `make serve` command is still running, and hit CTRL-C to stop it.
# Background
This section will explain the customizations I'm making to how `mdbook` formats its output.
## Section Lines
### What are "Section Lines"?
If you're looking at the [generated HTML from this template repo](https://kg4zow.github.io/mdbook-template/), you will notice horizontal lines of different thickness above some of the section headers. The three example section headers will have the three "sizes" of lines.
⚠️ Note that these lines will only show up in the HTML that `mdbook` generates. If you're looking at this file in the GitHub web interface, you'll see GitHub's "Section Lines", which are different (i.e. only for `H1` and `H2`, with `H1`/`H2` lines being the same thickness, and with the lines *below* the section header text). GitHub does not allow any kind of custom stylesheets within their web pages, for security reasons.
### Why do I want this?
I do this because when I'm skimming through a long document, having the lines *above* the section header text makes it easier for me to see where each section starts. Also, having lines of different thickness makes it easier to see where each major and minor section starts.
### How does this work?
This is done using two files:
* **`section-lines.css`** - contains the CSS declarations which add the lines to the HTML.
The CSS customizes the appearance of HTML `H1`, `H2`, and `H3` elements, *other than* the first `H1` on the page. I don't add the heavy bar above the first `H1` because I use the same CSS when generating PDF files, I normally use an `H1` header for the document title, and I didn't want to see that extra heavy bar across the top of the first page.
* **`book.toml`** - tells `mdbook` how to generate the book. The following line tells `mdbook`, when it generates HTML output, to include the following CSS files in addition to the ones included in the theme.
```toml
[output.html]
additional-css = [ "section-lines.css" , "version-commit.css" ]
```
This example has a second file in the `additional-css` list. This other file is used by the Automatic Git Commit Information feature, which is explained below.
## Automatic Git Commit Information
If you're looking at the [generated HTML from this template repo](https://kg4zow.github.io/mdbook-template/), you will see a block at the bottom of the navigation bar on the left, containing some information.
* The "Version" section contains information about the git commit from which the HTML was generated. This will include:
* The most recent tag, if any. (This is part of why I always tag the first commit in a repo, especially an mdbook book.)
* How many commits *after* that tag it is.
* The commit hash itself (after the "`g`").
* *Maybe* a "`-dirty`" indicator, which means that there were changes which hadn't been committed yet.
Below this will be the timestamp of that commit.
* The "Generated" section contains the timestamp when the HTML files were generated.
This is not something that `mdbook` does on its own, I had to figure out how to make it happen.
**I am not the first person to figure out how to do this**, or even to ask about it. I was able to get this working based on information from the links in [this GitHub issue](https://github.com/rust-lang/mdBook/issues/494). My contributions, if any, are...
* Bundling those commands into a `Makefile`, in a template repo that others are free to use.
* Figuring out how and where to edit the `theme/index-template.hbs` file to put the version info at the bottom of the navigation bar.
* Writing the `version-commit` filter script, in such a way that the `"""sh -c 'jq ".[1]"; sed ...'"""` thing that I've seen in a few different places, isn't needed. (This particular command can be difficult for people to understand, especially if they're not used to working with scripting languages.)
* Writing the documentation you're reading right now.
The way this repo does it it involves the following files:
### `version-commit`
This is a script which does two things:
1. **Reads a stream of data provided by `mdbook`, and prints part of it out.**
When `mdbook` runs a filter, it sends that script a JSON list with two elements. The first element will contain information about the overall "book", and the second element will contain information about the "chapters", or pages, which make up the book. This includes the contents of the input Markdown files.
Filtering scripts are expected to output a *possibly modified* copy of *just the second element* from the JSON structure. If the script happens to modify the JSON, it will change the "input" that `mdbook` processes, and thereby modify the generated HTML pages.
In our case, we aren't modifying anything about the Markdown input itself. We're only writing a "filter" script because it's the only "hook" that `mdbook` provides to run user-supplied scripts before building the HTML pages.
Because we aren't modifying the input, the script just prints the second element as-is.
2. **Reads the "`theme/index-template.hbs`" file, substitutes a few values related to the state of the git working directory, and writes a new "`theme/index.hbs`" file.**
Specifically, the script substitutes values for the following tags:
* `@VERSION_COMMIT_HASH@` - the output from "`git describe --always --tags --dirty`", which includes the most recent tag and how many commits "ahead" of that tag we are, the commit hash (if it's different from the tag), and whether or not the content is "dirty" (i.e. if the working directory contains content which hasn't been committed yet).
* `@VERSION_COMMIT_TIME@` - the timestamp of that commit.
* `@VERSION_COMMIT_NOW@` - the timestamp when the script was executed by `mdbook`.
All other content from the "`the/index-template.hbs`" file is copied as-is to the "`theme/index.hbs`" file.
The "`theme/index.hbs`" file written by this script, is used as the template for every HTML page that `mdbook` generates.
### `version-commit.css`
Contains CSS to control the formatting of the items being added to the template.
In my case I wanted the text to be a bit smaller than the normal text in the Table of Contents, so [the file I use](./version-commit.css) contains some simple directives to set the text size and line spacing. I like the way it looks, obviously you're free to customize it any way you like.
### `theme/index-template.hbs`
This is a *copy* of the `index.hbs` file from the default theme built into `mdbook`, with the appropriate lines added to make the version information appear where and how I wanted it.
#### Copy the original file
There are two ways to get the original file:
* Download it from [the `mdbook` source code](https://github.com/rust-lang/mdBook/blob/master/src/theme/index.hbs). Before you download the file, sure to select the same tag as the version of `mdbook` you're running.
* Create a dummy book with the full theme files, and copy `theme.index.hbs` from there.
* In your new book, create a "`theme/`" directory. (You'll be copying a file into this directory in a bit.)
```
$ cd ~/git/newbook/
$ mkdir theme
```
* Create a dummy book with the theme files.
```
$ mdbook init --theme --ignore none --title x ~/dummybook
```
* Copy `theme/index.hbs` from the dummy book, into the `theme/` directory you just created. Give your copy the name "`index-template.hbs`".
```
$ cp ~/dummybook/theme/index.hbs theme/index-template.hbs
```
* You can delete the dummy book now if you like.
```
$ rm -rf ~/dummybook/
```
#### Edit your copy
```
$ nano theme/index-template.hbs
```
There are two places where you *could* make changes, depending on where you want the version info to appear within the pages.
* **You can add the version info at the bottom of the navigation bar on the left.** The info will be present when somebody is reading the book with a web browser, but will not be present if they print the document.
To do this, search for "`{{/toc}}`". You should find it within a "``" element that looks like this:
```html
```
Between the `{{/toc}}` and `` tags, add the new lines shown below.
```html
```
* **You can add the version info at the bottom of every page.** If you do this, the info will be present when somebody is reading the book with a web browser, AND will be present if they print the document. (I'm leaving this active for the template repo itself so people can see what it looks like, but I normally comment this out or remove it from my own books.)
To do this, search for "`{{{ content }}}`". You should find this:
```
{{{ content }}}
```
Between the `{{{ content }}}` and `` tags, add the new lines shown below.
```
{{{ content }}}
Generated
@VERSION_COMMIT_NOW@
@VERSION_COMMIT_HASH@
@VERSION_COMMIT_TIME@
```
Both of these changes are present in the `src/index-template.hbs` file in the template repo, so that people who see [the preview online](https://kg4zow.github.io/mdbook-template/) can see what they look like. If you don't want both of them, you may want to commment out or remove one of them.
### `book.toml`
The `[preprocessor.version-commit]` section in the `book.toml` file tells `mdbook` to run the `version-commit` script before generating the HTML output.
```toml
[preprocessor.version-commit]
renderers = [ "html" ]
command = "./version-commit"
```
* If you want to change how or where the git commit information appears within the pages, edit `theme/index-template.hbs`. (See above for examples.)
* If you want to change how the "version" or the timestamps are presented (i.e. to use a different "`git describe`" command to get the commit info, or to format the timestamps differently), edit the `version-commit` script. The script is written in Perl, however it's very *simple* Perl, and if you're familiar with other scripting languages (i.e. shell, Python, Ruby, etc.) you should be able to understand it, even if you aren't familiar with Perl itself.
This file also contains a declaration which makes `mdbook` include the `version-commit.css` file as part of the HTML files.
```toml
[output.html]
additional-css = [ "section-lines.css" , "version-commit.css" ]
```
This example has a second file in the `additional-css` list. This other file is used by the Section Lines feature, which is explained above.
# Pre-requisites
The following items need to be installed on the machine where you're going to be working. Note that the instructions below are meant for macOS and Linux. I don't use ms-windows -- if you're stuck with it, you can always use [VirtualBox](https://www.virtualbox.org/) and fire up a VM running Linux.
This is a *quick* list of what's required, I'm not going to go into a lot of detail about these.
### `mdbook`
Obviously.
* **macOS** using Homebrew: run "`brew install mdbook`".
* **Others**: [This page](https://rust-lang.github.io/mdBook/guide/installation.html) explains how to install `mdbook`.
### `git`
Also obvious.
Note that the workflow described above, for publishing to Github Pages using a totally "disconnected" `gh-pages` branch, requires `git` version 2.0.7 or higher.
* **macOS**: The `git` program is included as part of the XCode Command Line Tools. macOS comes with a `git` "stub" that will walk you though installing the XCode Command Line Tools if they aren't already installed.
You can also install `git` using Homebrew if you like, however you will need to check your `PATH` to be sure that Homebrew's binaries are seen *before* the `/usr/bin/` directory.
* **CentOS 7**: run "`yum install git`" as root.
* **Debian 10 and 11**: run "`apt install git`" as root.
* **Arch Linux**: run "`pacman -S git`" as root.
* **Others**: See the [Installing Git page](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) in the official `git` documentation.
### Perl and the JSON module
The `version-commit` script is written in Perl, and uses the JSON module to parse the data stream it receives from `mdbook`.
Perl itself is generally installed as part of the operating system, or available from the OS vendor's package repositories (i.e. `yum`, `apt`, etc.) On macOS, Perl is already installed.
The Perl JSON module contains code for Perl scripts to work with JSON files. Some operating systems may install this automatically as part of Perl itself -- if you run "`perldoc JSON`" and it shows you documentation, it's already installed.
* **macOS**: Perl is installed by the OS, run "`cpan install JSON`" to install the JSON module.
* **CentOS 7**: run "`yum install perl perl-JSON`" as root.
* **Debian 10 and 11**:
* Perl itself is installed by the OS.
* Run "`apt install libjson-perl`" as root to install the JSON module.
* **Arch Linux**: run "`pacman -S perl perl-json`" as root.
# Starting a new book from this repo
The "**TL;DR**" section at the top of this document walks through how to start a new book from this repo.
It's easier to direct you there, than it is to try and maintain two sets of directions for the same process.
# Working on your book
My normal workflow looks like this:
#### Start work
```
$ cd ~/git/newbook/
$ git fetch -p
$ git checkout main
$ git pull
$ bbedit .
$ make serve
```
ℹ️ I always start with "`git fetch -p`", "`git checkout main`", and "`git pull`" because I use several different computers to work on things, and running these commands before I start ensures that I'm starting from whatever I last pushed up to GitHub from any other machines.
The "`make serve`" command runs "`mdbook serve --open --hostname 127.0.0.1`". This does a few things:
* Generates the HTML files for your book.
* Starts up a web server, listening on `127.0.0.1:3000`.
* Opens a browser window pointing to `127.0.0.0:3000`.
* Watches the files in the book's directory. If it detects any changes to a file containing (or controlling) the book's content, it re-generates the HTML and signals the browser window that it opened, to refresh itself.
This lets you see a preview of what the finished product is going to look like, every time you save your changes.
#### Finished (for now)
When you're finished working on the book for now, hit CONTROL-C to exit from "`mdbook serve`", and then commit your changes.
```
$ git add .
$ git commit
```
If the book's git repo is stored on a remote service (like GitHub), push the changes.
```
$ git push
```
If the generated HTML is being hosted on a web site somewhere, or on GitHub Pages, push the updated HTML files to the web server.
```
$ make push
```
# License
Most of the content in this repo, including the `version-commit` script and the stylesheets, were written by myself and are licensed under the [MIT License](LICENSE.txt).
The `/theme/index-template.hbs` file in this repo was copied from `/src/theme/index.hbs` in [the mdbook source](https://github.com/rust-lang/mdBook/blob/master/src/theme/index.hbs) and then modified. As such, that file is technically covered by the Mozilla Public License 2.0, [as noted in their repo](https://github.com/rust-lang/mdBook/blob/master/LICENSE).
The files under `/src/` were originally *generated* using mdbook, and in the case of `/src/introduction.md`, modified after that. I'll be honest, I'm not sure if this means they're covered under my MIT license or mdbook's MPL license, but either way, I have no intention of going after anybody who wants to copy and use them, and I *seriously* doubt that the mdbook developers will either.
Enjoy.
*-jms1 2022-05-30*