Back in 2019 I wrote about how this blog works. A lot has changed since then — automated deploys, dev.to sync, AI-generated tweets. Time for an update.
The foundation hasn't changed. Grav CMS, flat-file, PHP 8.2, Markdown, no database. Plugin updates and security patches aren't something I think about — Grav just runs.
The blog theme extends quark and adds custom styles — alternating white/gray section backgrounds, a tag cloud with CSS custom properties, article cards. Pure vanilla JS.
The site is bilingual — Czech (*.cs.md) and English (*.en.md). Every article has both files side by side.
This hasn't fundamentally changed since 2019, just gotten more solid. Deployer 7.3 handles the actual deploy — symlink strategy, shared dirs for cache, automatic cleanup of old releases.
GitLab CI orchestrates everything:
master triggers the pipelineDockerfile.ci changes)dep deploy over SSH to biberle.czThe SSH key is stored as a base64 CI variable, decoded at runtime. The entire deploy is fully automatic.
English articles automatically get published to dev.to. The sync script scans all English articles, matches them against existing dev.to articles via canonical_url, and creates or updates as needed. Stateless — the canonical URL serves as the key. Runs in CI after every deploy and also on a schedule (for future-dated articles that become publishable).
The reverse works too — importing dev.to articles into the local blog, downloading cover images, and setting the canonical_url back to the blog.
This is the new addition. When a new article goes live, it automatically gets tweeted. But not with some generic "New blog post!" — the tweet text is generated by OpenAI (gpt-4o-mini) following my writing style guide.
Here's how it works:
The writing style guide lives in a single file, so there's one source of truth for both the AI and me.
Tweets only go out on weekdays at 9:00 AM (Europe/Prague), via a separate GitLab CI schedule with the TWITTER_POST=true variable.
Some things just work and there's no reason to touch them:
But mostly — write more. Automation is great, but it's useless without articles.