Transition to Pelican

Wed 06 June 2018 by kroche in Meta

During the spring semester, I decided to transition my site from Pico to a static site generation framework, for a few reasons:

  • Reduced hosting costs. My site is now hosted on GitHub Pages, which is free. Even on my own infrastructure, I would need very little system resources to host the site.

  • Reduced attack surface. PHP or other backend platforms are not needed with a statically generated site, as templates are used only at compile time, unlike CMSs where they are evaluated for each request. Without any backend logic to exploit besides the web server itself, the site is more secure.

  • Faster load times and better resiliency. Even a minimal CMS like Pico has greater backend overhead than static HTML. Thanks to the lack of PHP overhead, my site loads much faster, and it is much less likely to be overloaded by a sudden surge in traffic.

In particular, I chose Pelican as my site generator, because:

  • Pelican is written in Python, which is easily extensible and is a language I'm familiar with.

  • Pelican's template engine is Jinja2, which is syntactically very similar to Twig, Pico's template engine. Transitioning from Twig to Jinja2 required only minor modifications to the site's template code.

As of mid-May, this transition is complete. I am very satisfied with the results. Given the recent acquisition of GitHub by Microsoft, I may seek another hosting solution depending on what Microsoft does with the platform. For the time being, GitHub Pages works fine; the other benefits of static site generation remain, in any case.

The first step in the transition process was to design a new site theme. I could have used the old one, but I wanted a fresh look for the site. I took some design cues from the online git reference, but changed some colors and fonts and designed my own flexbox-based layout.

Step Two was to take the pieces of the old site templates that I wanted to hold onto and convert those from Twig to Jinja2. This was mostly painless, except for the archive code, which relied on variables outside the loop scope that were changed from within each loop iteration. Jinja2 does not allow variables to be changed from anywhere but their own scope, which means changing a variable declared outside a loop from inside a loop is not possible without some hacking. I accomplished this using garbage variables inside the loop. For example:

{% set year = 0 %}
{% for article in dates %}
    {% set year = article.date|date('Y') %}
{% endfor %}

Line 3 in the above code would modify the year variable in Twig, but not in Jinja2 since the statement and the variable are not in the same scope. That statement and similar statements would become the following in Jinja2:

{% set year = [0] %}
{% for article in dates %}
    {% set _ = year.append(article.date.strftime('%Y')) %}
{% endfor %}

There are some other minor differences between Twig and Jinja2, but that was the only thing that caused me any headache. I don't understand the reasoning behind that design decision, so if someone can explain to me why the Jinja2 team made it, I would be very curious to hear.


Tags: None