How to run Python software that is twelve years old
August 11, 2024 | View Comments
Back in the day when blogs were all the crazy people like me tended to change blog software faster than their underwear. (Yes there were issues with hygiene too.) Obviously changing your blog software is problematic. If you care about your old posts still available at the same URL, then have fun with writing rewrite rules between how your old software liked to organize your posts into folders and your new one. Or consider the problem of migrating integrated comments.
And consider that I'm actually pretty fond of my homepage's setup. I have HTML files in a repo that when checked in get automatically published via Netlify. In the case of my blog that means writing articles in reStructured Text, rendering them with Blogofile, checking both the source files and the rendered HTML into my Gitlab repository, and then seeing the changes online in a matter of seconds. Yes, mind you, I would've maybe used GitHub pages or Jekyll if those had been available when I started this blog. Although for reasons of privacy and control over my web content, I still prefer owning my own platform.
So, given that blogs aren't all the craze any more, it would only make sense that people don't change their blog software so often anymore. And so here's a short story of how I decided to fork Ryan McGuire's Blogofile static site generator that I started to use in 2009, and that became unmaintained about ten years ago, to make it work in a modern environment.
The main ingredient of making this work is as you would have guessed Docker. Let's take a look at the Dockerfile that I use to build Blogofile in, line by line:
# Use Ubuntu 20.04 as the base image FROM ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive # Install Python 2.7, development tools, and curl RUN apt-get update && \ apt-get install -y \ python2.7 \ python2-dev \ ca-certificates \ build-essential \ curl
We're basing the image off of Ubuntu 20.04, which at this time still has a maintained package repository. I think it might not see security updates anymore, but since we're dealing with static site generation, i.e. we're never running this image anywhere close to a server, I guess we're fine having the odd security vulnerability inside the Docker image. (I actually got a little nervous after writing this.)
Next, what's great about Ubuntu 20.04 as a choice is that its repository comes with Python 2.7 and so installing that including certificates is a breeze using apt-get as per above.
Let's look at the next few lines. Ubuntu 20.04 suprisingly doesn't ship with Pip for Python 2.7, and so we have to grab that from the web and install it via a script:
# Install pip for Python 2.7 RUN curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py && \ python2 get-pip.py
The notable thing about the remaining lines of our Dockerfile is that Blogofile 0.7 (which is the version I use) didn't come with a set of pinned requirements. But luckily there was a branch of Blogofile called plugins or 0.8 that introduced pinned requirements, and so we lift the requirements from there and put them into our Dockerfile:
WORKDIR /app COPY . . # Install Python dependencies RUN pip2 install \ BeautifulSoup==3.2.2 \ Jinja2==2.6 \ Mako==0.7.3 \ Markdown==2.3 \ MarkupSafe==0.15 \ PyYAML==3.10 \ Pygments==1.6 \ docutils==0.10 \ pytz==2013b \ six==1.2.0 \ textile==2.1.4 \ Unidecode==0.04.12 # Install the package defined in setup.py RUN python2 setup.py install
I honestly didn't expect that all those requirements would still exist and that they would still be installable from PyPI. But it just works.
The remaining part of the magic is a shell wrapper script that allows us to call the /usr/local/bin/blogofile script that's now installed in the container and call it as if it was a script installed in our host system. We call this script run-blogofile.sh:
#!/bin/bash # Define the Docker image name IMAGE_NAME="blogofile" # Define the local directory to mount LOCAL_DIR="$(pwd)" # Define the container's working directory CONTAINER_DIR="/app" # Run the Docker container with volume mounting docker run --rm -it \ -v "$LOCAL_DIR:$CONTAINER_DIR" \ "$IMAGE_NAME" \ /usr/local/bin/blogofile "$@"
So after building the Docker image:
docker build -t blogofile .
We can run this script like so:
$ ./run-blogofile.sh --help usage: blogofile [-h] [-s DIR] [--version] [-v] [-vv] {help,build,init,serve,info} ... positional arguments: {help,build,init,serve,info} help Show help for a command. build Build the site from source. init Create a new Blogofile site from an existing template. serve Host the _site dir with the builtin webserver. Useful for quickly testing your site. Not for production use, please use Apache instead ;) info Show information about the Blogofile installation and the current site. optional arguments: -h, --help show this help message and exit -s DIR, --src-dir DIR Your site's source directory (default is current directory) --version show program's version number and exit -v, --verbose Be verbose -vv, --veryverbose Be extra verbose