Migrate Blog to Hexo

This post is a celebration for the migration from Octopress to Hexo

Octopress has done an excellent job on filling the gap between jekyll and full function repo based blog engine. But because of the tech stack it is based on. It isn’t really a awesome framework to use.

The first time I got pissed off by Octopress was by the end of 2012. Then I come up the idea to rewrite it in Node.js. But I wasn’t able to make it happen, because I was held by the new project assignment, and didn’t have too many spare time on the blog engine.

To save effort, I began to customize Octopress by rewriting some code in Octopress and Jekyll, which started the long march of Octopress customization.

I did a number of customization on Octopress, from erb template to Jekyll generators, from Rake script to TextMate bundles.

Before I switch to Sublime, I uses TextMate for quite long time. So I customized the Rake script and TextMate bundle, which enables me to invoke almost every rake command in TextMate with hotkey. I can even rename the blog post file name according to the title in front-matter without leaving TextMate. Besides the functionality, I also customized the templates and the widgets a lot to get better visual effects and reading experience.

I’ve benefited from these customization a lot. On the other hand, these deep customization blocks me from migrating to Hexo, a better alternative. Even I have found Hexo in the early 2013, and believe it is a better blog platform. But it is really costy for me to migrate the blogs away from the Octopress.

Luckily, after years development, a bunch of tools and libraries came up, which has minimized the gap between Octopress and Hexo.
After several days effort, finally retired the Octoress engine, and completed the journey of moving from Octopress to Hexo.

Upgrading DSL from CoffeeScript to JSON: Part.1. Migrator

I’m working on the Harvester-AdKiller version 2 recently. Version 2 dropped the idea “Code as Configuration”, because the nature of Chrome Extension. Recompiling and reloading the extension every time when configuration changed is the pain in the ass for me as an user.

For security reason, Chrome Extension disabled all the Javascript runtime evaluation features, such as eval or new Function('code'). So that it become almost impossible to edit code as data, and later applied it on the fly.

Thanks to the version 1, the feature and DSL has almost fully settled, little updates needed in the near future. So I can use a less flexible language as the DSL instead of CoffeeScript.

Finally I decided to replace CoffeeScript to JSON, which can be easily edited and applied on the fly.

After introducing JSON DSL, to enable DSL upgrading in the future, an migration system become important and urgent. (Actually, this prediction is so solid. I have changed the JSON schema once today.) So I come up a new migration system:

class Upgrader
constructor: ->
execute: =>
console.log "[Upgrader] Current Version: #{Configuration.version}"
migrationName = "#{Configuration.version}"
migration = this[migrationName]
unless migration?
console.log '[Upgrader] Latest version, no migration needed.'
console.log "[Upgrader] Migration needed..."
migration.call(this, @execute)
'undefined': (done) ->
console.log "[Upgrader] Load data from seed..."
'1.0': (done) ->
console.log "[Upgrader] Migrating configuration schema from 1.0 to 1.1..."
# Do the migration logic here

The Upgrader will be instantiate when extension started, after Configuration is initialized, which holds the DSL data for runtime usage.

When the execute method is invoked, it check the current version, and check is there a upgrading method match to this version. If yes, it triggers the migration; otherwise it succeed the migration. Each time a migration process is completed, it re-trigger execute method for another round of check.

Adding migration for a specific version of schema is quite simple. Just as method 1.0 does, declaring a method with the version number in the Upgrader.

'undefined' method is a special migration method, which is invoked there is no previous configuration found. So I initialize the configuration from seed data json file, which is generated from the version 1 DSL.

The seed data generation is also an interesting topic. Please refer to next post(Redfine DSL behavior) of this series for details.