Tagrelease

./blog.sh 1.9

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

The release about publishing in more than one language. Name them in site.locales, and a post keeps one date, one set of tags and one series, with a text per language written by ./blog.sh translate. The site's own language keeps the root, every other one gets its own, and one rebuild makes all of them. The banner, menu, footer and tags speak each language too; a post nobody has translated still shows, linking to the one copy that exists. This very site has been running in English and Czech since the release was a draft.

Around it, the first hour: two people installed 1.9 from nothing and upgraded a copy of a 6,600-post site, doing only what the docs said, and everything they tripped over is fixed. Esc never does anything, the setup speaks your language from its first screen and will not take the template's example as your site's name, a draft's properties show where to read it — with a QR code for the phone — and doctor --online now asks the deploy target what stands in its root that the site does not put there.

Read more

./blog.sh 1.8

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

A release about what happens when something goes wrong. Every page, feed and index is now written to a temporary file and renamed into place, so a save interrupted by a full volume or a build stopped mid-write no longer leaves a page at half its length, or a post at 0 bytes with the old text gone. A cold build costs about a fifth more for it; a cached rebuild does not, so an ordinary day of publishing costs exactly what it did.

Arriving twice stopped meaning happening twice: a post sent from the phone twice, or one receipt delivered in two copies at once, is one post now, and re-importing this engine's own export no longer turns 1200 posts into 2400. And an address is no longer trusted just because it looks like one — a ../ in a slug, a media filename or a redirect target stays inside the site, a local deploy stays inside its directory, and a feed's redirect cannot send a fetch to localhost.

Read more

./blog.sh 1.7

./blog.sh 1.7 is out. The page that writes a post on a phone now publishes it too, and it finds out what happened rather than being told: it mints a receipt before sending and asks the site for it every three seconds until the answer comes.

Beside it, the other half of the same idea — changing what a post is without opening what it says: its series, its tags, its type and three flags, from a screen. Then a link card written in the front matter, about fifty drawings a tag can wear, a video repacked on the way in so the reader does not wait for the whole file, and seventeen fixes.

#blogsh #ruby #staticsite

./blog.sh 1.6

./blog.sh 1.6 is out.

The build stopped rebuilding what nobody changed: a rebuild that changes nothing costs a seventh of what it did, an ordinary publish about two fifths. A post can be handed over as a file — by a script, a cron job, or the new page at /write/ on the blog itself, which sends from a phone over the SSH the server already has. Nothing new listens on the network.

Around those: a row of share controls under a post, an icon a tag can carry, a way out of the trash and the versions, and a photograph stored once instead of twice.

#blogsh #ruby #staticsite

./blog.sh 1.5

1.5 is the release where the site learned to say what it holds. The release is on GitHub.

A post that never had a title used to be called by its address — on this project's own reference archive that was 62 % of it. Now it is called by its own opening words, everywhere except its heading, which stays the date it has been since 1.3.

Two new pages come with it: /archive/, a map of the whole site in two levels, and /tag/, every subject it has ever written about. Both are built from the posts themselves — nothing to configure and nothing to keep up to date.

Two things a post can now decide for itself: where its teaser stops (//--more--//, asked for in issue #35), and how much of it a listing card shows — cards are cut before they are written rather than drawn in full and hidden with CSS. And a code block carries a copy button, asked for by an operator running a blog of terminal how-tos.

Read more

./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.

Read more

./blog.sh 1.3.2

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

A GoToSocial release, and both fixes came from the first site to wire blog.sh to a GoToSocial instance: arch-linux.cz runs its own, instead of pointing at a Mastodon account. My own tests had Mastodon on both ends, so they could never have found either.

GTS speaks Mastodon's API with its own accents, and two places in the engine had taken the Mastodon dialect for the whole language: the feed widget asked every host for JSON first and got exactly that, a JSON Feed the XML parser then reported as malformed; and doctor refused the account id its own setup had pointed you at, because it demanded digits where GTS answers with a 26-character ULID.

One small thing for skins came from the same site: a listing page now says whether it is the first one. <body> carries page-first or page-cont, so a stylesheet can tell the front of a listing from its /page/N/ continuations — something CSS cannot work out on its own, because it cannot read an address.

Read more

./blog.sh 1.3.1

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

A bug-fix release about restraint. The scheduled-publish cron now declines the announcements it cannot take back: a backdated post publishes without announcing, and a post that already carries its announcement address is never announced again — not by the cron, not by publish, not by toot, whichever network the address lives on. The queue takes its lock for every write too, so a tick landing mid-edit cannot revert a post it just published.

The first hour of a fresh install stopped misleading: preview and publish print the address that actually opens instead of example.com, the deploy speaks the site's language, and the example config no longer chooses Mastodon for you — a network is a choice.

And emptiness is an answer now, in both directions: an emptied section takes its heading with it instead of crashing the build or failing doctor, and a heading over content that stays can finally be turned off.

Nothing to migrate — git pull, rebuild, deploy. Full notes →

./blog.sh 1.3

./blog.sh 1.3 is out.

Dressing a site no longer means editing the engine: your own stylesheet, menu, sidebar and lead images are settings now. Posts group into series, pages step out of the stream, and every edit keeps the version before it.

And three commands that read the whole archive: check reports what's broken, stats counts what's there, and export packs it all into Jekyll's layout. The way out ships with it.

#blogsh #ruby #staticsite

./blog.sh 1.2.1

./blog.sh 1.2.1 is out.

A phone records where you were standing in every photo it takes. Social networks strip that on upload; a static site has nobody to do it. Now the engine does, on the way into the archive — and the doctor will clean the ones you saved before.

The bio, the footer, and the banner's claim are now Markdown. They were the only texts on a Markdown blog that had to be HTML.

./blog.sh 1.2

./blog.sh 1.2 is out — the import release.

Eight import sources became twenty-two: every social network you are likely to have posted to, the blog platforms, and the Wayback Machine for blogs whose platform no longer exists.

Setting a site up is a conversation now. So is how it looks, with the candidate palette rendered on your own site before you keep it.

And a screen for walking an archive that got too big to scroll.

./blog.sh 1.1

./blog.sh 1.1 is out.

Pin a post to the top. Schedule into publishing slots that queue instead of colliding. Attach files as download cards. Rename a slug without breaking a link — the old URL redirects forever. One dialogue for everything you can do to a post.

And deploy guards that measure the build against the build, so a failed upload can't quietly switch them off.

./blog.sh 1.0.1

./blog.sh 1.0.1 is out — eighteen fixes, none of them reported by a user.

They came out of an independent verification pass over the release: a failed write could leave a post without a file, one bad post could stop the whole build, a missing media file could delete the copy that was already live.

There's now a CHANGELOG a clone can read, and ./blog.sh version tells you what you're running.

./blog.sh 1.0

./blog.sh 1.0 is out.

A blog engine that's a folder and a shell script. Ruby stdlib and bash — no database, no gems, no admin. Comments live on the Fediverse. Eight importers, so your Tumblr and Twitter archives have somewhere to go. MIT licensed.

The site you're reading is the engine running itself.