My goal here is to dynamically generate pages like /categories/code/, that would list all articles categorized in code. As far as I can tell, there’s no standard way. I found a few articles explaining how to create such pages for a given category, but none regarding how to auto-generate them for all your categories.

For this purpose, I resorted to some scripting, which through the power of CI, is completely transparent.

The process is:

  1. Create layout for category page
  2. Generate a JSON file containing the list of categories.
  3. Generate a page for each category using the layout in 1.
  4. jekyll build

Layout

The layout I used ressembles a lot the layout of the home page. In the _layouts folder of Jekyll, I created this file:

---
layout: default
---

<div class="home">

    &#123;&#123; content &#125;&#125;

    <h2>About "&#123;&#123;page.category&#125;&#125;"</h2>

    <ul class="post-list">
        &lcur;% for post in site.categories.&lbra;page.category] %&#125;
        <li>
            <a href=""><span class="post-title">&#123;&#123; post.title | escape &#125;&#125;</span><br/>
                <span class="post-meta">&#123;&#123; post.date | date: "%Y-%b-%-d" &#125;&#125; | &#123;&#123; post.categories | array_to_sentence_string &#125;&#125;</span>
            </a>
        </li>
        &#123;% endfor %&#125;
    </ul>
</div>

This basically iterates through the posts of a category contained in property category of a given page using this layout.

Category list

In my assets folder, I created an assets.json file with the following contents:

---
---
{
  "categories": [
    
      "Personal development",
    
      "Continuous improvement",
    
      "Sport",
    
      "Career",
    
      "Psychology",
    
      "Code",
    
      "Reading notes",
    
      "Business",
    
      "Delivery",
    
      "Process",
    
      "Code like a monkey",
    
      "Hiring",
    
      "Design patterns",
    
      "Organization",
    
      "Self improvement",
    
      "Management",
    
      "Source control",
    
      "Devops",
    
      "Coffee script",
    
      "Functional programming",
    
      "Node.js",
    
      "Slack",
    
      "WIP",
    
      "Azure",
    
      "Bot",
    
      "Security",
    
      "Flow",
    
      "Small batches",
    
      "Product management",
    
      "CI",
    
      "AWS",
    
      "Physics",
    
      "KSP",
    
      "Azure functions",
    
      "Space",
    
      "Lambda",
    
      "Git",
    
      ".Net",
    
      "Jekyll",
    
      "Webapps",
    
      "Speed",
    
      "Agile",
    
      "DevOps",
    
      "Smells",
    
      "Memo",
    
      "Architecture",
    
      "VSTS",
    
      "Quick note",
    
      "Software",
    
      "Coding",
    
      "Geek",
    
      "Linux",
    
      "Windows",
    
      "Development",
    
      "Authentication and authorization",
    
      "App Services",
    
      "Cloud",
    
      "Other",
    
      "API Management",
    
      "Tools",
    
      "Self organization",
    
      "Performance",
    
      "expload",
    
      "Engineering",
    
      "Microservices",
    
      "Modernization",
    
      "Refactoring",
    
      "Rearchitecting",
    
      "Quick post",
    
      "Systems",
    
      "Game",
    
      "Kubernetes",
    
      "Containers",
    
      "Patterns",
    
      "Docker",
    
      "AKS",
    
      "StuffIBuilt",
    
      "Random",
    
      "Automation",
    
      "Social sciences",
    
      "Documentation",
    
      "LogicApps",
    
      "Lean",
    
      "Product Flow",
    
      "Product Management",
    
      "Marketing",
    
      "Build",
    
      "Sociology",
    
      "History",
    
      "SOPs",
    
      "Opinion",
    
      "Training",
    
      "business",
    
      "priorities",
    
      "meetings",
    
      "Productivity",
    
      "biology",
    
      "code",
    
      "Goals",
    
      "Communication",
    
      "GTD",
    
      "organization",
    
      "workflow",
    
      "Continuous Improvement",
    
      "Peculiar Ways",
    
      "Work",
    
      "Happiness",
    
      "development tools",
    
      "goals",
    
      "principles",
    
      "leadership",
    
      "management",
    
      "sociology",
    
      "common sense",
    
      "bias",
    
      "psychology",
    
      "engineering",
    
      "I am a pedantic ass",
    
      "semantics",
    
      "machine learning",
    
      "hide",
    
      "pattern",
    
      "law",
    
      "Shit post",
    
      "Statistics",
    
      "Interviewing",
    
      "Human Management",
    
      "Planning",
    
      "delivery",
    
      "process",
    
      "product management",
    
      "Human management",
    
      "Philosophy",
    
      "Time management",
    
      "Policy",
    
      ""
  ]
}

The --- tell Jekyll to build the file. The rest is just simple liquid. When building, the result looks like this.

Generate a page for each category

The page is plain and simple:

---
layout: category
category: Code
---

We just need to generate one for each in a categories folder, so that they can be accessed through /categories/code/.

Let’s start with a simple node script running through the categories.json:

const path = require("path");
const fs = require("fs");

const categoryFile = path.join("_site", "assets", "categories.json");
const categoryFolder = path.join("categories");

const categoryContent = fs.readFileSync(categoryFile).toString();
const deserializedCategories = JSON.parse(categoryContent);
const categories = deserializedCategories.categories;

categories.forEach(function(category) {
  if (category === "")
    return;
  const content = "---\nlayout: category\ncategory: " + category + "\n---";
  const file = path.join(categoryFolder, category + ".md");
  fs.writeFileSync(file, content);
});

I then invoke that in my build script:

#!/usr/bin/env bash

function build {
    if [ "$1" != "--refresh" ]; then
        jekyll build --incremental
    else
        jekyll build
    fi
}

function updateCategoryPages {
    if [ -d "categories" ]; then
        rm -rf categories
    fi
    mkdir categories
    node build/updateCategories.js
}

build
updateCategoryPages
build

The first build is going to generate the categories.json file. Then we iterate through it and generate files. Then we build again.

Having my build done on Codeship, all I need to do is push and this is done automagically.