Site tooling is now complete

21.06.2025

In the last three entries in this blog I've explained the thought process behind developing the tooling to build this static site [1] [2] [3]. The tooling is now considered complete, since all of the original design principles are met:

Various remarks about the implementation

RSS feed generation

In order to provide a neat interface to read the blog, an RSS feed is required. I made the decision to exclusively leverage the same data and files that are used to generate the site itself. This makes it so that the feed comes "for free", as long as there is a single static template file that's baked into the feed.xml.

The RSS feed did however trigger the need for being more strict on the data organization, and prompted a complete refactoring of the site templates. This proved useful very quickly, and paid itself back by enabling a pretty simple data validation setup.

Site and data validation

Initially, the setup simply ensured that all generated HTML was valid. This worked well, but had a few shortcomings: Firstly, it required manually updating the same information in several places, which was prone to manual errors, as listed in the the blog post about the site generator. Secondly, the HTML validation doesn't cover validating the generated RSS feed.

As all the information about the posts was eventually consolidated into a simple dictionary, the input data was simple to validate by simple ansible modules. Practically all there is to do is checking that the relevant values are unique. Additionally the value of the individual post path is checked to actually exist in the repository. After the site has been generated, the feed timestamps and directory structure of the site is checked. The workflow of adding a blog posts is as simple as first appending the dictionary with the new post details, and then adding the actual content to a new file.

To conclude, a succesfull generation guarantees that:

Thank you lighttpd 2.6.2025 - 21.6.2025

Lighttpd is replaced with caddy. This was initially done due to debugging images not being served correctly, as debugging the lighttpd configuration was a bit cumbersome. The lighttpd-based setup worked well, but it is a bit easier to use a well maintained and documented server in the container.

Hidden blog posts are supported

Initially, a hidden post was generated by simply omitting the post link from the blog index. This proved cumbersome as the blog index generation was automated alongside the RSS feed, so the post inclusion is now simply controlled by a post-specific variable, which has to be explicitly set for each post. Simply declaring the post to have the public: false attribute will exclude it from the blog index and the RSS feed.

The site only contains absolute paths

A simple variable in the generator playbook determines the base URL of the site. For local development, it is convenient to be able to set the URL to simply be http://localhost:3000, and automatically update everywhere with ansible-playbook generator.yml --extra-vars "base_url=http://localhost:3000". The default value remains the public URL of the site.

In conclusion

I really liked this project, and am pretty happy about the eventual outcome. I learned quite a lot of small morsels of new information, and managed to complete this project in less than three weeks.

I most likely won't really add new features to the site generator, but I might do several non-functional refactorings and other similar additions, such as moving all the modules to a new purpose-made ansible collection.

Go back to list of entries