Migrating from Jekyll to Wordpress
Published on
After a few years maintaining a blog in Jekyll (not this one, this one is in Hugo), I started wanting to migrate to WordPress on a VPS. The reasons were many:
-
the blog started growing in the number of posts, which made writing new ones more sluggish, due to Jekyll’s slowness and the lack of motivation that this slowness causes
-
a feeling of “immobility” from Jekyll, almost as if it isn’t evolving anymore
-
easier writing experience: sure, markdown works well, but it’s much more practical and quicker to open the admin panel in a browser and start writing the post directly in the format that will be seen in the end. Media handling and having everything centralized makes the act of writing easier and more straightforward
-
the reason above all: because I wanted to. It had been a while since I properly maintained a VPS running live, and I had grown too comfortable with the static site hosted on Netlify. I wanted to shake things up. Simple as that
The Plan
There are many plugins and tools going the opposite way, but few that help you migrate to WordPress, and most of them use the feed to generate content in the end (as described here). That works, but gives me less control than I like.
I was thinking about writing a script to insert directly into the WordPress database when I came across the wp utility. That made life a lot easier.
Relying on wp-cli, I put together the Ruby script below to generate the commands and files needed to import everything into WordPress.
require "time"
require "set"
if ARGV.size != 2
puts "Usage: ruby migrate_posts_from_jekyll.rb posts_folder output_folder"
exit 1
end
POSTS_DIR = ARGV[0]
OUTPUT_DIR = ARGV[1]
def starts_with_label?(line, label)
line.downcase.strip.start_with?(label)
end
def clean(line, label)
replacements = {
"#{label}: " => "",
"'" => "",
"\"" => "",
"–" => "-",
"“" => "\"",
"”" => "\"",
"\n" => "",
"/" => ""
}
line.strip.gsub(Regexp.union(replacements.keys)) { |match| replacements[match] }
end
commands = "#!/usr/bin/env bash \n"
categories_to_create = Set.new
Dir.children(POSTS_DIR).each do |filename|
full_path = File.join(POSTS_DIR, filename)
next if File.directory?(full_path) or filename.start_with?(".")
title = ""
date = ""
permalink = ""
categories = []
tags = []
content = ""
in_categories = false
in_tags = false
header_count = 0
File.foreach(full_path) do |line|
unless line.start_with?(" ")
in_categories = false
in_tags = false
end
case
when starts_with_label?(line, "title")
title = clean(line, "title")
when starts_with_label?(line, "date")
date = clean(line, "date")
when starts_with_label?(line, "permalink")
permalink = clean(line, "permalink")
when starts_with_label?(line, "categories")
in_categories = true
when starts_with_label?(line, "tags")
in_tags = true
when in_categories
value = line.sub("- ", "").delete("\"").gsub("'", "").strip
categories << value
categories_to_create << value
when in_tags
tags << line.sub("- ", "").delete("\"").gsub("'", "").strip
when starts_with_label?(line, "---")
header_count += 1
when header_count == 2
content += line
end
end
output_file_name = permalink + ".txt"
parsed_date = Time.parse(date).strftime("%Y-%m-%d %H:%M:%S")
puts "#{title} - #{parsed_date} - #{permalink} - #{categories.to_s} - #{tags.to_s}"
commands += "wp post create ./#{output_file_name} --post_author=1 --post_title='#{title}' --post_name='#{permalink}' --post_date='#{parsed_date}' --post_category='#{categories.join(', ')}' --tags_input='#{tags.join(', ')}' --post_status=publish --post_type=post \n"
File.write(OUTPUT_DIR + output_file_name, content)
end
categories_commands = "#!/usr/bin/env bash \n"
categories_to_create.each do |cat|
categories_commands += "wp term create category '#{cat}' \n"
end
File.write(OUTPUT_DIR + "categories.sh", categories_commands)
File.write(OUTPUT_DIR + "posts.sh", commands)Know that the metadata collection expects the Jekyll posts header to be in the following format:
---
id: 124
title: The Title
date: 2018-04-10T10:19:55-03:00
category: blog
author: Name SecondName
layout: post
permalink: /the-permalink/
categories:
- Category1
- Category2
tags:
- tag1
- tag2
---
TEXTAnd then you can run the script as following:
ruby migrate_posts_from_jekyll.rb content/_posts/ ../output/The command will (hopefully) generate the files categories.sh, posts.sh, and the individual posts .txt in the output folder. Notice the post creation line with the parameter –post_author=1. This must match the ID of the post author in WordPress.
With that in hand, just copy this folder into your WordPress installation and run categories.sh and posts.sh using “./” as is.
The only thing not covered here was the media part, because it depends a lot on how media was used throughout your blog’s lifetime. If there’s a pattern, just modify the content variable in the script to handle the way you insert images into your posts and convert them to the format WordPress expects. In my case, it was just a matter of copying the Jekyll media folder into the WordPress media folder and running the WP Media Sync plugin. Afterwards, I uninstalled the plugin and that was it.
And with that, the blog was migrated to WordPress.
(just kidding, I had several issues with DNS configuration, but that was my own fault and a topic for another post)
Reminder: before doing this kind of operation, make sure you have backups and keep your Jekyll repository data saved, as it might still be useful in the future.
Comments
I feel that comments on specific blogs have been dying down as the times goes. If you have any questions or want to talk about the post, contact me through the below links.