Generate Table of Contents with GitHub Actions
Tags: docker, devops, github, tutorial
Published: 2019-11-27 08:29:54

I love taking notes in Markdown and lately, I've been using [GitHub](https://github.com/) to write (and store) them.

There are many advantages of using GitHub: accessible on different platforms, online, offline, Markdown support, editor agnostic, versioning, simple structure (files/folders), and free.

However, one thing that is not supported out of the box is generating Table of Contents (TOC). With it, I can organize my notes differently and have many of them into a single Markdown file.

Here is how I generate the Table of Contents for my notes using [GitHub Actions](https://github.com/features/actions).

## Prerequisites

* Make
* Docker
* Compose
* GitHub account
* Git
* Terminal

## Implementation

My notes repository contains many files and folders but for the purpose of this blog post, I will keep everything as simple as possible. Below is the file structure of an example git repository.

```
$ tree -a
.
├── .git
│   ├── ...
├── Makefile
├── docker-compose.yml
├── story.md
└── package.json
```

The repository has one note written in Markdown called `story.md` which has some random content.

```Markdown
# Story

## Introduction

Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Praesent elementum tincidunt felis, condimentum accumsan nibh imperdiet nec.
Sed cursus justo eu purus rhoncus, id facilisis lorem semper.

## Development

Duis nunc arcu, bibendum quis turpis ut, dapibus feugiat massa.
Sed tempus tortor eget diam porta vulputate. Ut tempor iaculis suscipit.
Morbi scelerisque felis ac justo consectetur luctus.

## Conclusion

Nullam malesuada rutrum gravida. Vivamus sit amet fermentum odio.
Donec tincidunt ex odio, vitae convallis ante posuere nec.
```

Since generating the Table of Contents is not a default option, I searched for a generator and found [DocToc](https://github.com/thlorenz/doctoc), a NodeJS module.

> DocToc: Generates table of contents for markdown files inside local git repository. Links are compatible with anchors generated by github or other sites.

To use DocToc, I created `package.json` file and added the module as a development dependency.

```json
{
  "name": "mynotes",
  "devDependencies": {
    "doctoc": "^1.4.0"
  }
}
```

I often use Docker to ensure consistency between different platforms like, in this case, my laptop and GitHub. In the `docker-compose.yml` file, I defined a service named `node` which will be used to install DocToc and generate the TOC using the latest NodeJS Docker image.

```yml
version: '3.7'
services:
  node:
    image: node
    volumes:
      - type: bind
        source: "."
        target: /opt/app
    working_dir: /opt/app
```

The last file is `Makefile` which encapsulates the Docker Compose commands. The `deps` target downloads the NodeJS Docker image and installs DocToc. The `generateTOC` target generates the Table of Contents with DocToc inside a NodeJS Docker container.

```Makefile
deps:
	docker-compose pull node
	docker-compose run --rm node yarn install

generateTOC:
	docker-compose run --rm node yarn doctoc .
```

Finally, to include the TOC to my notes, I simply run `make deps generateTOC` in a terminal. The `story.md` file is now updated.

```Markdown
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*

- [Story](#story)
    - [Introduction](#introduction)
    - [Development](#development)
    - [Conclusion](#conclusion)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

# Story

## Introduction

Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Praesent elementum tincidunt felis, condimentum accumsan nibh imperdiet nec.
Sed cursus justo eu purus rhoncus, id facilisis lorem semper.

## Development

Duis nunc arcu, bibendum quis turpis ut, dapibus feugiat massa.
Sed tempus tortor eget diam porta vulputate. Ut tempor iaculis suscipit.
Morbi scelerisque felis ac justo consectetur luctus.

## Conclusion

Nullam malesuada rutrum gravida. Vivamus sit amet fermentum odio.
Donec tincidunt ex odio, vitae convallis ante posuere nec.
```

Et voilà! I then commit my changes to `master`, push, and view them on GitHub.

This works well on a computer where tools like Make, Docker, and Compose are installed. I could even go furter and automate the process with [Git Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks).

However, what if I write on a tablet or directly on GitHub website? The Table of Contents will likely get out of date. Wouldn't be great to get it generated no matter where the notes are written from? GitHub Actions to the rescue!

## GitHub Actions

> [GitHub Actions](https://github.com/features/actions) makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub. Make code reviews, branch management, and issue triaging work the way you want.

Below is the structure of the example git repository with GitHub Actions included.

```
$ tree -a
.
├── .git
│   ├── ...
├── .github
│   └── workflows
│       └── generate_toc.yml
├── Makefile
├── docker-compose.yml
├── story.md
└── package.json
```

I defined a workflow in the `.github/workflows/generate_toc.yml` file to generate the Table of Contents whenever I push to `master` branch.

```yaml
name: Generate TOC
on:
  push:
    branches:
      - master
jobs:
  generateTOC:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Generate TOC
      run: make deps generateTOC
    - name: Auto commit
      uses: stefanzweifel/git-auto-commit-action@v2.1.0
      with:
        commit_message: Apply automatic changes - TOC generated
        branch: master
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

This workflow checks out the repository on a `master` branch push event, runs the command `make deps generateTOC` to generate the Table of Contents, and finally, commits the changes automatically to `master` with the GitHub Action named [git-auto-commit-action](https://github.com/marketplace/actions/auto-commit-changed-files).

From the GitHub repository, I can see my Generate TOC workflow being executed.

![GitHhub Workflows][linkGitHubWorkflows]

And see the automated commit with the Table of Contents updated from the workflow right after I updated my notes.

![GitHub Commits][linkGitHubCommits]

## Wrapping Up

I am more than happy that GitHub Actions supports Make, Docker, and Compose. I could develop and test locally some parts of my workflow, and feel confident that they will work on GitHub. And now, I can write my notes from anywhere and get the Table of Contents magically updated. This is a simple use case but there are many Actions that can just be "plug & play" to create more sophisticated workflows.

---

<br/>

Liked this blog post? Checkout [3musketeers.io](https://3musketeers.io) to learn more about how to build, test, and deploy your apps from anywhere, the same way!

<br/>

_Disclaimer: Do your own research before using this solution and do not store sensitive information in plain text on GitHub._

[linkGitHubWorkflows]: https://thepracticaldev.s3.amazonaws.com/i/wbfkk9nykth2mnvz362n.png
[linkGitHubCommits]: https://thepracticaldev.s3.amazonaws.com/i/aouej6wbteq53ovfgq86.png