Série./blog.sh v1.4

./blog.sh 1.4

./blog.sh 1.4 is out — release on GitHub.

The widest release since 1.0. Three things arrived: the commits widget reads Gitea and Forgejo — asked for by one of the first people outside this project to run the engine, because most of the Fediverse keeps its code on Codeberg, and mirroring to GitHub for one sidebar card was the workaround. widgets.commits.instance takes the server's address; that is the whole configuration. Comments work on GoToSocial — found on arch-linux.cz, where no post had ever shown a single comment, silently; my own tests had Mastodon on both ends, so they could never have caught it. And the engine now speaks the site's language everywhere: a Czech or German site used to watch its own deploy and build warnings go by in English, one line above a translated sentence. Sixty-odd sentences moved into the locales.

Číst dál

The card that reads Codeberg

The sidebar has a commits card. Until now it read GitHub, full stop — which is a strange default for an engine whose natural habitat is the Fediverse, where the code lives on Codeberg or on somebody's own Forgejo, and GitHub is the mirror you keep for one widget's sake.

One of the first people outside this project to run the engine asked the obvious question, and the answer shipped: widgets.commits.instance takes the server's address, and that is the whole configuration.

One key, not two

There is no forge_type beside it. An address already answers what kind of host it is — Gitea and Forgejo speak the same API, and a key that must agree with another key is a config mistake waiting for its moment. Leave instance out and the card reads GitHub, exactly as before.

Cheaper than the original

Číst dál

The same language, spoken differently

Comments here are replies to a toot, and GoToSocial speaks Mastodon's API — so comments on a GTS instance should have simply worked. They did not, not once, and nobody saw it fail: arch-linux.cz, the first site to pair this engine with its own GTS instance, had never shown a single comment under any post. My own tests had Mastodon on both ends, so they could never have caught it.

Dialect is not language

Two accents were in the way. GoToSocial writes a status address in its own shape, and the engine's pattern insisted on Mastodon's — a numeric id where GTS answers with a 26-character ULID. And GTS requires a token even to read a public thread, where Mastodon hands it to anyone who asks. The first accent broke the address parsing; the second broke the entire idea of live comments, which is a visitor's browser fetching the thread — a browser you cannot give a token to.

Moderation is the door in

Číst dál

Deploy in your own language

The wizards were translated from the start; the narration around them was not. A Czech site watched its own deploy go by in English, one line above a Czech sentence — build warnings, announcement failures, import errors, the queue's repair instructions, all of it. Not broken, exactly. Just a house where half the rooms answer in a different language.

Sixty sentences moved home

1.4 finishes the job: the deploy's whole narration — header, progress, the closing tally, every degradation notice — plus all eleven build warnings, every Bluesky and Mastodon failure, and the sentences an import prints when a source dies. English, Czech and German carry the same set, and the suite enforces the parity, so a key cannot exist in one language and abort in another.

What stays as it arrived

The diagnostics inside the sentences do not get translated: an HTTP code, a server's raw answer, an exception's text. Those are evidence, and evidence is quoted, not paraphrased — the sentence around them is the site's, the thing inside it is the network's.

Číst dál

The other half of check

./blog.sh check reads the whole archive and says what is wrong: media a post asks for and hasn't got, links to addresses nothing answers at, two posts that would be served at one address. Through 1.3 that was where it ended — a list of findings and an exit code, with the fixing left to you and a text editor.

--repair walks it with you

check --repair is the checker's other half: for each finding it offers the one repair that finding allows — an old address written into the target post's redirect_from, a relative link rewritten to the address it means, an orphaned file moved to the trash. Nothing is applied without a keypress, nothing is deleted outright, and a finding with no obvious answer — two posts colliding, an image only a human can judge — is shown and passed over rather than guessed at.

It proved itself before it shipped: on a real archive it took 238 findings down to 42, and the 21 repairs it proposed matched what a person had already decided by hand, one for one.

--json says everything

Číst dál

What a crash may cost

The queue can now swap two scheduled posts that share a slug across two years — with their media and their edit history riding along, because those are keyed by year and slug exactly like the post file. That sentence is short; making it true was most of a release cycle. The interesting part is not the swap. It is what happens when the power goes out in the middle of one.

The budget is zero

A move is several renames, and a crash can land between any two of them. The rule 1.4 holds to: whatever the moment, a crash may cost you the move — never a post. Files step aside under parking names before anything writes; a failed write puts everything back, byte for byte; a hard kill that strands a parked file leaves it where check finds it and says truthfully what it is.

The advice is part of the system

Číst dál

Tested like an enemy

1.4 went through four adversarial audit rounds before the tag: fleets of independent reviewers over real archives — real exports, a real 4400-post site, real deploy targets — each told to break the engine rather than confirm it. The findings numbered over a hundred. Every one was reproduced before it was fixed, and every fix is pinned by a test that demonstrably fails on the code it fixed.

The pattern that kept repeating

The uncomfortable lesson: each round's fixes were where the next round found its bugs. A fix for a comment-parsing rule deleted a hundred lines of somebody's config; a fix for that hid a widget's answers; the recovery advice written to save posts could destroy one. Four times in a row, the newest code was the most dangerous code in the tree — which is exactly why the last round audited nothing but the fixes themselves. It caught three regressions of mine before any user could.

Eighteen files

Číst dál