Migrating From Middleman to Another Static Site

For about a year I’ve been flirting with leaving behind Middleman and trying another static site generator.

While I still haven’t done it, I’ve been slowly documenting the hacks and tricks that build my current website, so that I’ll be able to preserve the same URLs and such.

I’ve taken a couple of stabs at some of the other generators, usually by making a branch, deleting a bunch of code, and trying to hot-change out the underlying engine from middleman. It’s fun and quick, but as soon as I want to write another blog post or make a change the branch falls behind. To combat this, I quickly threw together a migration script the other weekend.

The script isn’t particularly clever, but I wanted to use it as an excuse to point out some good practices for migration style scripts.1

# MIGRATION SCRIPT
# ----------------
#
# The purpose of this is to pull the content out of my existing site into a
# format that makes sense for a new static site generator.
#
# As it is taking me a while to decide which one I want to try, having
# something that I can "live" update will be _very_ important.

You should use comments… I know there’s a lot of debate about whether code should document itself, but I often find that a one-file script doesn’t have enough context to really document itself, so writing some pseudocode in comments can help guide your process.

require 'fileutils'
require 'date'

SRC = "../evantravers.com/source"
DST = "./content/"

# Reset
if File.directory?(DST)
  FileUtils.remove_dir(DST)
  FileUtils.mkdir(DST)
end

You should make your script repeatable and non-destructive. The above lines set a SRC and DST directory that are different… so any changes won’t damage the SRC.

In addition, each time the script is run it deletes the whole generated folder. While this is “expensive” it reduces weird side effects that can cause confusion. (For instance, if I change the format of a filename, then I could end up with two versions of the same file.)

Really, if you do anything, do that. You will thank me someday.

The rest of the script does some massaging and renaming of files to remove some of the Middleman specific naming and formatting.

# Middleman requires some weird naming conventions... it'd be nice to be able
# to remove those.
def clean_filename(str)
  File.basename(str)
    .gsub(/\d\d\d\d-\d\d-\d\d-/, "")
    .gsub(".html", "")
    .gsub(".markdown", ".md")
end

# Move over the articles
# URL format: /articles/YYYY/MM/DD/slugified-title/
FileUtils.mkdir_p(File.join(DST, "articles"))
Dir.glob("#{SRC}/articles/*.{md,markdown}").each do |file|
  date = Date.parse(file)
  path = File.join(DST, 'articles', date.year.to_s, date.strftime('%m'), date.strftime('%d'))
  FileUtils.mkdir_p(path)
  FileUtils.copy_file(
    file,
    File.join(path, clean_filename(file))
  )
end

This is the bulk of the work… I just wanted bare markdown files in a directory structure that matched some of the static generators I’ve been looking at.

Middleman generates articles (middleman article "<name of article>") in your configured location for articles with the filename of YYYY-MM-DD-slugified-name-of-article.html.markdown.

I have no clue why the .html.markdown, I think it’s a remnant of some parser or other. I would prefer to store my content with a simple .md, so that’s what I do here.

In the end, the above code transforms:

.
├── articles
│  ├── 2020-07-20-migrating-from-middleman-to-another-static-site.html.markdown
│  └...
└...

to

.
├── articles
│  ├── 2020
│  │  ├── 07
│  │  │  ├── 20
│  │  │  │  └── migrating-from-middleman-to-another-static-site.md
│  │  └...
│  └...
└...

The best part… if I ever change my mind, I can adjust the script and the name and folders will change appropriately. 👌

# Move over the images
# URL format: standard (based on folder)
FileUtils.cp_r("#{SRC}/images/", DST)

Pretty boring call out to cp -r here… only note is the consistent use of DST constant to avoid mistakes.

# Move special essays and pages
# URL format: /name-of-file/
[
  "git.html.md",
  "about-me.html.md",
  "humans.txt",
  "thanks.html.md"
].each do |special|
  FileUtils.copy_file(
    File.join(SRC, special),
    File.join(DST, clean_filename(special))
  )
end

# Move over style assets
# These are currently .scss files
FileUtils.cp_r(
  File.join(SRC, "css"),
  File.join(DST, "style")
)

I don’t want to grab every file that Middleman uses, so I specifically grab some content files that I would want to bring over to a new site. I can use my same clean_filename method to remove that weird .html.md file extension.

This script is pretty ugly and foolish, and you could write or re-write this in any language that you choose, but I wanted to point out these basic principles: Make it non-destructive and make it repeatable. Because of these principles, the next time I run the script (after this post!) my experimental playground for new static site generators will be automatically updated with this very post, cleaned up and placed in the right folder. 👍 If there are any useful scripting principles that I missed, feel free to reach out on twitter, and I’ll consider including them.


  1. I’ve got a post about a migration script for plain-text notes to Zettelkasten format that will go into this more deeply. 


Changelog
  • 2020-07-20 15:15:22 -0500

    Add folder for day

  • 2020-07-20 14:31:28 -0500

    Give example of the transformation

  • 2020-07-20 11:51:19 -0500

    New Post: migration scripts