Adding changelogs to blog posts

This week I published a post (a collection of today-I-learn nuggets) that I might further update in the future. For the updates I'd love to add a changelog to the post bottom, and also add the last updated to the post top. However: I store posts in Markdown with a frontmatter; and the frontmatter contains the date field; and the one field is not sufficient to capture the changes. So here's how I changed the frontmatter and how I migrated existing posts.


Storing the changelog

First, I discovered that Markdown frontmatters are, in fact, full-fledged YAML. So I can store a complex data structure, such as a list of maps in there. I think this is a really cool feature of frontmatters. Here's a new frontmatter shape:

title: "Typescript & NodeJS: Collection of TILs"
changelog: 	# 🎉
  - date: 2021-03-30
    change: postUpdated
    detail: >
		Added 3 TILs: absolute imports in Webstorm,
		const assertions and
		checking string literal types at runtime
  - date: 2021-03-29
    change: postPublished
# ... rest of the frontmatter ...

# ... content of the post...

Updating the static-site generator

I use NextJS for this blog. NextJS is a React-based static site generator. I use NextJS together with Typescript. I have an interface representing a post, so I expanded it as follows:

interface ChangelogItem {
  date: string // string because of NextJS serializion
  change: "postPublished" | "postUpdated"
  detail?: string

interface Post {
  changelog: ChangelogItem[]
  publishedOn?: string
  lastUpdatedOn?: string
  // ...

I updated the code that parses the frontmatter and also updated the React component that generates the site.

Migrating existing posts

Ok, so that should do for new posts. However, I have around 50 existing posts with a, now obsolete, date field:

title: "A post with an obsolete date"
date: 2021-03-30 # <----- this needs to be migrated to a new shape
tags: engineering
Lorem ipsum dolor

What I needed is to migrate the old frontmatters into a new shape. So I summed my favorite awk to do the heavy lifting:

# content of migrate-to-change-log.awk
BEGIN { delims = 0 }
# count the delimiters to determine if we are inside the frontmatter
/---/ { delims++; }

  if (delims == 1 && /^date:/) {
    printf "changeLog:\\n  - date: %s\\n    change: postPublished\\n",$2
  } else {

Once I had the awk script down, I migrated all posts with:

echo *.md | xargs -n1 gawk -i inplace -f migrate-to-change-log.awk

Here's an outcome on the sample post:

title: "some title"
changeLog: # 🎉🎉🎉
  - date: 2021-03-30
    change: postPublished
tags: engineering
Lorem ipsum dolor

Using git instead?

When writing this post, it occurred to me that instead of the frontmatter, git can also be used as a data source for the changelog. For this to work, I would have to exclude refactoring commits, which change the post structure. For example, a commit that replaces the date field with the changeLog field. Or a commit that moves the file around. I'll keep this idea in the background mind for now.

Would you like to connect? Subscribe via email or RSS , or follow me on Twitter!