blog.kronis.dev


My blog doesn't need quality, it needs to look like it's from the 90s

Date:

Why, hello there! It's been a little while since I last posted anything. The thing is, the blog has been in a bit of a hiatus. After moving to the city, I found myself more busy with all sorts of work than I'd like, meaning that my free time ceased to be a thing for a while. Plus, the experience of writing blog posts had gotten quite annoying for a number of, primarily technical, reasons. So, I did the reasonable thing that any individual would do: I burnt it all down and rebuilt the blog from scratch.

You might have actually noticed, that it no longer looks how it used to:

01-old-blog

But instead has a more, shall we say, "focused" look to it, at least at the time of writing this:

02-new-blog

The reason for this, is that now it's running an entirely fresh instance of Grav under the hood, the very same flat-file based CMS as previously, but a much newer version, with a somewhat different plugin setup and a completely different writing and deployment story. The theme on top of all of it (a somewhat modified version of Hypertext) is just an attempt at switching towards something more minimal and focused, perhaps utilitarian.

Let's dive into why I felt all of that necessary...

What was wrong with the old install of Grav?

In contrast to a more traditional CMS like WordPress, Grav doesn't need a separate database instance. That's where the "flat-file" structure comes into play. For example, to make this blog post happen, what I actually needed to do, was just create a new file:

my-blog-doesnt-need-quality-it-needs-to-look-like-its-from-the-90s/item.md

With some frontmatter inside of it, along the lines of:

title: 'My blog doesn''t need quality, it needs to look like it''s from the 90s'
date: '2025-02-16'
publish_date: '2025-02-16'
allowCSS: default
allowJS: default
show_header_image: false
show_clickthrough: true
show_date: default
is_headless: false

(some of those depend on the theme used and you can have your own custom stuff, but you get the idea)

which is then followed by the rest of the page contents in Markdown, which then gets transformed into HTML thanks to some Twig templating. In other words, it more or less has the best of both worlds: Markdown is just a text based format that is really nice to edit and from an ops perspective, it's also really easy to handle deploying and later backing up what's just a bunch of files. Here's an example of one of the template fragments:

02-twig

At the same time, for the second iteration of my blog, I was actually considering static site generators like Jekyll or maybe Hugo, both of which would take similar collections of files and output fully static HTML without the need for PHP or another server side component to process the Twig templates, making the security situation more or less completely foolproof, since a PHP runtime still leaves you with some risks.

However, static site generators are a bit more annoying to work with, because in the case of using Grav, it handles ensuring consistency between the templates, so the file contents and whatever the built/cached versions of the final HTML are pretty much live. Grav also has a pretty nice Admin plugin which you can use to get an experience that's not all that different from managing a WordPress site, if you want:

02-admin-page

Right now, I'm actually writing this article in Notepad++ locally, however I do occasionally jump into the admin panel to change the site configuration, since tracking down the exact YAML I need can prove to be a bit of a challenge otherwise. In the old setup, the experience of writing new posts "live" was pretty stressful, because I've ran into the occasional bug previously which prevented pages from rendering. I'd also very much prefer to version my articles and other files in Git, as opposed to having to count on my backups always being made in the background correctly (both the Grav built in ones and the ones from my server backup setup, which I will write about soon).

In other words, I want to let the software occasionally fuck up with no impact on the live instance - very much like developing any other piece of software locally, making sure that everything is okay and then deploying it live. Furthermore, the older version of the Admin plugin had some annoying quirks about it, which made writing new articles in the web browser worse than just creating them (with the desired configuration) there and continuing by editing the actual post in a text editor.

For the site as a whole, there also was the separation into multiple site sections:

While the other issues are primarily technical, this one was related to something a bit more human: this structure gave me writer's block, because I could never really figure out what would fit best where and I didn't feel like some of my ideas would live up to the "title" of being called an article or a tutorial. To tackle that, I also decided on abolishing that separation and turning this into a linear stream of various posts, a bit more like your average social media feed.

The new setup

So, with all of that in mind, the old instance had to go, since my former attempts at updating it had been unlucky. The new setup, on the other hand would need all of the following, no compromises:

To achieve this, Grav itself is perfectly serviceable and I can still keep benefitting from its simplicity. Well, at least a newer version and not just one that's running on a server somewhere by itself, but something that I can run locally, make changes on, then package and push to the server for deployment. In the end, the setup ended up being a bit like:

If you want, here's a look at the Dockerfile:

02-the-dockerfile

I do use my own custom Apache2 and PHP image which won't be very relevant to you, but that's more or less the entire setup, with some other specific CI/CD stuff in the background. The actual trouble that I ran into mostly concerned migrating all of the old data and making things look consistent, as well as tuning the theme, plugins and the overall setup.

Trying to migrate everything

So, first off, the frontmatter used a format that was quite different from what the new theme wanted and in some cases had quite a lot of extraneous values in it that weren't relevant in the slightest for the type of item. For example, some blog posts would have configuration for showing sub items, when they would never have any. Other times, there might be random configuration parameters that were stuck around from when I was using the Admin plugin and quite possibly had clicked on something in the page, without the need for it.

Ergo, I needed to change a bunch of frontmatter. What's more, in the current setup or maybe Grav in general, sometimes has a bit of confusion between the following parameters:

date: '2025-02-16'
publish_date: '2025-02-16'

I can't, for the life of me, tell you exactly why (probably the fact that the templates and plugins and core Grav can all have interactions that are slightly more complex than there should be), but if both of those aren't set, then either the ordering or the display of posts goes a bit awry. In some cases, that would mean that my 2025 posts would go somewhere down the list and would comfortably sit on page 3 between posts from 2022 and 2024. In other words, that needed to be fixed.

My first attempt was to just throw LLMs at the problem and call it a day. After all, they can just take some vaguely written instructions and perform pretty well when it comes to some boring boilerplate related tasks. The problem, of course, is the context length. At the time of migrating the setup, there were over 80 different posts across the sections, whereas GitHub Copilot only allows you to edit 10 files at a time:

03-editing-frontmatter-with-ai

Frankly, I was a little bit bummed out about that, since this was a one shot migration, something that throwing AI at would actually be pretty good for. But fret not, if I can't get AI to change the files for me, then surely we can collaborate on writing some code to do the processing for us. Essentially, make one machine make another machine do the thing:

04-writing-code-with-ai-instead

I have to say, that for this use case, it was actually really good. Lots of people have expressed that LLMs are like a highly motivated but perhaps slightly absent minded junior developer, though I will admit that as long as I can verify the correctness of its output, I will trust it on common tasks in niche areas, like writing regular expressions more than my own brain. Furthermore, even when it does mess up, with a bit of poking and prodding, it can also fix its own blunders:

05-fixing-issues-with-ai

It wasn't that long before I had a script that would generate the consistent frontmatter while preserving the post dates and titles. That's also when another issue made itself apparent, not all of the files actually had either of the dates set in the Markdown files:

06-processing-files

You see, the old Grav setup didn't enforce setting either (there's a plugin for setting one of the values upon creating a new page, which I modified to set both on the new setup), which means that in some cases the file modification dates on the server would be used instead.

That is very slightly insane, because what if at some point I decide to do something like fixing the frontmatter, which would then throw the whole blog into utter disarray? Thankfully, since I was working on this fully locally, I could just log into the server, pull all of the file modification dates and put them into a file:

07-file-date-confusion

In turn, I could use this file when the frontmatter itself wouldn't have the needed date set. Once more, it's a hodgepodge setup of some Bash scripts as well as a healthy deal of Python.

Did I mention that the LLMs are also pretty decent at autocomplete nowadays? Maybe not always correct, but they'll at least decrease the friction of writing new code and iterating, which is why all of this is just one day's side project and why I'm writing this blog post at 1 AM, instead of 3 AM. LLMs are single handedly saving my sleep schedule, you could even say:

08-ai-has-decent-autocomplete

Huzzah, we have a script that fixes all of the frontmatter and finally everything is ordered correctly:

09-fixing-publish-dates

I also had to deal with some regular expressions (I could have parsed the line manually, but this was a tad faster) when trying to get rid of some old Grav image resizing, in part because some of the images that it produced were actually 4x larger filesize wise than the locally exported ones (I do some XnView MP batch processing, which is pretty good), but also the syntax in the new versions differs slightly, where if you try to just have ?resize=1024, instead of ?resize=1024 then it will break and prevent the whole page from rendering:

10-fixing-regex-with-ai

Along the way, I realized that not only the old blog section had the awkward URL of https://blog.kronis.dev/everything%20is%20broken, but that plenty of the files also had spaces in their names. While that's not generally all that dangerous nowadays, I still believe that just placing a dash or underscore instead of a space in there is a bit more readable and elegant, so I did just that:

11-also-fixing-filenames

That did lead to quite a few changes (some 600-800 images, if I recall correctly), but seeing everything showing up in a Git diff was actually quite pleasant for once. If nothing else, it gives me confidence that what I'm doing is the right thing and also helps me catch a few edge cases along the way, since I can just add all of the changed image files and going through less than a 100 blog post files with diffs isn't too hard:

12-lots-of-git-changes

Now, someone said that cool URLs don't change, which is an aspect that I did commit some sins in. With the change of the site structure, I no longer would have the separate:

sections, but rather just a singular:

Now, the blog doesn't have like a big readership or anything, but breaking a bunch of old links and returning 404s for content that is still technically on the server, but just in a different location wouldn't be very nice of me. So, it's time for some path rewriting! I could probably get something similar working within Grav itself (I do vaguely recall it having functionality like that), but frankly it was just easier to do in Apache2:

13-path-rewriting

The separate RSS, Atom and JSON feeds have also been merged into just one in each format for the whole blog, following the structure changes of just having one timeline of posts.

Along the way, I did have some further issues in getting Apache2 to properly reload its configuration files, because I had what were the correct directives, but they just wouldn't show up in the form of the redirects being executed when opening a matching URL. I think it did have something to do with Docker caching because even with --build the issue was there, but only after deleting the container altogether and rebuilding it from scratch did it start working.

Oh, also this is where the AI also lost its mind and started printing the same thing over and over:

14-breaking-the-ai

Quite the funny place to have a breakdown, to just start repeating "Everything is broken". I think we've reached levels of meta never thought of previously. But either way, with those changes in place, most of the old URLs should still redirect to the up to date ones.

I might have used the status code for a temporary redirect instead of a permanent one, but frankly that's something for the future me to think about.

So what's the end result?

In the end, I met all of the goals above, migrated all of the old data and now can work with everything fully locally. I just make some changes to the Markdown file, hit reload in the browser and boom, new page version is visible. When I want to deploy it? I just commit, merge into the production branch and let CI/CD take over.

Alongside all of that, I also made some further common sense improvements, such as loading the images lazily (only when you scroll to them), no longer having a sticky header (I liked it, but some people really didn't), having some better breakpoints in the CSS and the aforementioned simpler site structure.

I did have to give some things up, too. For example, previously, you'd see the blog posts as individual cards, but now they're just a simple table with the title and publishing date:

15-no-more-pictures

Personally, the old format did take a bit more work (since I had to set the first image correctly for it to show up, make it in a size that would fit within the card and also make sure that the size/format didn't result in big filesizes), but I did enjoy that bit of visual flare. Of course, it's not really tenable when you have 20 posts per page and don't want to bloat the size any more than it already is thanks to all of the images. So, I had to give that up:

16-the-old-picture-look

I mean, come on, seeing Gilfoyle's face is definitely something to drive up user engagement, right? But alas, usability over form, at least for now. Whenever the image was actually used in the page it will still show up as previously, but in some places the header images are just gone:

17-hey-look-gilfoyle

I am also somewhat concerned over the overall size of the whole page.

The repo without the .git folder sits somewhere around 250 MB and even Git LFS doesn't necessarily help that much. Plus, when I initially added all of the source code to the repo directly, it was over 7000 files (ain't modern software grand) which easily breaks the GUI in GitKraken, an otherwise lovely tool, but thankfully the Git CLI was more than enough for cases like that. If they can put the Linux source code in Git, surely I'll be fine.

Plus, with caching, the actual builds aren't like crazy long either and storage itself is cheap enough nowadays:

18-caching-works-fine

The whole ordeal took longer than I'd like and does make the Never update anything blog post seem a bit silly, though in practice it was less about updating (which didn't work when I tried anyways, the part in that other blog post about updates being evil is still very much true) and more about starting over and making something better.

Summary

In summary, I'm pretty happy with how things worked out so far. The new setup looks okay, performs well and moves me a little bit more towards feeling secure in it and not having to worry too much about the main instance of the blog randomly blowing up one day.

I might prefer the even greater simplicity of static site generators, but that'd mean even more rewrites and I don't think I have the time for that right now. There are some additional plugins that you can get that go one step further towards having a static site generator, instead of just using templates and caching, but honestly I'd rather keep it less complicated, rather than more so.

Here's hoping that this gets me out of my writer's block as well!


Other posts: « Next Previous »