O kontrole odkazov som na tomto blogu písal už predtým. Najprv v Mastering internal links in Zola, kde som sa venoval konverzii sebaodkazujúcich linkov na internú syntax Zoly, potom vo Fragment link checking in Astro, kde som nastavil validáciu kotiev pomocou lychee po migrácii na Astro. Oba sa zameriavali na interné odkazy. Externé odkazy zostali nekontrolované a začínalo to byť viditeľné.

Problém s kontrolou všetkého #

Naivný prístup by bol pustiť lychee na celý web a opraviť, čo nahlási. Skúsil som to. V praxi to nefunguje. S viac ako 250 príspevkami existujú stovky externých odkazov. Mnohé smerujú na stránky ako GitHub alebo Reddit, ktoré aktívne blokujú automatizované požiadavky. Vracajú 403 alebo 429 bez ohľadu na to, ako slušne sa opýtate. Skončíte so stenou falošných pozitív a bez možnosti zistiť, ktoré odkazy sú skutočne nefunkčné.

Spustenie kontroly celého webu tiež trvá dlho. Aj so štedrými timeoutmi a opakovaniami sa pozeráte na minúty čakania len na to, aby ste dostali report, ktorému nemôžete úplne dôverovať.

Kontrola len toho, čoho sa dotknete #

Prístup, na ktorom som sa usadil, je jednoduchší. Keď pushnem zmeny, git pre-push hook spustí lychee len na markdown súboroch, ktoré boli skutočne zmenené v pushnutých commitoch. Ak upravujem príspevok z roku 2021, skontrolujú sa len externé odkazy toho príspevku. Zvyšok blogu ostáva nedotknutý.

Hook číta push range zo stdin, aby zistil, ktoré commity sa pushujú, potom filtruje .md súbory:

pushed_md=$(git diff --name-only --diff-filter=ACM "$push_range" | grep '\.md$' || true)

Potom lychee beží len na týchto súboroch:

echo "$pushed_md" | xargs lychee \
    --base-url 'http://localhost:4321' \
    --exclude 'localhost' \
    --exclude '127\.0\.0\.1' \
    --no-progress \
    --max-retries 2 \
    --timeout 20

--base-url je tam preto, lebo root-relatívne odkazy v markdowne ako [feed](/atom.xml) by inak zlyhali pri resolovaní. S base URL nastavenou na lokálny dev server (ktorý už beží z kontroly interných odkazov) sa tieto resolujú na http://localhost:4321/atom.xml a potom sa vylúčia filtrom na localhost.

Kontrola beží len ako varovanie. Ak lychee nahlási zlyhania, push pokračuje ďalej. Je to zámerné, pretože niektoré stránky budú vždy odmietať link checkery a nechcem, aby falošné pozitívum blokovalo môj workflow.

Udržiavanie konzistentných odkazov naprieč jazykmi #

Odkedy som urobil blog dvojjazyčným, existuje ďalší rozmer kontroly odkazov. Každý príspevok existuje ako en.md a sk.md v tom istom priečinku. Text sa líši, no odkazy sa neprekladajú, preto by sa mali udržiavať identické. Ak opravím nefunkčný odkaz v anglickej verzii, ale zabudnem na slovenskú, alebo naopak, skončím s rozdielnym obsahom.

Pre-push hook teraz tiež extrahuje všetky URL z oboch jazykových súborov a porovnáva ich. Ak je nesúlad, push sa zablokuje:

Links mismatch in my-post:
  only in en: https://example.com/old-link
  only in sk: https://example.com/new-link

Toto už zachytilo niekoľko prípadov, keď bola URL aktualizovaná v jednom jazyku, ale nie v druhom. Extrakcia URL najprv odstráni frontmatter a prehľadá celé telo, nielen sekciu odkazov na konci.

Prečo sa obťažovať s opravou odkazov a 301 presmerovaní #

Nefunkčné odkazy sú zlý zážitok pre čitateľov. Niekto klikne na odkaz s očakávaním, že sa dozvie viac, a miesto toho dostane 404. Narúša to dôveru v obsah, obzvlášť pri technických článkoch, kde si čitatelia potrebujú overiť tvrdenia alebo postupovať podľa dokumentácie.

Vyhľadávače sa o toto tiež zaujímajú. Odchádzajúce odkazy na mŕtve stránky môžu signalizovať, že obsah je zastaraný alebo neudržiavaný.

Lychee štandardne hlási aj 301 presmerovania a stojí za to tomu venovať pozornosť. 301 znamená, že cieľ sa trvalo presunul. Odkaz stále funguje, pretože prehliadač nasleduje presmerovanie, ale existujú dobré dôvody na jeho aktualizáciu:

  1. Extra round trip - každé presmerovanie pridáva latenciu. Prehliadač musí urobiť dodatočný request predtým, než sa dostane k skutočnému obsahu.
  2. Reťazce presmerovaní - dnešný 301 sa môže stať zajtrajším 301 na ďalšiu URL. Reťazce degradujú výkon a nakoniec sa niektoré odkazy v reťazci môžu úplne rozbiť.
  3. Presnosť obsahu - ak sa projekt presunul z jednej domény na inú, stará URL môže jedného dňa prestať presmerovávať. Aktualizácia teraz predchádza budúcemu zlyhaniu.
  4. Signál čerstvosti - aktualizované odkazy ukazujú, že obsah je udržiavaný. Čitatelia aj vyhľadávače si to všimnú.

Keď lychee nahlási 301, aktualizujem URL v príspevku tak, aby smerovala priamo na nové umiestnenie. Trvá to pár sekúnd na odkaz a predchádza to problémom v budúcnosti.

Čo z toho nakoniec vyšlo #

Pre-push hook teraz robí tri veci v poradí:

  1. Kontroluje interné odkazy a fragmenty (blokuje push pri zlyhaní)
  2. Porovnáva URL medzi jazykovými pármi (blokuje push pri nesúlade)
  3. Kontroluje externé odkazy v dotknutých súboroch (varuje, ale neblokuje)

Poradie je dôležité. Ak kontrola jazykových párov nájde nesúlad, nemá zmysel spúšťať externú kontrolu, pretože niektoré z tých odkazov sú zjavne nesprávne. Externá kontrola beží posledná a len na súboroch, ktoré prešli predchádzajúcimi kontrolami.

Nie je to dokonalé. Plný audit webu by zachytil viac. Ale je to praktické a zachytáva problémy práve vtedy, keď pracujem na príspevku, čo je presne vtedy, keď mám kontext na ich opravu. Enjoy!

Odkazy #