Série./blog.sh v1.015

./blog.sh — minimalistický blogovací systém

./blog.sh v1.0 — díl 1 z 15

./blog.sh je blogovací systém, který se spouští z terminálu. Příspěvky jsou soubory, web je výsledek sestavení a komentáře žijí na Fediversu — v celé té větě nikde není databáze.

Tři věci ho odlišují od ostatních pěti set generátorů statických stránek:

  • Žádná databáze. Jeden příspěvek je jeden soubor JSON. Záloha je tar, historie je git, odchod je smyčka for.
  • Žádné závislosti. Standardní knihovna Ruby a bash. Žádné gemy, žádné npm, žádný lockfile. Stačí naklonovat a spustit.
  • Žádný systém komentářů. Každý příspěvek se ohlásí na Mastodonu nebo Bluesky; odpovědi na to oznámení jsou komentáře, které tvůj prohlížeč načítá z veřejného API.
Číst dál

Postaveno záměrně pro jeden blog

./blog.sh v1.0 — díl 2 z 15

Tohle není univerzální engine a předstírat opak by ti promarnilo odpoledne.

./blog.sh vznikl kolem přesně jednoho nasazení: jeden autor, jeden archiv, terminál a komentáře na Fediverse. To je obvykle část, kterou projekt schovává na stránce „o nás". Radši s tím začnu, protože je to zároveň vysvětlení všeho vyhraněného na tomto nástroji. Univerzální generátor vyřeší obecný případ a pak nechá tebe nakonfigurovat si cestu zpět k tomu tvému konkrétnímu. Tenhle engine u konkrétního případu začal a nikdy ho neopustil.

Takže tady je poctivá tabulka:

./blog.shHugoJekyllGhostWordPress
Databázežádnážádnážádnánutnánutná
ZávislostiRuby stdlib*jedna binárkaekosystém gemůNode + MySQLPHP + MySQL
KomentářeFediverse, vestavěnévlastnívlastnínativní + členovénativní + pluginy
Nasazení6 backendů, s pojistkamivlastnívlastníje to serverje to server
Více autorůneanoanoanoano
Pluginy / ekosystémne, záměrněobrovskýobrovskýdobrýnepřekonatelný
Velikost komunityobyvatel: 1ohromnávelkávelkátřetina webu
Sestavení 10 000 příspěvkůpomalejšívteřiny, vyhrává snadnopomalé––
Newsletterneneneano, nativnípřes pluginy
Číst dál

Žádná databáze. Jeden soubor JSON na příspěvek.

./blog.sh v1.0 — díl 3 z 15

Tady jsou tři řádky Markdownu a tady je, jak vypadá soubor na disku poté, co je uložíš.

Co napíšeš:

Příspěvky jsou **soubory**. Sestavení je cyklus přes složku.

Žádný třetí krok není.

Co dopadne na disk, jeden soubor JSON na příspěvek:

Číst dál

Komentáře, které nehostuji

./blog.sh v1.0 — díl 4 z 15

Počítadla hvězdiček, boostů a odpovědí hned pod titulkem tohoto příspěvku patří tootu, ne tomuto webu. A komentáře na konci této stránky jsou odpovědi na ten toot. To je celý systém – tady je, jak funguje.

Když je příspěvek publikován, engine ho oznámí na Mastodonu nebo Bluesky – jedna síť na web, nikdy obě; sestavení odmítne konfiguraci se dvěma, protože komentáře bydlí přesně na jednom místě. Odpovědi na to oznámení jsou komentáře: tvůj prohlížeč načte vlákno rovnou z veřejného API sítě, bez přihlášení, bez klíče, bez prostředníka. Počítadla pod titulkem jsou táž čísla, přečtená z téhož tootu.

Co z toho plyne

Žádná databáze komentářů – nic k migraci, k zálohování ani k moderování ve dvě ráno. Žádný filtr spamu, protože síť už jeden má a je lépe financovaný než ten můj. Počet komentářů je prostě počet odpovědí. A moderace je moderace tvé instance: blokování, ztlumení a nahlášení fungují přesně tam, kde se rozhovor skutečně děje.

Číst dál

Přidej svůj jazyk. Je to jeden soubor.

./blog.sh v1.0 — díl 5 z 15

Jediný způsob, jak přispět do ./blog.sh bez napsání jediného řádku Ruby, je přeložit ho – a je to jeden soubor YAML.

Všechno, co čtenář vidí – navigace, data, nadpisy archivu, pole hledání, výzva ke komentářům – pochází ze souboru lokalizace. Celý návod bydlí tady:

docs/localization.md – přidání jazyka do ./blog.sh

Tři věci to dělají opravdu snadným:

  • Jazyk jsou data, ne kód. Zkopíruješ en.yml, přejmenuješ ho, přeložíš hodnoty. Žádné šablony se nedotkneš, žádné Ruby nečteš.
  • Záložní hodnota funguje po klíčích. Napůl hotový překlad funguje od prvního dne – cokoli jsi ještě nepřeložil, se prostě ukáže anglicky, dokud se k tomu nedostaneš.
  • Úplný musí být jen en.yml. Ten tvůj může růst tempem, jaké ti dovolí víkendy.

Angličtina, čeština a němčina už jsou v. Ten tvůj by byl čtvrtý.

Číst dál

Co ./blog.sh nedělá

./blog.sh v1.0 — díl 6 z 15

Seznam funkcí ti řekne, čím nástroj chce být. Tohle je ten druhý seznam.

Funkce, které tu nejsou – a nepřijdou

  • Více autorů – engine je postavený kolem pracovního postupu jednoho člověka a každá víceautorská funkce zdaňuje jediného autora rolemi, oprávněními a rozhraním pro autorství
  • Pluginy – API pro pluginy je slib držet vnitřnosti navždy stabilní; nástroj takhle malý se má forkovat, ne rozšiřovat
  • Editor WYSIWYG – tvůj $EDITOR sis konfiguroval léta; nebudu s ním soupeřit v textarea
  • Vlastní systém komentářů – úložiště, moderace, spam, GDPR a databáze, to všechno kvůli tomu, znovu postavit to, co Fediverse už dělá veřejně
  • Témata jako stažitelné balíčky – sedm klíčů barev a vlastní banner tě dostanou dál než tržiště témat, za nula tržišť

Nezaškrtnutá políčka, jak je vykreslují vlastní seznamy úkolů enginu. Tak už zůstanou.

Co odmítá parser

Číst dál

Proč ne X, a proč Threads čeká

./blog.sh v1.0 — díl 7 z 15

Čtení příspěvku: 0,005 $ za zdroj. Vytvoření příspěvku: 0,015 $ za požadavek – 0,200 $, když nese URL. Bezplatný veřejný přístup ke čtení: žádný.— ceník X API pay-per-use, docs.x.com, ověřeno v červenci 2026

Dvě sítě jsou v. Dvě nejsou – a tady je celá úvaha, protože „rozhodli jsme se proti" není důvod.

./blog.sh oznamuje každý publikovaný příspěvek na Mastodonu nebo Bluesky a odpovědi na to oznámení se stávají komentáři příspěvku. Aby to fungovalo, potřebuje engine od sítě dvě věci: způsob, jak přispět, a způsob, jak prohlížeč přečte veřejné vlákno. Levně, ideálně zdarma, navždy – protože osobní blog je projekt na desítky let s rozpočtem koníčku.

X: ne

Podívej se na ten ceník znovu. Oznámit příspěvek s odkazem stojí skutečné peníze. Načíst vlákno odpovědí – což se děje pokaždé, když kdokoli sroluje ke komentářům – stojí skutečné peníze, za každé čtení, navždy. Na osobním blogu je to předplatné vlastní sekce komentářů, účtované podle zvědavosti tvých čtenářů.

Na cenové rozhodnutí neexistuje inženýrská odpověď. Takže: ne.

Threads: zatím ne

Číst dál

Nasazení, které odmítne smazat tvůj web

./blog.sh v1.0 — díl 8 z 15

Petr
Pustil jsem --prune po rozbitém sestavení.
Pavel
Takže máš prázdný web.
Petr
Ne. Nasazení odmítlo.
Pavel
…to chci.

To je celá funkce: nasazení předpokládá, že velká změna je chyba, dokud neřekneš jinak.

Jak to funguje

Každé nasazení si vede manifest – SHA-256, velikost a mtime každého nahraného souboru. Další nasazení se proti němu porovná a sáhne jen na to, co se skutečně změnilo. Rychlé, nudné, správné.

Zajímavá část je to, co se stane, než se cokoli nahraje. Když počet souborů od posledního nasazení klesl o víc než 20 %, celé se to zastaví. Tohle je skutečný výstup z tohoto webu – na minutu jsem odstavil polovinu složek sestavení z dohledu a požádal o nasazení:

Číst dál

Žádné gemy (jedna hvězdička)

./blog.sh v1.0 — díl 9 z 15

./blog.sh nepotřebuje žádné gemy. Tady je ta jedna hvězdička u té věty, protože tvrzení bez své výjimky za moc nestojí.

Napřed tvrzení: nula gemů, nula balíčků z npm, žádný Bundler, žádný lockfile, žádná složka node_modules potichu tloustnoucí ve tmě. Ruby 2.7 nebo novější a bash. Naklonuj a běží.

Hvězdička, hned na začátku

Dva volitelné widgety postranního panelu – Pixelfed a obecné RSS – rozebírají XML pomocí rexml. To je výchozí gem: přichází přibalený s každou normální instalací Ruby, takže pro většinu lidí tvrzení platí, jak je napsáno. Ale některé linuxové distribuce Ruby rozdělují do balíčků a výchozí gemy dávají do samostatného.

Takže: když ty dva widgety zapneš na Ruby z distribučního balíčku a sestavení si stěžuje na rexml, jsi ta hvězdička. Dvě cesty ven – nainstalovat balíček své distribuce, něco jako ruby-rexml, nebo gem install rexml. Tak či tak je to jeden balíček, jednou, a jen když ty widgety vůbec používáš.

Číst dál

$ ./blog.sh add

./blog.sh v1.0 — díl 10 z 15

Z prázdného terminálu ke sdílitelnému konceptu, bez střihu.

Jeden záběr, padesát sedm vteřin, bez komentáře a bez střihů. Telefon se skládací klávesnicí a ./blog.sh běžící přes SSH na obrazovce za ním.

Co sleduješ:

Číst dál

Sedm klíčů

./blog.sh v1.0 — díl 11 z 15

Titulní strana ve výchozí modré paletě
Výchozí modrá – paleta, se kterou tento web přichází
Číst dál

Proč jsem to postavil (38 vteřin)

./blog.sh v1.0 — díl 12 z 15

Proč jsem to postavil – 38 vteřin, jeden záběr, nahráno telefonem

Třicet osm vteřin, nahráno telefonem, bez střihu. Psaná verze je níže, kdybys raději četl.

Statických generátorů webů jsou stovky. Stejně jsem napsal další a důvody se vejdou do necelé minuty.

Moje příspěvky bydlely čtrnáct let na velkých platformách. Některé z těch platforem změnily pravidla. Některé prostě zmizely. Trávím celý den v terminálu, takže jsem chtěl i svůj blog psát tam. A malý osobní blog nepotřebuje databázi – soubory stačí.

Takže: příspěvky jsou soubory, web je sestavení a komentáře žijí na Fediverse. To je celá myšlenka.

Některé příspěvky jsou rozhovory

./blog.sh v1.0 — díl 13 z 15

Petr
Potřebuju do příspěvku dialog.
./blog.sh
Napiš ho do plotu chat. Jméno, dvojtečka, řádek.
Petr
To je všechno?
./blog.sh
Řádek bez dvojtečky pokračuje v tom nad ním.
Petr
A Markdown uvnitř?
./blog.sh
Funguje. Tučné, odkazy, kód – jako obvykle.
Petr
Každý jiný engine na tohle chce plugin.
./blog.sh
Každý jiný engine chce plugin i na tabulky.

Zdroj té výměny je přesně to, co jsi právě četl – jméno mluvčího, dvojtečka, řádek:

Petr: Potřebuju do příspěvku dialog.
./blog.sh: Napiš ho do plotu chat. Jméno, dvojtečka, řádek.
Petr: To je všechno?

Zabal ty řádky do plotu kódu označeného chat a máš hotovo.

Právě jsi přečetl tu funkci. To je celá dokumentace.

Čtrnáct let a každý obrázek se vrátil domů

./blog.sh v1.0 — díl 14 z 15

Chybová stránka prohlížeče s hláškou, že IP adresu serveru distilleryimage1.instagram.com se nepodařilo najít
distilleryimage1.instagram.com, o čtrnáct let později

V roce 2012 jsem dal fotku na Instagram. Odkaz pořád funguje. Fotka ne.

Číst dál

./blog.sh 1.0

./blog.sh v1.0 — díl 15 z 15

./blog.sh 1.0 je venku.

Blogovací engine, který je složka a shellový skript. Standardní knihovna Ruby a bash – žádná databáze, žádné gemy, žádná administrace. Komentáře žijí na Fediverse. Osm importérů, aby měly tvoje archivy z Tumblru a Twitteru kam jít. Licence MIT.

Web, který čteš, je ten engine, jak běží sám sebou.