I spent the day porting my blog from a hacked-together homegrown static generator using Python and Jinja to Pelican, a free software static generator using Python and Jinja. The result should be at least 90% invisible, which is partially a result of the shared foundations, and partially a testament to good, flexible design on the part of Pelican.

Among the changes that crucially did not happen were the following:

  • URLs are all the same. I think the risk of anyone carrying around links to my nigh-untrafficked blog that might rot is minimal, but it's the principal of the thing. URLs are never supposed to change without at least leaving behind an HTTP 301. Mostly, Pelican made this straightforward, with easily overridable paths for most page types; the biggest hassle was with the error pages, which are stored under a subdirectory. I ended up having to override the output path with save_as metadata.
  • Article content did not change. I was easily able to port articles from their previous dubious HTML-embedded-in-YAML format to Pelican's HTML article format easily; it would have been straightforward to automate if I'd had more than five articles to port.
  • Page content, for the most part, did not change. I was able to straight port the vast majority of my preexisting templates with most needing only variable name changes. There are doubtless some minor changes e.g. to whitespace, and I made a few enhancements while I was at it, but page structure should be very, very similar.

Changes that are noticeable include the following:

  • I've got Atom and RSS feeds now.
  • There's pagination on the index page.
  • There are now article counts under tags (thus exposing them as mostly not very useful…).
  • I updated the about page. Pelican didn't make me do that, but I figured it should be up to date and wanted to link the feeds for people whose clients don't pick up on the link elements.
  • Articles now recognize themselves in the “Recent Articles” section to replace their link with an empty anchor. I'm almost certain nobody noticed that functionality was missing (I didn't).

Overall, I'm pretty happy. I found Pelican's design to be satisfyingly configurable; I'm especially impressed that I was able to port my site, in a single day, as cleanly as I was. The content porting was largely enabled by the fact that both source and target generators used Jinja as their templating engine, but I could easily imagine maintaining the structure to have been difficult or impossible, and it wasn't.

Mostly for the sake of documenting them, there were a handful of difficulties:

  • The aforementioned error pages. They're regular pages, to take advantage of the regular page machinery (though they arguably shouldn't be, since they may get served at any URL and the relative links probably won't work right), but they live under a subdirectory. This structure proved difficult enough to maintain that I settled for per-page overrides, since there's not very many of them. Templating them separately is probably a better approach.
  • The page render timestamp (which you can see below) was pointlessly difficult to create. (I find this sort of thing extremely useful as a user—has it really been two years since the last update, or is the site still getting attention, if not new content?) Did you know that Python's datetime.utcnow() method returns a timezone-unaware object, even though it obviously knows exactly what timezone it's in? Also, the whole datetime API is needlessly painful, and Python has helpfully decided to permanently lock all their misdesigns into Python 2—but those are both rants for another time. As currently implemented, that timestamp is an awful hack, about which we'll say no more.
  • Having a fold-style article summary (where some amount of the beginning of the article serves as the “summary”) isn't natively well-supported. I was easily able to find a plugin to provide this.