Adding a Changelog to My 11ty Blog

Ever since I saw this feature on Søren Birkemeyer's blog, I’ve been envious. He wrote about his method of accomplishing this, but it was a little bit intimidating, to be honest. There are steps and technologies I don’t understand (like Perl 😳), and I wanted to find an easier way, a way with fewer moving pieces.

Søren maintains a json version of his git history within his repository, but I hope to avoid that. I made a Feedbin favorites plugin that uses a similar technique, and it adds a lot of complexity, complexity that I don’t want to maintain. The code behind this blog is already a mess in many areas, and I don’t want to make it any worse with a shiny new "unnecessary" feature!

So that’s the goal: create a Changelog with few moving pieces, make it as simple as possible, and learn a few things along the way. Let's see how we can do that.

What are we building? #

If you've never seen a changelog on a blog before, here's the changelog for this page:

Changelog
  • Bug fix for inline changelog
  • Publish new post about my swanky new changelog

And here's the code I used to create that:

{% changelog filePath=page.inputPath %}

It’s a list that includes all modifications made in my git repository for this file. Why do we need this? A few reasons:

  • Transparency. I want my writing to be evergreen in a way, always updated to the best of my ability, and I want people to know that I’m always improving things around here.
  • Clarity. Updating a technical article should be akin to updating a plugin: if people use your information, they deserve to know why it changed, so that they can update their code if need be. You don't have to use a changelog for this, but it helps prevent laziness.
  • I’m also a big fan of learning in public, and this makes it easier for me to do that. Hopefully this means more blog posts and less perfectionism!

Setup #

My first thought was that there must be a node plugin that would allow you to access git history without cloning it, so I did a little digging, and eureka! I found a package called gitlog. This looks perfect for my purposes. To install it in our repo, we'll need to run this command in the terminal/command line:

npm install gitlog --save

And include it at the top of our .eleventy.js file:

const gitlog = require("gitlog").default;

As long as you already have an 11ty blog, this should be all the setup you need.

Implementing the shortcode #

Here's the idea. 11ty has a feature called shortcodes, they allow you to extend template languages with your own functionality. This allows me to use the syntax I noted above:

{% changelog filePath=page.inputPath %}

I like this because my post templates are already pretty crowded, so it’s nice to have a small one-liner for my changelogs. We'll do all the heavy lifting elsewhere.

One important thing to note: page.inputPath is a bit of Eleventy Supplied Data: it is the actual path to the source file (for example, on this page that data is ./posts/2020/adding-a-changelog-to-my-11ty-blog.md). We will use that momentarily.

Next we need to set up the shortcode, and figure out how to use the gitlog plugin. I use Nunjucks as my templating language, so that’s what I will use here, but you could adapt this shortcode to whatever language you like (the beauty of 11ty!)

Here is my finished shortcode. You should be able to copy/paste this into your own .eleventy.js file if you like. I have heavily commented it, and I’ll go into more detail below:

// Setting up the changelog shortcode
eleventyConfig.addNunjucksShortcode("changelog", ({filePath}) => {
// First we remove "./" from filePath
// This leaves us with the format "posts/2020/file.md"
var relPath = filePath.slice(2);
// Limit logs to 20, only fetch commit message and date
const options = {
repo: __dirname,
number: 20,
fields: ["subject", "authorDate"],
file: relPath
};

// Here's where the magic happens!
// We pass our params into gitlog, and it handles the rest
var commits = gitlog(options);
// Now we need to loop through the commits, and create our HTML.
// I use a list and a <details> element here,
// but you could use whatever markup you want!
var html = "<details><summary>Changelog</summary><ul>";
for (var i=0; i<commits.length;i++) {
// Convert the git date to ISO
var isoDate = commits[i].authorDate.slice(0,10);
// Convert ISO to readable date e.g. "May 05 2020"
var readableDate = DateTime.fromISO(isoDate).toFormat('LLLL dd yyyy');
html += `<li><time datetime="${isoDate}">${readableDate}</time> ${commits[i].subject}</li>`;
}
html += "</ul></details>";
return html;
});

A few things to note here:

  • We’re using a small slice of the functionality that gitlog provides. I am only fetching the commit message and the date, but there are many other things we could use: name, email, hash, body, you name it, you can get it. Look on GitHub to see all the options.
  • Thanks to the filePath parameter you don’t have to use the current page. You can use whatever page you want. This makes the utility much more flexible.
  • You could add another parameter to change the text to something other than "Changelog". This would make it even more flexible.
  • I decided to put the markup for this changelog in the shortcode itself. This isn't the easiest thing to read, but I don’t expect to change it much, so I’m happy with it. If you wanted to, you could make a separate template file for this, but I decided to go with simplicity in this case.

Conclusion #

All in all I’m pretty happy with how this turned out. It’s a surprisingly small amount of code, it doesn't seem to increase build times by much, and it outputs a clean and accurate history of how each blog post has changed over time. Goal accomplished!

In the future, it would be nice to add a way to view the changes themselves rather than only the dates and messages, but that’s a project for another day.

← Home

Changelog
  • Bug fix for inline changelog
  • Publish new post about my swanky new changelog