In the last week I experienced the limits of the pelican blog software. My workflow includes saving work between steps in git. However, when I switch machines I have to push these commits.
The result is, that unfinished articles end up on the public page. For that I am sorry.
This weekend I looked into Zola as a replacement. It has some very compelling features:
- Has an option to render drafts locally, this would solve my problem.
- Live Reload, very comfortable.
- It is faster, but since pelican takes ~2 seconds to render the whole page I am okay with this kind of speed.
However, it is very hard to keep the dates visible in the URLs.
So /2021/3/28/blog-bugs/
would just be /blog-bugs/
or /posts/blog-bugs/
.
There is a workaround for that by placing _index.md
files in every folder:
+++
title = "2018"
sort_by = "date"
template = "section.html"
page_template = "page.html"
transparent = true
+++
My blockers for using Zola are:
- Transparent option should cascade down so I don't have to specify it on every level
- More compatible integration for Categories and Tags
In the end I would have to make the theme work with the new generator again, but that is just work that has to be done.
Pelican to Zola Migration Script
For completeness, I wrote this script to migrate most of my content. If this helps you, please let me know.
#!/usr/bin/env python3
# LICENSE: GPLv3 with attribution
import glob
from pathlib import Path
from dateutil import parser as timeparser
import os
for e in glob.glob('../estada.ch/content/20*'):
print(e)
p = Path(e)
if p.is_dir():
print("is dir, aborting")
break
if p.suffix != ".md":
print("is not .md, aborting")
break
date = p.stem.split("_")[0]
d = timeparser.parse(date)
dirname = "content/{}/{}/{}/".format(d.year, d.month, d.day)
name = "content/{}/{}/{}/{}{}".format(d.year, d.month, d.day, p.stem.split("_")[1], p.suffix)
os.makedirs(dirname, exist_ok=True)
with open(e) as r:
lines = r.readlines()
in_header = True
content = []
headers = {
"date": "{}-{:02d}-{:02d}".format(d.year, d.month, d.day),
}
taxonomy = {}
for line in lines:
if in_header:
line = line.strip()
if line == "":
in_header = False
else:
print(" line: {}".format(line))
(key, val) = line.split(':', 1)
if key == "Tags":
taxonomy[key.lower()] = list(map(lambda t: t.strip(), val.split(',')))
elif key not in ["Date", "Lang", "SocialImage"]:
if key == "Summary":
key = "description"
headers[key.lower()] = val.strip()
else:
content.append(line)
## make header
with open(name, 'w') as w:
headers['slug'] = headers["title"].lower()
w.write("+++\n")
for k, v in headers.items():
if k in ["status", "category"]:
continue
elif k in ["title", "description", "slug", "path", "template"]:
w.write('{} = "{}"\n'.format(k, v))
else:
w.write('{} = {}\n'.format(k, v))
w.write("[taxonomy]\n")
for k, vs in taxonomy.items():
s = None
for v in vs:
if s is None:
s = ""
else:
s += ", "
s += '"{}"'.format(v)
w.write('{} = [{}]\n'.format(k, s))
w.write("+++\n\n")
w.writelines(content)