{"authors":[{"name":"Евгений Кушнаренко","url":"https://kushnaren.co/about/"}],"description":"Личный блог Евгения Кушнаренко: инженерия, мышление, наблюдения. Длинные тексты о смыслах и системах.","favicon":"https://kushnaren.co/favicon-32x32.png","feed_url":"https://kushnaren.co/feed.json","home_page_url":"https://kushnaren.co/","icon":"https://kushnaren.co/android-chrome-192x192.png","items":[{"content_html":"\u003ch2 class=\"heading\" id=\"почему-это-стоит-прочитать\"\u003e\n  Почему это стоит прочитать\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%8d%d1%82%d0%be-%d1%81%d1%82%d0%be%d0%b8%d1%82-%d0%bf%d1%80%d0%be%d1%87%d0%b8%d1%82%d0%b0%d1%82%d1%8c\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eС прошлого разбора (v0.160.1, 8 апреля) накопилось пять релизов: \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.161.0\"\u003e0.161.0\u003c/a\u003e, \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.161.1\"\u003e0.161.1\u003c/a\u003e, \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.162.0\"\u003e0.162.0\u003c/a\u003e, \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.163.0\"\u003e0.163.0\u003c/a\u003e и патчи до \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.163.3\"\u003e0.163.3\u003c/a\u003e (18 июня). Это не один минор, а целый квартал развития — поэтому разбор догоняющий.\u003c/p\u003e\n\u003cp\u003eЕсли сжать до двух заголовков, их два:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eAVIF\u003c/strong\u003e — Hugo научился кодировать и декодировать AVIF прямо в image pipeline. Третий формат рядом с JPEG и WebP, и для фото-блога это самое интересное.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eХардненинг безопасности\u003c/strong\u003e — Node-инструменты теперь запускаются с \u003ccode\u003e--permission\u003c/code\u003e, \u003ccode\u003etext/html\u003c/code\u003e-контент запрещён по умолчанию, ужесточены \u003ccode\u003esecurity.http.urls\u003c/code\u003e, плюс транзитом приехали XSS-фиксы из Go 1.26.3.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eОстальное — приятные улучшения конфигурации (slice-based permalinks, гибкие идентификаторы в именах файлов, вложенные vars в \u003ccode\u003ecss.Build\u003c/code\u003e) и порция депрекейтов. Breaking changes есть, но точечные — разберём, кого они задевают.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"главное-за-один-взгляд\"\u003e\n  Главное за один взгляд\n  \u003ca class=\"anchor\" href=\"#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d0%be%d0%b5-%d0%b7%d0%b0-%d0%be%d0%b4%d0%b8%d0%bd-%d0%b2%d0%b7%d0%b3%d0%bb%d1%8f%d0%b4\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003ev0.161.0 / 0.161.1 (28–29 апреля)\u003c/strong\u003e — фичи + первый хардненинг:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eNode-инструменты (PostCSS, Babel, TailwindCSS) запускаются с флагом \u003ccode\u003e--permission\u003c/code\u003e. Нужен \u003cstrong\u003eNode ≥ 22\u003c/strong\u003e, а Tailwind теперь только как npm-пакет (standalone-бинарь больше не поддерживается).\u003c/li\u003e\n\u003cli\u003eУжесточены дефолты \u003ccode\u003esecurity.http.urls\u003c/code\u003e; добавлен \u003ccode\u003esecurity.node.permissions\u003c/code\u003e (включая \u003ccode\u003eAllowChildProcess\u003c/code\u003e).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSlice-based permalinks\u003c/strong\u003e — конфигурация permalinks с \u003ccode\u003etarget\u003c/code\u003e-матчерами (как в \u003ccode\u003ecascade\u003c/code\u003e).\u003c/li\u003e\n\u003cli\u003eГибкая схема идентификаторов в именах файлов: \u003ccode\u003e_language_en_\u003c/code\u003e, \u003ccode\u003e_role_\u003c/code\u003e, \u003ccode\u003e_version_\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eВложенные vars в \u003ccode\u003ecss.Build\u003c/code\u003e / \u003ccode\u003ecss.Sass\u003c/code\u003e: \u003ccode\u003e@import \u0026quot;hugo:vars/dark\u0026quot;\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003ev0.162.0 / 0.162.1 (26–28 мая)\u003c/strong\u003e — AVIF + security:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e🎯 \u003cstrong\u003eПоддержка AVIF\u003c/strong\u003e (encoder + decoder) в image pipeline.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003etext/html\u003c/code\u003e-контент запрещён по умолчанию\u003c/strong\u003e — новая политика \u003ccode\u003esecurity.allowContent\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eGo 1.26.3: фиксы XSS в \u003ccode\u003ehtml/template\u003c/code\u003e (CVE-2026-39826, CVE-2026-39823).\u003c/li\u003e\n\u003cli\u003eПроверка \u003ccode\u003esecurity.http.urls\u003c/code\u003e на каждом редирект-хопе \u003ccode\u003eresources.GetRemote\u003c/code\u003e; отказ от symlink-записей в \u003ccode\u003eresources.Get\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003ev0.163.0 (8 июня)\u003c/strong\u003e — тюнинг AVIF + депрекейты:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eAVIF по умолчанию \u003ccode\u003equality = 60\u003c/code\u003e (≈ JPEG/WebP q75), content-aware chroma subsampling через \u003ccode\u003ehint\u003c/code\u003e, качество настраивается \u003cstrong\u003eper-format\u003c/strong\u003e.\u003c/li\u003e\n\u003cli\u003eДепрекейт глобального \u003ccode\u003eimaging.quality\u003c/code\u003e и \u003ccode\u003eImaging.Compression\u003c/code\u003e (переезжают в per-format конфиги).\u003c/li\u003e\n\u003cli\u003eДобавлен \u003ccode\u003e.IsBranch\u003c/code\u003e, депрекейт \u003ccode\u003e.IsNode\u003c/code\u003e; депрекейт \u003ccode\u003ebaseUrl\u003c/code\u003e в jsconfig.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003ev0.163.1 / 0.163.2 / 0.163.3 (11–18 июня)\u003c/strong\u003e — патчи:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eSecurity: bump \u003ccode\u003egolang.org/x/image\u003c/code\u003e, нормализация integer-IPv4 в \u003ccode\u003ehttp.urls\u003c/code\u003e, отказ от symlink в \u003ccode\u003eos.ReadDir/ReadFile/Stat/FileExists\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eEscape \u003ccode\u003elang\u003c/code\u003e в дефолтном рендере код-блоков; сохранение non-ASCII пробела после summary-разделителя; поддержка вариантов конфигов babel/postcss; фикс регрессии коллизии имён page/section.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eДальше — что это значит на практике.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"avif-третий-формат-в-pipeline-\"\u003e\n  AVIF: третий формат в pipeline 🖼️\n  \u003ca class=\"anchor\" href=\"#avif-%d1%82%d1%80%d0%b5%d1%82%d0%b8%d0%b9-%d1%84%d0%be%d1%80%d0%bc%d0%b0%d1%82-%d0%b2-pipeline-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eГлавная фича квартала. В 0.162.0 Hugo получил полноценную обработку AVIF — и кодер, и декодер (issue \u003ca href=\"https://github.com/gohugoio/hugo/issues/7837\"\u003e#7837\u003c/a\u003e, висел с 2020 года). AVIF при том же визуальном качестве заметно легче JPEG и обычно легче WebP, плюс умеет HDR (у Hugo есть \u003ca href=\"https://github.com/bep/hdrsdr.com\"\u003eдемо-сайт\u003c/a\u003e, который имеет смысл смотреть на HDR-экране вроде Retina).\u003c/p\u003e\n\u003cp\u003eВ 0.163.0 фичу довели до ума:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eДефолтный \u003ccode\u003equality\u003c/code\u003e для AVIF опустили до \u003cstrong\u003e60\u003c/strong\u003e — оказалось, JPEG/WebP с q75 сопоставимы с AVIF q60. Не гоните качество вверх по привычке.\u003c/li\u003e\n\u003cli\u003eПоявился \u003ccode\u003ehint\u003c/code\u003e (как у WEBP): для \u003ccode\u003elossy\u003c/code\u003e фото по умолчанию используется YUV420 chroma subsampling, а для текста/иконок/схем — 444. Это резко снижает память на кодирование больших картинок.\u003c/li\u003e\n\u003cli\u003eКачество можно задавать \u003cstrong\u003eна формат\u003c/strong\u003e:\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eimaging\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c\"\u003e# глобальный quality теперь deprecated\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eimaging\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ewebp\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003equality\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e80\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eimaging\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eavif\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003equality\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e60\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ehint\u003c/span\u003e    \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;photo\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eВ шаблоне AVIF получается так же, как WebP — через \u003ccode\u003e.Process\u003c/code\u003e / метод ресайза:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$img\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e:=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eresources\u003c/span\u003e\u003cspan class=\"na\"\u003e.Get\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;img/cover.jpg\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$avif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e:=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$img\u003c/span\u003e\u003cspan class=\"na\"\u003e.Process\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;resize 1600x avif\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003epicture\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003esource\u003c/span\u003e \u003cspan class=\"na\"\u003esrcset\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$avif\u003c/span\u003e\u003cspan class=\"na\"\u003e.RelPermalink\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003etype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;image/avif\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003esource\u003c/span\u003e \u003cspan class=\"na\"\u003esrcset\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003e$img\u003c/span\u003e\u003cspan class=\"na\"\u003e.Process\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;resize 1600x webp\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"na\"\u003e.RelPermalink\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003etype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;image/webp\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003eimg\u003c/span\u003e \u003cspan class=\"na\"\u003esrc\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003e$img\u003c/span\u003e\u003cspan class=\"na\"\u003e.Resize\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;1600x\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"na\"\u003e.RelPermalink\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003ealt\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;…\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003epicture\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003cstrong\u003eЧто это значит для нашего блога.\u003c/strong\u003e Раздел \u003ccode\u003e/photos\u003c/code\u003e уже генерит 800/1600/2400 px в JPEG + WebP и отдаёт через \u003ccode\u003e\u0026lt;picture\u0026gt;\u003c/code\u003e/\u003ccode\u003esrcset\u003c/code\u003e. Добавить AVIF — это правка шаблона, который собирает \u003ccode\u003e\u0026lt;source\u0026gt;\u003c/code\u003e-ы (плюс per-format \u003ccode\u003equality\u003c/code\u003e в \u003ccode\u003ehugo.toml\u003c/code\u003e): первым \u003ccode\u003e\u0026lt;source type=\u0026quot;image/avif\u0026quot;\u0026gt;\u003c/code\u003e, дальше WebP, в конце JPEG-фолбэк. Браузеры берут первый поддерживаемый. На фотографиях с retina это даст ощутимую экономию трафика при том же качестве. В этот заход не делаю — но это очевидный следующий шаг для фото-ленты.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"хардненинг-безопасности-\"\u003e\n  Хардненинг безопасности 🔒\n  \u003ca class=\"anchor\" href=\"#%d1%85%d0%b0%d1%80%d0%b4%d0%bd%d0%b5%d0%bd%d0%b8%d0%bd%d0%b3-%d0%b1%d0%b5%d0%b7%d0%be%d0%bf%d0%b0%d1%81%d0%bd%d0%be%d1%81%d1%82%d0%b8-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eСамая «breaking» часть. Изменения копились через все релизы:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNode-инструменты под \u003ccode\u003e--permission\u003c/code\u003e (0.161.0).\u003c/strong\u003e PostCSS, Babel и TailwindCSS теперь запускаются с Node permission-флагом и правами из \u003ccode\u003esecurity.node.permissions\u003c/code\u003e. Следствия:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eнужен \u003cstrong\u003eNode ≥ 22\u003c/strong\u003e;\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ecss.TailwindCSS\u003c/code\u003e требует Tailwind CLI как \u003cstrong\u003enpm-пакет\u003c/strong\u003e — standalone-бинарь больше не годится.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЕсли вы собираете CSS через Hugo Pipes с Tailwind/PostCSS — это первое, что стоит проверить перед обновлением.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eЗапрет \u003ccode\u003etext/html\u003c/code\u003e-контента по умолчанию (0.162.0).\u003c/strong\u003e Новая политика \u003ccode\u003esecurity.allowContent\u003c/code\u003e гейтит, какие media-типы допустимы для страниц в \u003ccode\u003e/content\u003c/code\u003e. \u003ccode\u003etext/html\u003c/code\u003e теперь \u003cstrong\u003eзапрещён по умолчанию\u003c/strong\u003e. Сайты, где есть руками написанные \u003ccode\u003e.html\u003c/code\u003e-файлы в \u003ccode\u003econtent/\u003c/code\u003e (или их эмитит content adapter), вернут доступ явно:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003esecurity\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eallowContent\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;.*\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003cstrong\u003eПрочее (0.162.0 → 0.163.3):\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003esecurity.http.urls\u003c/code\u003e перепроверяется на \u003cstrong\u003eкаждом\u003c/strong\u003e редирект-хопе в \u003ccode\u003eresources.GetRemote\u003c/code\u003e — мимо валидации больше не проскочить через 30x.\u003c/li\u003e\n\u003cli\u003eSymlink-записи отклоняются в \u003ccode\u003eresources.Get\u003c/code\u003e, а в 0.163.1 — ещё и в \u003ccode\u003eos.ReadDir/ReadFile/Stat/FileExists\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eGo подняли до 1.26.3 с фиксами XSS в \u003ccode\u003ehtml/template\u003c/code\u003e (CVE-2026-39826, CVE-2026-39823) — это касается любого сайта, рендерящего недоверенный ввод в шаблонах.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЛюбопытная ремарка от мейнтейнера в release notes 0.163.1: всплеск security-репортов в последнее время — это не «Hugo стал менее безопасным», а работа новых AI-инструментов, которые берут \u003ca href=\"https://gohugo.io/about/security/\"\u003esecurity-модель Hugo\u003c/a\u003e за baseline и методично её простукивают.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"slice-based-permalinks-\"\u003e\n  Slice-based permalinks 🔗\n  \u003ca class=\"anchor\" href=\"#slice-based-permalinks-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eВ 0.161.0 конфигурация \u003ccode\u003epermalinks\u003c/code\u003e стала гибче (старая map-форма продолжает работать). Теперь это список с теми же \u003ccode\u003etarget\u003c/code\u003e-матчерами, что и в \u003ccode\u003ecascade\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003epermalinks\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ekind\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epage\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/books/**\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003epattern\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e/books/:year/:slug/\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ekind\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epage\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003epattern\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e/other/:slug/\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eУдобно, когда для разных секций нужны разные схемы URL без дублирования настроек. Нашему блогу с плоской структурой \u003ccode\u003eposts/\u003c/code\u003e это пока избыточно, но знать про механизм полезно — особенно если когда-нибудь заведём разделы со своими правилами URL.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"гибкие-идентификаторы-в-именах-файлов-\"\u003e\n  Гибкие идентификаторы в именах файлов 🏷️\n  \u003ca class=\"anchor\" href=\"#%d0%b3%d0%b8%d0%b1%d0%ba%d0%b8%d0%b5-%d0%b8%d0%b4%d0%b5%d0%bd%d1%82%d0%b8%d1%84%d0%b8%d0%ba%d0%b0%d1%82%d0%be%d1%80%d1%8b-%d0%b2-%d0%b8%d0%bc%d0%b5%d0%bd%d0%b0%d1%85-%d1%84%d0%b0%d0%b9%d0%bb%d0%be%d0%b2-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eТоже из 0.161.0. Раньше язык кодировался как \u003ccode\u003emypost.en.md\u003c/code\u003e. Теперь доступна расширяемая схема префиксов в имени файла:\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eПрефикс\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eЧто задаёт\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eГде применимо\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e_language_\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eЯзык\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eКонтент и layout-файлы\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e_role_\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eРоль\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eКонтент и layout-файлы\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e_version_\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eВерсия\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eКонтент и layout-файлы\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e_outputformat_\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eOutput format\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eLayout-файлы\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e_kind_\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003ePage kind\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eLayout-файлы\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e\u003ccode\u003econtent/mypost._language_en_.md\u003c/code\u003e эквивалентно \u003ccode\u003econtent/mypost.en.md\u003c/code\u003e, но открывает дорогу к версиям и ролям контента. Для одноязычного блога — мимо, для документации с версиями — заметное упрощение.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"cssbuild-вложенные-vars-\"\u003e\n  css.Build: вложенные vars 🎨\n  \u003ca class=\"anchor\" href=\"#cssbuild-%d0%b2%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%bd%d1%8b%d0%b5-vars-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eРазвитие фичи из 0.160. К \u003ccode\u003e@import \u0026quot;hugo:vars\u0026quot;\u003c/code\u003e добавились \u003cstrong\u003eвложенные\u003c/strong\u003e наборы — \u003ccode\u003e@import \u0026quot;hugo:vars/\u0026lt;name\u0026gt;\u0026quot;\u003c/code\u003e. Классический сценарий — палитры под \u003ccode\u003eprefers-color-scheme\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eprimary\u003c/span\u003e    \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;#000000\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ebackground\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;#ffffff\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003edark\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eprimary\u003c/span\u003e    \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;#ffffff\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ebackground\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;#000000\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e@\u003c/span\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;hugo:vars\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e@\u003c/span\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;hugo:vars/dark\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nt\"\u003eprefers-color-scheme\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"nt\"\u003edark\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eАккуратно, но не для нас: переключатель дня/ночи на блоге работает через JS и \u003ccode\u003elocalStorage\u003c/code\u003e (явный выбор пользователя, без авто), а не через \u003ccode\u003eprefers-color-scheme\u003c/code\u003e. Палитра живёт в \u003ccode\u003e:root\u003c/code\u003e. Так что фича в копилку «знать», но не «внедрять».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"депрекейты-и-мелочи\"\u003e\n  Депрекейты и мелочи\n  \u003ca class=\"anchor\" href=\"#%d0%b4%d0%b5%d0%bf%d1%80%d0%b5%d0%ba%d0%b5%d0%b9%d1%82%d1%8b-%d0%b8-%d0%bc%d0%b5%d0%bb%d0%be%d1%87%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003e.IsNode\u003c/code\u003e → \u003ccode\u003e.IsBranch\u003c/code\u003e\u003c/strong\u003e (0.163.0): если в шаблонах есть \u003ccode\u003e.IsNode\u003c/code\u003e, мигрируйте на \u003ccode\u003e.IsBranch\u003c/code\u003e (issue \u003ca href=\"https://github.com/gohugoio/hugo/issues/11574\"\u003e#11574\u003c/a\u003e).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eГлобальный \u003ccode\u003eimaging.quality\u003c/code\u003e\u003c/strong\u003e (0.163.0): помечен deprecated в пользу per-format (\u003ccode\u003e[imaging.webp]\u003c/code\u003e, \u003ccode\u003e[imaging.avif]\u003c/code\u003e, …). Старая форма пока работает и даже не всегда ругается — но направление задано.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ejsconfig \u003ccode\u003ebaseUrl\u003c/code\u003e\u003c/strong\u003e убран (был deprecated).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eСохранение non-ASCII пробела после summary-разделителя\u003c/strong\u003e (0.163.3) — мелочь, но именно для кириллических текстов с `\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e` это правильное поведение.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eФикс коллизии имён page/section\u003c/strong\u003e (0.163.3, \u003ca href=\"https://github.com/gohugoio/hugo/issues/15046\"\u003e#15046\u003c/a\u003e) — регрессия, всплывшая в 0.163.x; ещё одна причина брать самый свежий патч, а не 0.163.0.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 class=\"heading\" id=\"что-это-значит-для-нашего-блога\"\u003e\n  Что это значит для нашего блога\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%8d%d1%82%d0%be-%d0%b7%d0%bd%d0%b0%d1%87%d0%b8%d1%82-%d0%b4%d0%bb%d1%8f-%d0%bd%d0%b0%d1%88%d0%b5%d0%b3%d0%be-%d0%b1%d0%bb%d0%be%d0%b3%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПрогнал блог на 0.163.3 — собирается чисто, \u003cstrong\u003e90 страниц, 0 ошибок\u003c/strong\u003e, 52 изображения обработаны без проблем. Конкретика:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eЗапрет \u003ccode\u003etext/html\u003c/code\u003e\u003c/strong\u003e нас не задел — в \u003ccode\u003econtent/\u003c/code\u003e нет руками-сверстанных \u003ccode\u003e.html\u003c/code\u003e, всё в Markdown. \u003ccode\u003esecurity.allowContent\u003c/code\u003e трогать не нужно.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eNode-permission\u003c/strong\u003e мимо — Tailwind/PostCSS через Pipes не используем, Pagefind живёт отдельным npm-инструментом.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003elanguageCode\u003c/code\u003e → \u003ccode\u003elocale\u003c/code\u003e\u003c/strong\u003e: попутно почистил deprecation в \u003ccode\u003ehugo.toml\u003c/code\u003e (\u003ccode\u003elanguageCode = 'ru-RU'\u003c/code\u003e → \u003ccode\u003elocale = 'ru-RU'\u003c/code\u003e). Этот warning ушёл. Оставшиеся три (\u003ccode\u003e.Site.LanguageCode\u003c/code\u003e, \u003ccode\u003e.Language.LanguageCode\u003c/code\u003e, \u003ccode\u003e.Language.Direction\u003c/code\u003e) приходят из шаблонов темы \u003ccode\u003etypo\u003c/code\u003e — уйдут, когда обновится тема.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAVIF\u003c/strong\u003e — главная зацепка на будущее: добавить третий \u003ccode\u003e\u0026lt;source\u0026gt;\u003c/code\u003e в фото-pipeline и срезать вес ленты \u003ccode\u003e/photos\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 class=\"heading\" id=\"чек-лист-обновления-\"\u003e\n  Чек-лист обновления ✅\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b5%d0%ba-%d0%bb%d0%b8%d1%81%d1%82-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eПрыгайте сразу на 0.163.3\u003c/strong\u003e, а не на 0.163.0 — патчи .1/.2/.3 закрывают security-дыры и регрессию коллизии имён.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eЕсли собираете CSS через Tailwind/PostCSS/Babel в Pipes\u003c/strong\u003e — поставьте Node ≥ 22 и Tailwind как npm-пакет; проверьте \u003ccode\u003esecurity.node.permissions\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eЕсли в \u003ccode\u003econtent/\u003c/code\u003e есть \u003ccode\u003e.html\u003c/code\u003e-файлы\u003c/strong\u003e — добавьте \u003ccode\u003esecurity.allowContent = ['.*']\u003c/code\u003e, иначе страницы выпадут из сборки.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eПочистите депрекейты:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# languageCode → locale в конфиге\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# .IsNode → .IsBranch в шаблонах\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egrep -rn \u003cspan class=\"s1\"\u003e\u0026#39;\\.IsNode\u0026#39;\u003c/span\u003e layouts/ themes/\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eЕсли работаете с изображениями\u003c/strong\u003e — присмотритесь к AVIF и per-format \u003ccode\u003equality\u003c/code\u003e; глобальный \u003ccode\u003eimaging.quality\u003c/code\u003e уводите в \u003ccode\u003e[imaging.\u0026lt;format\u0026gt;]\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eСтандартный прогон\u003c/strong\u003e и сверка warning\u0026rsquo;ов до/после:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo --minify --gc\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 class=\"heading\" id=\"итог-\"\u003e\n  Итог 🧠\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%82%d0%be%d0%b3-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003e0.161–0.163 — это квартал «инфраструктурного взросления» Hugo: новый формат изображений (AVIF), методичный security-хардненинг и более гибкая конфигурация (permalinks, идентификаторы файлов, вложенные CSS-vars). Громкая фича ровно одна — AVIF; всё остальное либо про безопасность, либо про удобство.\u003c/p\u003e\n\u003cp\u003eBreaking changes реальны, но адресные: они бьют по сайтам с Tailwind-в-Pipes и с HTML-контентом в \u003ccode\u003econtent/\u003c/code\u003e. Если вы (как наш блог) пишете на чистом Markdown и не гоняете Node-сборку CSS — переход бесшовный, достаточно поднять версию и пробежаться по депрекейтам.\u003c/p\u003e\n\u003cp\u003eЕсли сидите на 0.160 или раньше — есть смысл прыгать сразу на 0.163.3, мимо промежуточных: так вы заодно получите все security-фиксы одним движением.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"источники\"\u003e\n  Источники\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eРелиз 0.161.0: \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.161.0\"\u003egohugoio/hugo v0.161.0\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eРелиз 0.162.0: \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.162.0\"\u003egohugoio/hugo v0.162.0\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eРелиз 0.163.0: \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.163.0\"\u003egohugoio/hugo v0.163.0\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eРелиз 0.163.3: \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.163.3\"\u003egohugoio/hugo v0.163.3\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eКонфигурация AVIF: \u003ca href=\"https://gohugo.io/configuration/imaging/#avif\"\u003egohugo.io/configuration/imaging/#avif\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eSecurity-модель Hugo: \u003ca href=\"https://gohugo.io/about/security/\"\u003egohugo.io/about/security/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","date_modified":"2026-06-19T07:49:52+03:00","date_published":"2026-06-19T07:49:52+03:00","id":"https://kushnaren.co/posts/2026-06-19-hugo-0-161-0-163-avif-i-hardening/","image":"https://kushnaren.co/og/2026-06-19-hugo-0-161-0-163-avif-i-hardening.png","summary":"Подробный обзор Hugo 0.161.0–0.163.3: поддержка AVIF (encoder+decoder) и его тюнинг (quality 60, content-aware chroma), хардненинг безопасности (Node permission-флаг, security.allowContent, http.urls, Go 1.26.3 XSS-фиксы), slice-based permalinks с target-матчерами, гибкая схема идентификаторов в именах файлов, депрекейты IsNode и глобального imaging.quality. Практические сценарии и чек-лист миграции с 0.160.","tags":["hugo","релизы","devops"],"title":"Что нового в Hugo 0.161–0.163: AVIF, хардненинг и гибкие permalinks 🚀","url":"https://kushnaren.co/posts/2026-06-19-hugo-0-161-0-163-avif-i-hardening/"},{"content_html":"\u003ch2 class=\"heading\" id=\"что-это-вообще-такое\"\u003e\n  Что это вообще такое\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%8d%d1%82%d0%be-%d0%b2%d0%be%d0%be%d0%b1%d1%89%d0%b5-%d1%82%d0%b0%d0%ba%d0%be%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЕсли ты когда-нибудь просил AI-помощника (Claude, ChatGPT, Cursor) сделать\nтебе сайт, ты замечал одно и то же: получается вроде неплохо, но\nподозрительно «как у всех». Тёмно-синий фон, фиолетовый градиент в\nзаголовке, карточки с лёгкой тенью, шрифт Inter. Узнаваемый «AI-look».\u003c/p\u003e\n\u003cp\u003eПричина банальная: большие языковые модели учились на одних и тех же\nтипичных шаблонах. Без отдельных указаний они выдают усреднённый\nдизайн, в котором повторяются одни и те же стандартные ошибки:\nградиент в заголовке через \u003ccode\u003ebackground-clip\u003c/code\u003e, серый текст на цветном\nфоне, бесконечные одинаковые карточки в сетке, ноль характера.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://impeccable.style\"\u003eImpeccable\u003c/a\u003e от Paul Bakaus решает именно эту\nзадачу. Сам автор позиционирует его как \u003cstrong\u003e«дизайн-словарь для\nAI-помощников по коду»\u003c/strong\u003e: набор из одного навыка и 23 команд, которые\nдают AI и человеку общий понятийный аппарат. Каждая команда отвечает\nза свою дизайн-дисциплину: типографика, цвет, ритм пространства,\nвзаимодействия, копирайтинг, элевация (тени и слои), анти-паттерны.\nПлюс отдельный список из 25 типичных «AI-ляпов», которые\nдетектор ловит автоматически.\u003c/p\u003e\n\u003cp\u003eЧто важно: impeccable — не библиотека CSS-токенов и не редактор\nмакетов. Он живёт в твоём боевом коде, читает существующие исходники,\nрендеренный HTML, дизайн-документы, и вместе с AI правит то, что уже\nесть. Не «нарисуй мне сайт с нуля», а «доведи существующий до\nсостояния, в котором он перестанет быть похожим на любой другой\nблог».\u003c/p\u003e\n\u003cp\u003eЯ взялся применить это к собственному Hugo-блогу. Hugo — статический\nгенератор сайтов: ты пишешь посты в Markdown, конфиг и HTML-шаблоны,\nон собирает готовые HTML-файлы. Дизайн жил в одном CSS-файле плюс\nтема \u003ccode\u003etypo\u003c/code\u003e как git submodule. К апрелю накопилось ощущение\n«работает, но как-то не так». Тёмная тема жила в одной палитре,\nсветлая — местами в другой. Страница «Обо мне» выглядела как\nкусочек чужого SaaS-сайта, попавший в блог по ошибке.\u003c/p\u003e\n\u003cp\u003eРешил пройти все нужные команды и посмотреть, что инструмент реально\nловит. Получилось пять заходов за вечер.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"фундамент-productmd-и-designmd\"\u003e\n  Фундамент: PRODUCT.md и DESIGN.md\n  \u003ca class=\"anchor\" href=\"#%d1%84%d1%83%d0%bd%d0%b4%d0%b0%d0%bc%d0%b5%d0%bd%d1%82-productmd-%d0%b8-designmd\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПервая команда — \u003ccode\u003e/teach\u003c/code\u003e. Это не «настройки», а короткое интервью:\nкому пишешь, каким голосом, чем не должен быть сайт, какие\nтребования к доступности. На выходе два файла в корне проекта:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003ePRODUCT.md\u003c/strong\u003e — стратегический документ. Регистр (бренд или\nпродукт), целевая аудитория, голос, анти-референсы, дизайн-принципы,\nпланка доступности.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDESIGN.md\u003c/strong\u003e — визуальная спецификация. Цвета, шрифты, шкала\nпространства, шкала радиусов, компоненты.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЭти два файла потом читает каждая последующая команда. Без них\nлюбой \u003ccode\u003e/audit\u003c/code\u003e или \u003ccode\u003e/polish\u003c/code\u003e даёт ответы вообще, не привязанные к\nконкретному проекту.\u003c/p\u003e\n\u003cp\u003eГлавное открытие здесь — раздел \u003cstrong\u003eанти-референсов\u003c/strong\u003e. PRODUCT.md\nзаставляет ответить, чем сайт \u003cstrong\u003eне\u003c/strong\u003e должен быть. Я выписал четыре\nконкретных образца, которые не хочу повторять:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eHabr и VC.ru с их боковыми панелями, бейджами и блоками «читайте\nтакже» на пять экранов;\u003c/li\u003e\n\u003cli\u003eMedium и Substack: безликий шаблон платформы, попап «подпишись», где\nощущение «это любой блог»;\u003c/li\u003e\n\u003cli\u003eсовременный SaaS с градиентами и стеклянными карточками;\u003c/li\u003e\n\u003cli\u003eDev.to и обычные GitHub Pages: стандартный markdown без типографики,\nсиние ссылки, sans-serif, ноль характера.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eАнти-референсы оказались полезнее обычных «делай как Stripe». «Как\nStripe» — растяжимое: можно повторить любой кусок и сказать «вот\nвидишь, похоже». «Не как Habr» — конкретный отказ. Каждое следующее\nдизайн-решение проверялось по этому списку и часто не проходило: «а\nне повторяю ли я сейчас Habr-ленту одинаковых превью?»\u003c/p\u003e\n\u003cp\u003eДальше команда \u003ccode\u003e/document\u003c/code\u003e отсканировала существующий CSS и собрала\nDESIGN.md. Здесь я первый раз ошибся — но об этом дальше.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"аудит-11-из-20-на-старте-\"\u003e\n  Аудит: 11 из 20 на старте 📊\n  \u003ca class=\"anchor\" href=\"#%d0%b0%d1%83%d0%b4%d0%b8%d1%82-11-%d0%b8%d0%b7-20-%d0%bd%d0%b0-%d1%81%d1%82%d0%b0%d1%80%d1%82%d0%b5-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e/audit\u003c/code\u003e — техническая проверка по пяти направлениям, каждое от 0\nдо 4 баллов: доступность, производительность, согласованность\nдизайн-токенов, адаптивность, отсутствие шаблонных AI-ляпов.\u003c/p\u003e\n\u003cp\u003eФинальная оценка моего блога — \u003cstrong\u003e11 из 20\u003c/strong\u003e. Между «нормально, но\nработы много» и «плохо». От «плохо» вытащило только производительность\n(Hugo и так быстрый, картинки грузятся лениво, шрифты с правильной\nстратегией загрузки).\u003c/p\u003e\n\u003cp\u003eЧто нашлось:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eКонтраст вторичного текста проваливал стандарт WCAG AA в обеих\nтемах.\u003c/strong\u003e Цвет \u003ccode\u003e#6272a4\u003c/code\u003e на светлом фоне \u003ccode\u003e#f8f8f2\u003c/code\u003e давал соотношение\nконтраста 3.92:1, на тёмном \u003ccode\u003e#282a36\u003c/code\u003e — 3.33:1. Стандарт WCAG AA\nтребует минимум 4.5:1 для основного текста. Эта переменная использовалась\nодновременно как цвет вторичного текста, как цвет ссылок в светлой\nтеме и как мета-информация. Поражено больше 40% не-основного контента\nсайта.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСтраница поиска в светлой теме выдавала белый текст на белом фоне.\u003c/strong\u003e\nВ моём CSS заголовки результатов поиска имели цвет \u003ccode\u003e#f8f8f2\u003c/code\u003e\n(захардкожен под тёмную тему). На светлой теме фон тоже \u003ccode\u003e#f8f8f2\u003c/code\u003e —\nбелый на белом. Я этого не замечал, потому что почти всегда сижу в\nтёмной теме. Половина посетителей видела пустые результаты поиска\nполтора года.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСтраница «Обо мне» — типичный SaaS-клон.\u003c/strong\u003e Шрифт Inter (один из\nсамых заезженных AI-выборов), фон с линейным градиентом, тяжёлая\nтень под аватаром. Все четыре анти-референса из PRODUCT.md в одном\nэкране.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eКликабельные элементы меньше 44 на 44 пикселей.\u003c/strong\u003e Кнопка\nпереключения темы — 32–35 пикселей. У ссылок в навигации не было\nвнутренних отступов, кликабельная зона = площадь самого текста.\nСтандарт WCAG 2.1 рекомендует минимум 44×44 для тача и моторной\nдоступности.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eУ цитат \u003ccode\u003eborder-left\u003c/code\u003e в 2 пикселя.\u003c/strong\u003e Эта самая полоска слева у\n«цитаты». В философии impeccable любая боковая полоска шире 1\nпикселя — отдельный анти-паттерн (типичный «callout-style» из\nстарых книг по веб-дизайну).\u003c/p\u003e\n\u003cp\u003eСемь крупных проблем плюс несколько помельче. Чинилось всё в одном\nпроходе: подняли цвет вторичного текста до читаемого, переписали\nстраницу «Обо мне» с нуля без шрифта Inter и градиентов, сделали\nвсе статусы reading-секции чувствительными к теме, расширили\nкликабельные зоны. После — оценка поднялась до 18 из 20.\u003c/p\u003e\n\u003cp\u003eБез \u003ccode\u003e/audit\u003c/code\u003e я бы половину этих проблем не нашёл. Особенно поиск в\nсветлой теме: я не садился в светлую тему вживую неделями.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"ux-ревью-и-кейс-с-фиктивным-компонентом\"\u003e\n  UX-ревью и кейс с фиктивным компонентом\n  \u003ca class=\"anchor\" href=\"#ux-%d1%80%d0%b5%d0%b2%d1%8c%d1%8e-%d0%b8-%d0%ba%d0%b5%d0%b9%d1%81-%d1%81-%d1%84%d0%b8%d0%ba%d1%82%d0%b8%d0%b2%d0%bd%d1%8b%d0%bc-%d0%ba%d0%be%d0%bc%d0%bf%d0%be%d0%bd%d0%b5%d0%bd%d1%82%d0%be%d0%bc\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eДальше — \u003ccode\u003e/critique\u003c/code\u003e. Это уже не технический аудит, а ревью на\nуровне дизайн-решений: правильно ли устроена главная как точка входа,\nне теряется ли пост в ленте, нужен ли архив-по-годам, есть ли узкие\nместа в навигации.\u003c/p\u003e\n\u003cp\u003eКоманда устроена так, что запускает \u003cstrong\u003eдва независимых ревью\nодновременно\u003c/strong\u003e. Один — отдельный AI-агент с фокусом на\nпользовательский опыт; читает исходники и рендеренный HTML.\nВторой — программный детектор \u003ccode\u003enpx impeccable --json\u003c/code\u003e, проверяющий\n25 конкретных шаблонных AI-ляпов автоматически. И ключевое: они\n\u003cstrong\u003eне видят выводы друг друга\u003c/strong\u003e. Это сделано специально — чтобы один\nне подтверждал догадки другого.\u003c/p\u003e\n\u003cp\u003eЯ к этому отнёсся скептически. Думал, что изоляция «для красоты».\u003c/p\u003e\n\u003cp\u003eОказался неправ.\u003c/p\u003e\n\u003cp\u003eОтдельный AI-агент за пять минут нашёл вот что: в моей же DESIGN.md,\nкоторую я сам написал часом ранее, описан \u003cstrong\u003eбоковой sticky-блок\n«Содержание» как одна из ключевых деталей сайта\u003c/strong\u003e. Подробно: фиксированное\nпозиционирование, ширина 260 пикселей, рамка, скругление, размеры\nссылок, активное состояние. И сразу следом: «один из главных\nкомпонентов сайта; обслуживает чтение длинных постов вторым по\nважности после самого текста».\u003c/p\u003e\n\u003cp\u003eА в \u003ccode\u003elayouts/_default/single.html\u003c/code\u003e (мой собственный шаблон страницы\nпоста) этого блока \u003cstrong\u003eнет\u003c/strong\u003e. Поиск по проекту: ноль вхождений\n\u003ccode\u003etoc-sidebar\u003c/code\u003e, \u003ccode\u003etoc-sticky\u003c/code\u003e, \u003ccode\u003eTableOfContents\u003c/code\u003e. Только 190 строк\nCSS-стилей, который никем не используется, потому что HTML-разметку\nникто не выводит. Документация описывала то, чего на сайте не было.\u003c/p\u003e\n\u003cp\u003eЧто произошло: при первом проходе \u003ccode\u003e/document\u003c/code\u003e я смотрел в CSS,\nувидел проработанный sticky-блок и описал его в DESIGN.md как\nрабочий компонент. Не проверил, что в шаблонах кто-то этот класс\nвыводит. CSS живёт, разметки нет — а я этого не заметил, потому что\nнеудобно увидеть, что я только что подробно описал то, чего не\nсуществует.\u003c/p\u003e\n\u003cp\u003eЭто та самая «слепая зона», когда видишь в собственной работе только\nто, что её подтверждает. Я писал код, я писал документацию по этому\nкоду. Свою же работу мне «удобно» считать согласованной.\u003c/p\u003e\n\u003cp\u003eОтдельный AI-агент в \u003ccode\u003e/critique\u003c/code\u003e не видел ни моих рассуждений по\nкоманде \u003ccode\u003e/document\u003c/code\u003e, ни моей DESIGN.md как авторитетного источника.\nОн читал её как один из файлов проекта. И тут же подсветил\nрасхождение: «sticky TOC задокументирован как ключевой элемент,\nне рендерится ни на одной странице».\u003c/p\u003e\n\u003cp\u003eЭтот один пункт окупил весь подход с двумя независимыми ревью.\nЯ зашил это в персональную память Claude Code как правило: \u003cstrong\u003eпрежде\nчем называть компонент ключевым в DESIGN.md, надо проверить, что он\nдействительно рендерится в шаблонах, а не только в стилях\u003c/strong\u003e. Стилевой\nблок без HTML-разметки — мёртвый код, не «компонент».\u003c/p\u003e\n\u003cp\u003eПосле этого \u003ccode\u003e/critique\u003c/code\u003e нашёл ещё несколько пунктов помельче:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eлистинг прочитанных книг сделан карточной сеткой — это прямой\nанти-референс из моей же PRODUCT.md;\u003c/li\u003e\n\u003cli\u003eтри параллельных шаблона шапки и подвала на разных страницах с\nчуть разным поведением;\u003c/li\u003e\n\u003cli\u003eтекст страницы «Обо мне» написан в типичном LinkedIn-стиле:\n«Я исследую пересечения технологий, мышления и практики», «Автор ·\nИнженер · Исследователь» — строго против моего же голоса;\u003c/li\u003e\n\u003cli\u003eв публичной копии сайта засветилось имя поискового движка\n(«Pagefind»), хотя пользователю это знать не нужно.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eВсё, кроме текста «Обо мне», починили в этом же сеансе. Текст —\nэто переписывание личного голоса, я сделал черновик, но коммитить не\nстал.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"полировка-деталей-\"\u003e\n  Полировка деталей 🎨\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%be%d0%bb%d0%b8%d1%80%d0%be%d0%b2%d0%ba%d0%b0-%d0%b4%d0%b5%d1%82%d0%b0%d0%bb%d0%b5%d0%b9-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПосле \u003ccode\u003e/critique\u003c/code\u003e оставшиеся три команды — это работа другого\nхарактера. Не «найди и почини», а «доведи до состояния, в котором\nдетали перестают раздражать».\u003c/p\u003e\n\u003cp\u003eКоманда \u003ccode\u003e/harden\u003c/code\u003e («закалка») закрыла прямой пробел: на месте\nанглийской заглушки темы «typo? Click here» теперь нормальная\nстраница 404 на русском с тремя осмысленными путями. Локализовал\nраздел тегов: хлебные крошки теперь говорят «Главная / Теги», а не\n«Tags». Расширил поддержку настройки «уменьшить движение» в\nоперационной системе — теперь обнуляются все плавные переходы, не\nтолько прокрутка. Защитился от очень длинных заголовков (если в\nназвание поста попадёт длинная ссылка без пробелов, сетка не\nпоедет).\u003c/p\u003e\n\u003cp\u003eКоманда \u003ccode\u003e/polish\u003c/code\u003e («полировка») добавила состояния «при наведении» и\n«при фокусе с клавиатуры» там, где их не было: теги статьи, ссылки\nна соцсети в шапке главной, навигация пагинации, ссылки между\nпостами, кнопка копирования кода. Каждая ссылка теперь визуально\nоткликается на наведение через смену цвета — без сдвига, без тени,\nпо принципу «никакой фальшивой глубины» из моей же DESIGN.md.\u003c/p\u003e\n\u003cp\u003eКоманда \u003ccode\u003e/typeset\u003c/code\u003e (типографика) — самая «невидимая» работа. Включил\nкернинг (профессиональная регулировка расстояний между парами букв)\nи лигатуры (где две буквы соединяются в один знак — \u003ccode\u003efi\u003c/code\u003e, \u003ccode\u003efl\u003c/code\u003e).\nВключил табличные цифры на датах в ленте: цифры теперь одной ширины\nи в столбце выстраиваются штамп-в-штамп. Добавил мягкие переносы\nрусских слов внутри статьи (без них длинные слова рвали строку\nабзаца на узкой колонке). Тонко уплотнил трекинг (межбуквенный\nинтервал) на крупных заголовках 28-44 пикселя — Literata в больших\nразмерах без лёгкого негативного трекинга «дребезжит» как\nкубики, с ним читается как один жест.\u003c/p\u003e\n\u003cp\u003eКаждая из тройки — 30-60 минут работы. Вместе они подняли качество\nс «работает» до «приятно читать».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-я-понял-про-инструмент\"\u003e\n  Что я понял про инструмент\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%8f-%d0%bf%d0%be%d0%bd%d1%8f%d0%bb-%d0%bf%d1%80%d0%be-%d0%b8%d0%bd%d1%81%d1%82%d1%80%d1%83%d0%bc%d0%b5%d0%bd%d1%82\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eИмпеккейбл работает с локальными файлами, не с задеплоенным сайтом.\u003c/strong\u003e\nКоманды читают исходники в репозитории, рендеренный HTML после\nлокальной сборки Hugo, и оба дизайн-документа. Прода нет ни в одной\nкоманде, кроме \u003ccode\u003e/live\u003c/code\u003e (интерактивный режим в браузере), которую я\nне использовал. То есть весь дизайн-цикл идёт у меня на ноутбуке, и\nвыкладка в боевой репозиторий — финальный шаг, не источник истины.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAI хорошо ловит противоречия в собственной работе, если ему дать\nнезависимое ревью.\u003c/strong\u003e Изоляция в \u003ccode\u003e/critique\u003c/code\u003e нужна не для эстетики\nпроцесса. Самопроверка у языковой модели страдает тем же эффектом\nподтверждения собственной правоты, что и у человека: ты видишь в\nсвоей работе то, что её обосновывает, и не видишь того, что её\nопровергает. Отдельный AI-агент в новой сессии, без моих рассуждений\nв его контексте, читал DESIGN.md как просто один из файлов и тут\nже поймал расхождение с шаблонами.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eАнти-референсы выстреливают сильнее, чем референсы.\u003c/strong\u003e «Не как\nHabr» — конкретный отказ, проверяемый автоматически. «Как Stripe» —\nрастяжимое, можно подогнать любой результат. PRODUCT.md заставляет\nвыписать четыре-пять анти-референсов, и каждое решение проверяется\nпротив них.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eМетрики важнее ощущений.\u003c/strong\u003e \u003ccode\u003e/audit\u003c/code\u003e дал 11 из 20. Это конкретное\nчисло. После правок — 19. Я знаю, на сколько лучше. Бессчётный\n«прогресс» — самообман: правишь, правишь, чувствуешь, что лучше. С\nоценкой видно, что иногда нет.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eУбывающая отдача наступает быстро.\u003c/strong\u003e После пяти проходов половина\nкоманд impeccable стали неуместными для моего блога. \u003ccode\u003e/animate\u003c/code\u003e,\n\u003ccode\u003e/delight\u003c/code\u003e, \u003ccode\u003e/bolder\u003c/code\u003e, \u003ccode\u003e/overdrive\u003c/code\u003e — прямо конфликтуют с принципом\n«без подкупа» из PRODUCT.md. \u003ccode\u003e/quieter\u003c/code\u003e, \u003ccode\u003e/distill\u003c/code\u003e — сайт уже\nплоский и сдержанный, дальше пустая страница. \u003ccode\u003e/colorize\u003c/code\u003e — палитра\nзакрыта на 13 цветах. \u003ccode\u003e/optimize\u003c/code\u003e — Hugo уже выжат до предела.\u003c/p\u003e\n\u003cp\u003eЭто знак, что инструмент сделал работу. Дальше — пиши посты, не\nправь дизайн.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"где-ai-ошибся-а-где-я\"\u003e\n  Где AI ошибся, а где я\n  \u003ca class=\"anchor\" href=\"#%d0%b3%d0%b4%d0%b5-ai-%d0%be%d1%88%d0%b8%d0%b1%d1%81%d1%8f-%d0%b0-%d0%b3%d0%b4%d0%b5-%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЗа эту сессию я дважды слишком уверенно объявил причину явления на\nосновании одного наблюдения. Это вообще частая ошибка и в инженерии,\nи в науке: одна точка наблюдений — не закономерность, это просто\nточка.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eПервый случай.\u003c/strong\u003e Один сборочный билд занял 9 минут вместо обычных\n40-50 секунд. Я уверенно сказал: причина — что я только что добавил в\nподвал отметку реального времени сборки. Каждая страница теперь\nсодержит уникальный текст в подвале, значит, утилита синхронизации\n(rsync) с проверкой по контрольной сумме перезаливает все 227\nстраниц целиком.\nТехнически красивое объяснение, инженерно стройное. И \u003cstrong\u003eневерное\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eСледующий билд с той же отметкой времени занял 40 секунд. То есть\n9-минутный билд был случайным выбросом — холодный старт исполнителя,\nсетевой сбой, что-то сторонне. Я экстраполировал из одной точки и\nготов был «починить» проблему, которой нет.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eВторой случай.\u003c/strong\u003e Я случайно удалил пустой файл \u003ccode\u003econtent/posts/.md\u003c/code\u003e\n(артефакт от прошлой попытки автоматического создания поста с битым\nименем) после того, как назвал его «пустой стуб». Не прочитал\nсодержимое перед удалением. Файл был не пустой — 101 байт. После\nудаления выяснилось, что в нём всё-таки лежал какой-то фронтматтер\n(служебный заголовок поста с метаданными), которого я не увидел до\nдействия.\u003c/p\u003e\n\u003cp\u003eОба случая зашиты в персональную память:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eПеред удалением читать каждый файл.\u003c/strong\u003e Не отслеживается ли git —\nне значит «можно выкинуть».\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eПрежде чем уверенно объявлять причину явления — собрать минимум\nдве сравнимых точки.\u003c/strong\u003e Один выброс ≠ закономерность.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eОсобенно полезно для вопросов производительности, где «фикс\nнесуществующей проблемы» — это напрасные телодвижения и потеря\nдоверия к собственным выводам.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"когда-импеккейбл-не-нужен\"\u003e\n  Когда импеккейбл не нужен\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d0%b8%d0%bc%d0%bf%d0%b5%d0%ba%d0%ba%d0%b5%d0%b9%d0%b1%d0%bb-%d0%bd%d0%b5-%d0%bd%d1%83%d0%b6%d0%b5%d0%bd\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eНа бренде, который требует «как у всех»: внутренний инструмент,\nслужебная панель, прототип без идентичности.\u003c/li\u003e\n\u003cli\u003eКогда нет 30-60 минут на интервью \u003ccode\u003e/teach\u003c/code\u003e и сборку DESIGN.md.\nБез фундамента остальные команды дают ответы вообще, не для тебя.\u003c/li\u003e\n\u003cli\u003eНа короткой одностраничной странице — обычная ручная работа\nбыстрее.\u003c/li\u003e\n\u003cli\u003eКогда дизайн уже отлажен и работает — повторное прохождение часто\nстоит дороже того, что находит.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 class=\"heading\" id=\"конкретные-числа\"\u003e\n  Конкретные числа\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d0%be%d0%bd%d0%ba%d1%80%d0%b5%d1%82%d0%bd%d1%8b%d0%b5-%d1%87%d0%b8%d1%81%d0%bb%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eМетрика\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eДо\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eПосле\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eТехнический аудит (баллы)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e11/20\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e19/20\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eUX-ревью (баллы по 10 эвристикам Нильсена)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e24/40\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e~32/40\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eЦвет фейлит стандарт WCAG AA\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e7 случаев\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e0\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eПараллельных дизайн-систем\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e2 (основная + SaaS на «Обо мне»)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eЗахардкоженных шестнадцатеричных цветов вне токенов\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e23+\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e0\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eКликабельные зоны меньше 44×44\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e5\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e0\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eШрифт-ошибка (отсутствующий шрифт первым в стеке)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eда\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eнет\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eБоковой блок «Содержание» — задокументирован vs реально рендерится\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e0 страниц\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eвсе длинные посты\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eКастомная страница 404\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eнет\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eесть, по-русски\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eПоддержка настройки «уменьшить движение»\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eчастичная\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eполная\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 class=\"heading\" id=\"итог\"\u003e\n  Итог\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%82%d0%be%d0%b3\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eИмпеккейбл не делает дизайн за тебя. Он даёт инструмент, через\nкоторый вы вместе с AI доводите дизайн до состояния, в котором вы\nоба перестаёте делать стандартные ошибки. Аудиторская часть\n(\u003ccode\u003e/audit\u003c/code\u003e, \u003ccode\u003e/critique\u003c/code\u003e) ловит сразу видимое. Доводочная часть\n(\u003ccode\u003e/harden\u003c/code\u003e, \u003ccode\u003e/polish\u003c/code\u003e, \u003ccode\u003e/typeset\u003c/code\u003e) делает то, что в одиночку лень\nдовести до конца.\u003c/p\u003e\n\u003cp\u003eСтоимость: один вечер плюс умение зафиксировать собственный голос\nв PRODUCT.md и DESIGN.md. Срок отдачи: пока продолжаешь редактировать\nсайт. Каждое следующее обращение к AI («поправь шапку», «добавь\nстраницу архива», «перепиши блок») идёт на фоне этих двух файлов, и\nшансы получить очередной AI-look уменьшаются.\u003c/p\u003e\n\u003cp\u003eСкрытая ценность инструмента в том, что он заставляет писать честно\nо собственном проекте. Анти-референсы зашиты в PRODUCT.md и\nзачитываются перед каждой правкой. Это ровно та дисциплина, которую\nя никогда не наводил руками.\u003c/p\u003e\n","date_modified":"2026-04-28T21:30:00+03:00","date_published":"2026-04-28T21:30:00+03:00","id":"https://kushnaren.co/posts/2026-04-28-pyat-prokhodov-impeccable-na-bloge/","image":"https://kushnaren.co/og/2026-04-28-pyat-prokhodov-impeccable-na-bloge.png","summary":"Личный отчёт о работе с AI-инструментом impeccable на Hugo-блоге: как он устроен, что меняет в процессе, кейс с фиктивным компонентом и набор уроков, которые стоят дороже самих правок.","tags":["ai","claude-code","impeccable","hugo","design"],"title":"Пять проходов impeccable по блогу: что AI-инструмент дизайна реально умеет 🛠","url":"https://kushnaren.co/posts/2026-04-28-pyat-prokhodov-impeccable-na-bloge/"},{"content_html":"\u003ch2 class=\"heading\" id=\"зачем-держать-llm-локально-\"\u003e\n  Зачем держать LLM локально 🎯\n  \u003ca class=\"anchor\" href=\"#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%b4%d0%b5%d1%80%d0%b6%d0%b0%d1%82%d1%8c-llm-%d0%bb%d0%be%d0%ba%d0%b0%d0%bb%d1%8c%d0%bd%d0%be-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eРабочий день разработчика всё больше обвязан вокруг подсказок от LLM: автодополнение, рефакторинг, объяснение чужого кода, генерация тестов. Облачные модели делают это качественно, но за это приходится платить тремя вещами: подпиской, скоростью соединения и доверием к провайдеру в части того, какой код вы туда отправляете.\u003c/p\u003e\n\u003cp\u003eЛокальная модель снимает все три. Подписка обнуляется, latency определяется только железом, а код никуда не уходит за пределы ноутбука. Цена вопроса — модель меньше топовых облачных, чуть больше суеты с настройкой и постоянное место на диске.\u003c/p\u003e\n\u003cp\u003eВ этом посте разбираемся с \u003cstrong\u003eQwen3-Coder-Next-MLX-4bit\u003c/strong\u003e: квантованной версией кодовой модели от Alibaba, оптимизированной под Apple Silicon. Что это такое, как поставить, как подключить к Codex CLI и когда честно лучше остаться в облаке.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-это-такое-и-почему-именно-эта-сборка\"\u003e\n  Что это такое и почему именно эта сборка\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%8d%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-%d0%b8-%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d0%b8%d0%bc%d0%b5%d0%bd%d0%bd%d0%be-%d1%8d%d1%82%d0%b0-%d1%81%d0%b1%d0%be%d1%80%d0%ba%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eQwen3-Coder\u003c/strong\u003e — линейка моделей от Alibaba, специально натренированных на коде. Базовая модель — около 8 млрд параметров. Это не GPT-4-class, но для большинства повседневных задач (понять код, написать функцию, сгенерить тесты, отрефакторить) — более чем достаточно.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eMLX\u003c/strong\u003e — относительно новый ML-фреймворк от Apple. Создан с нуля под Apple Silicon: использует unified memory (CPU и GPU делят одну RAM), zero-copy между ними, lazy evaluation. Для пользователя это значит, что модель работает заметно быстрее, чем тот же файл, запущенный через PyTorch с MPS-бэкендом.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4-bit квантизация\u003c/strong\u003e — каждое значение веса хранится в 4 битах вместо стандартных 16. Это уменьшает память в 4 раза с заметной, но обычно приемлемой потерей качества.\u003c/p\u003e\n\u003cp\u003eВажно не путать форматы: \u003cstrong\u003eMLX\u003c/strong\u003e хранит модели в собственном формате (\u003ccode\u003e.safetensors\u003c/code\u003e обёрнутый в MLX-структуру), а не в \u003ccode\u003e.gguf\u003c/code\u003e (это формат llama.cpp). При выборе модели в LM Studio это нужно учитывать: «MLX»-вариант и «GGUF»-вариант — это разные файлы для разных движков, не одно и то же.\u003c/p\u003e\n\u003cp\u003eДля \u003cstrong\u003eQwen3-Coder-Next-MLX-4bit\u003c/strong\u003e характеристики такие:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eПараметры:\u003c/strong\u003e ~8B (тот же что и у не-квантованной версии).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eФормат:\u003c/strong\u003e MLX (\u003ccode\u003e.safetensors\u003c/code\u003e).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eРазмер на диске:\u003c/strong\u003e ~5.5 GB.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eКонтекст:\u003c/strong\u003e 32 768 токенов.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eМинимум RAM:\u003c/strong\u003e 16 GB (модель + накладные).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eКомфортный минимум:\u003c/strong\u003e 24-32 GB (если ещё нужна работа с IDE и браузером параллельно).\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 class=\"heading\" id=\"установка-через-lm-studio-\"\u003e\n  Установка через LM Studio 📦\n  \u003ca class=\"anchor\" href=\"#%d1%83%d1%81%d1%82%d0%b0%d0%bd%d0%be%d0%b2%d0%ba%d0%b0-%d1%87%d0%b5%d1%80%d0%b5%d0%b7-lm-studio-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://lmstudio.ai/\"\u003eLM Studio\u003c/a\u003e — desktop-приложение для работы с локальными LLM. Поддерживает и MLX, и GGUF, плюс встроенный OpenAI-совместимый сервер на \u003ccode\u003elocalhost\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eШаги:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eПоставить LM Studio с официального сайта.\u003c/li\u003e\n\u003cli\u003eОткрыть \u003cstrong\u003eDiscover\u003c/strong\u003e (вкладка с моделями).\u003c/li\u003e\n\u003cli\u003eВ фильтре указать «MLX» (важно — без этого подсунет GGUF-вариант).\u003c/li\u003e\n\u003cli\u003eНайти \u003ccode\u003eQwen3-Coder-Next\u003c/code\u003e или подобную сборку.\u003c/li\u003e\n\u003cli\u003eСкачать (~5.5 GB).\u003c/li\u003e\n\u003cli\u003eЗагрузить в \u003cstrong\u003eMy Models\u003c/strong\u003e, потом \u003cstrong\u003eLoad\u003c/strong\u003e.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eПосле загрузки в правом sidebar\u0026rsquo;е будут параметры:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eContext length:\u003c/strong\u003e 8 192 для большинства задач хватает; 32 768 если нужен большой prompt с длинным куском кода.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eGPU offload:\u003c/strong\u003e «Max» — отдать всё на Metal-ядра.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eThreads:\u003c/strong\u003e оставить дефолт (LM Studio сам подберёт).\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLM Studio автоматически поднимет OpenAI-compatible endpoint на \u003ccode\u003ehttp://localhost:1234/v1/\u003c/code\u003e. Дальше с моделью работают любые клиенты, понимающие OpenAI API.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"подключение-к-codex-cli-через-mcp-\"\u003e\n  Подключение к Codex CLI через MCP 🔌\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%be%d0%b4%d0%ba%d0%bb%d1%8e%d1%87%d0%b5%d0%bd%d0%b8%d0%b5-%d0%ba-codex-cli-%d1%87%d0%b5%d1%80%d0%b5%d0%b7-mcp-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЕсли вы используете Codex CLI (агентский интерфейс на базе LLM), к нему можно подключить локальную модель через \u003ca href=\"https://modelcontextprotocol.io/\"\u003eMCP-сервер\u003c/a\u003e для LM Studio.\u003c/p\u003e\n\u003cp\u003eВ \u003ccode\u003e~/.codex/config.toml\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003emcpServers\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;lmstudio\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ecommand\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;npx\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;-y\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;@mlaboard/lmstudio-mcp\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eПосле перезапуска Codex увидит локальную модель как один из доступных провайдеров. Smoke-тест: попросить простую задачу типа «напиши функцию Фибоначчи на Python» — должен ответить за 0.5–1 секунду.\u003c/p\u003e\n\u003cp\u003eПри первом запуске MCP-сервер скачивает свой пакет через \u003ccode\u003enpx\u003c/code\u003e. Это разовая операция, дальше работает с уже установленного.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"замеры-на-m2-max-\"\u003e\n  Замеры на M2 Max 📊\n  \u003ca class=\"anchor\" href=\"#%d0%b7%d0%b0%d0%bc%d0%b5%d1%80%d1%8b-%d0%bd%d0%b0-m2-max-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eНа моём железе (M2 Max, 32 GB unified memory) получились такие цифры:\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eМетрика\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eЗначение\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eРазмер модели на диске\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e5.5 GB\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eRAM при загрузке\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e~6 GB\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eFirst-token latency\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e0.3-0.5 с\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eGeneration speed\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e18-22 токена/с\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eВремя на ~500-токенный ответ\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e~25 секунд\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eЭто медленнее облачного GPT-4 (там 50-80 ток/с в среднем), но быстрее, чем кажется по первому впечатлению. Для итеративной работы (написал → попросил → поправил руками → попросил снова) разница в latency почти неощутима, потому что много времени всё равно уходит на чтение и редактирование результата.\u003c/p\u003e\n\u003cp\u003eЕсли поднять контекст до 32k и закинуть в prompt большой кусок кода (типа целого файла на 1000 строк), скорость падает примерно вдвое: 9-12 ток/с при first-token latency около 2 секунд. Ничего страшного, но уже заметно.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"сравнение-с-облаком\"\u003e\n  Сравнение с облаком\n  \u003ca class=\"anchor\" href=\"#%d1%81%d1%80%d0%b0%d0%b2%d0%bd%d0%b5%d0%bd%d0%b8%d0%b5-%d1%81-%d0%be%d0%b1%d0%bb%d0%b0%d0%ba%d0%be%d0%bc\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eПоказатель\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eОблако (GPT-4-class)\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eЛокально (Qwen3-MLX)\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eLatency (first token)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e1-3 с\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e0.3-0.5 с\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eGeneration speed\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e50-80 ток/с\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e18-22 ток/с\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eСтоимость на месяц\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e$20-200 (зависит от плана)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e$0 (после железа)\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eКонфиденциальность\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eПо договору с провайдером\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eПолная, код не уходит\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eOffline\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e✗\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e✓\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eКонтекст\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e128k+\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e32k\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eКачество на сложных задачах\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eЛучше\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eХуже\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 class=\"heading\" id=\"когда-честно-лучше-облако\"\u003e\n  Когда честно лучше облако\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d1%87%d0%b5%d1%81%d1%82%d0%bd%d0%be-%d0%bb%d1%83%d1%87%d1%88%d0%b5-%d0%be%d0%b1%d0%bb%d0%b0%d0%ba%d0%be\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЛокальная модель не серебряная пуля. Есть классы задач, где она объективно проигрывает:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eБольшой контекст.\u003c/strong\u003e 128k+ контекст-окно у топовых облачных моделей позволяет закидывать в prompt целые модули кода. У 32k Qwen банально не хватит места.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСложные многошаговые задачи.\u003c/strong\u003e Спроектировать архитектуру модуля, отрефакторить взаимосвязанные классы, написать миграцию с несколькими шагами — здесь топовые модели заметно качественнее.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eМультимодальность.\u003c/strong\u003e Анализ скриншотов UI, генерация по чертежу, чтение PDF — Qwen3-Coder-Next специализирован под код, картинки не обрабатывает.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eРедкие языки/фреймворки.\u003c/strong\u003e На массовом Python, JS, Go качество приемлемое. На нишевых вещах (Elixir, Crystal, какой-нибудь специфичный DSL) облачная модель тренировалась на большем корпусе и даёт более уверенные ответы.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"когда-локалка-точно-полезна\"\u003e\n  Когда локалка точно полезна\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d0%bb%d0%be%d0%ba%d0%b0%d0%bb%d0%ba%d0%b0-%d1%82%d0%be%d1%87%d0%bd%d0%be-%d0%bf%d0%be%d0%bb%d0%b5%d0%b7%d0%bd%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eИ обратная сторона:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eЧувствительные репозитории.\u003c/strong\u003e Закрытый клиентский код, медицинские/финансовые данные, что-то под NDA — локально единственный вариант, который не нарушает соглашений.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eРабота в дороге без сети.\u003c/strong\u003e Поезд, самолёт, кафе с глюкавым WiFi — локальная модель отвечает одинаково ровно.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eВысокочастотные задачи.\u003c/strong\u003e Если модель используется десятки раз в час (autocomplete, объяснение fragment\u0026rsquo;ов кода в IDE), даже маленькая абонентская плата быстро накапливается. Локалка после железа бесплатна.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eЭксперименты с агентами.\u003c/strong\u003e Запускать тестовый цикл, где LLM крутится в фоне без лимитов, проще на собственном железе. Лимиты по rate limit / стоимости в облаке быстро становятся узким местом.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"размышления-после-месяца-использования\"\u003e\n  Размышления после месяца использования\n  \u003ca class=\"anchor\" href=\"#%d1%80%d0%b0%d0%b7%d0%bc%d1%8b%d1%88%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%bf%d0%be%d1%81%d0%bb%d0%b5-%d0%bc%d0%b5%d1%81%d1%8f%d1%86%d0%b0-%d0%b8%d1%81%d0%bf%d0%be%d0%bb%d1%8c%d0%b7%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eДля меня сложилась гибридная схема: 70% повседневной работы (объяснить функцию, написать regex, сгенерить unit-тест, набросать структуру модуля) — Qwen локально. Остальные 30% (сложная архитектура, разбор большого PR, работа со скриншотом) — облако.\u003c/p\u003e\n\u003cp\u003eЭто не «локальная победила облачную». Это разделение по типу задач: где важна privacy и скорость отклика — локально, где важно качество и контекст — облако.\u003c/p\u003e\n\u003cp\u003eУстановка занимает максимум полчаса (LM Studio + скачать файл + подключить через MCP). Дисковое пространство в 5.5 GB не критично даже на 256 GB Mac. Если вы давно думали попробовать локальные LLM, но откладывали из-за «сложно настроить» — Qwen3-Coder-Next-MLX-4bit как раз тот случай, когда барьер входа честно низкий. ✅\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"источники\"\u003e\n  Источники\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eДокументация MLX: \u003ca href=\"https://github.com/ml-explore/mlx\"\u003egithub.com/ml-explore/mlx\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eLM Studio: \u003ca href=\"https://lmstudio.ai/\"\u003elmstudio.ai\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eQwen models на HuggingFace: \u003ca href=\"https://huggingface.co/Qwen\"\u003ehuggingface.co/Qwen\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eModel Context Protocol: \u003ca href=\"https://modelcontextprotocol.io/\"\u003emodelcontextprotocol.io\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","date_modified":"2026-04-24T12:00:00+03:00","date_published":"2026-04-24T12:00:00+03:00","id":"https://kushnaren.co/posts/2026-04-24-qwen3-coder-next-mlx-4bit/","image":"https://kushnaren.co/og/2026-04-24-qwen3-coder-next-mlx-4bit.png","summary":"Подробный разбор Qwen3-Coder-Next-MLX-4bit для локальной разработки на Mac: формат MLX vs GGUF, требования к памяти, настройка через LM Studio, подключение через MCP к Codex CLI и честное сравнение с облачными моделями.","tags":["qwen","mlx","apple silicon","lmstudio","оптимизация"],"title":"Qwen3-Coder-Next-MLX-4bit: Локальная разработка на Mac 🍏","url":"https://kushnaren.co/posts/2026-04-24-qwen3-coder-next-mlx-4bit/"},{"content_html":"\u003cp\u003eУ меня скопилась большая коллекция технических книг в PDF и DJVU. Хотелось читать их прямо на сайте и делиться с другими. Решил автоматизировать процесс: конвертировать документы в Markdown, обогащать метаданные через поиск и публиковать в новом разделе блога.\u003c/p\u003e\n\u003cp\u003eЗаписываю всё как runbook — чтобы в следующий раз не вспоминать и сразу делать.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"-обзор-что-получилось\"\u003e\n  🗺 Обзор: что получилось\n  \u003ca class=\"anchor\" href=\"#-%d0%be%d0%b1%d0%b7%d0%be%d1%80-%d1%87%d1%82%d0%be-%d0%bf%d0%be%d0%bb%d1%83%d1%87%d0%b8%d0%bb%d0%be%d1%81%d1%8c\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eРаздел \u003ccode\u003e/reading\u003c/code\u003e на сайте с карточками книг: название, автор, статус чтения, рейтинг, теги. При открытии книги — полный текст с оглавлением. Скрипты для пакетного и одиночного импорта.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСтек:\u003c/strong\u003e Hugo, Python, \u003ca href=\"https://github.com/microsoft/markitdown\"\u003eMarkItDown\u003c/a\u003e, Open Library API.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 class=\"heading\" id=\"часть-1-структура-hugo-раздела\"\u003e\n  Часть 1. Структура Hugo-раздела\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b0%d1%81%d1%82%d1%8c-1-%d1%81%d1%82%d1%80%d1%83%d0%ba%d1%82%d1%83%d1%80%d0%b0-hugo-%d1%80%d0%b0%d0%b7%d0%b4%d0%b5%d0%bb%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003ch3 class=\"heading\" id=\"что-создаётся\"\u003e\n  Что создаётся\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d1%91%d1%82%d1%81%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cpre tabindex=\"0\"\u003e\u003ccode\u003econtent/reading/           # контент книг\n  _index.md                # лендинг раздела\n  pro-git.md               # каждая книга — отдельный .md\n\nlayouts/reading/\n  list.html                # карточки книг с сеткой\n  single.html              # страница одной книги\n\narchetypes/reading.md      # шаблон для hugo new\nassets/css/reading.css     # стили карточек\u003c/code\u003e\u003c/pre\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003ch3 class=\"heading\" id=\"front-matter-книги\"\u003e\n  Front matter книги\n  \u003ca class=\"anchor\" href=\"#front-matter-%d0%ba%d0%bd%d0%b8%d0%b3%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nn\"\u003e---\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Pro Git\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eauthor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Скотт Чакон, Бен Штрауб\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"ld\"\u003e2026-04-10T12:00:00\u003c/span\u003e\u003cspan class=\"m\"\u003e+03\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"m\"\u003e00\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003edraft\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003esummary\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Полное руководство по Git от авторов системы.\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003edescription\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Полное руководство по Git от авторов системы.\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003etags\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;git\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;контроль версий\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;разработка\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003etoc\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ereadTime\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003estatus\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;finished\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c\"\u003e# want-to-read | reading | finished\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003erating\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e             \u003c/span\u003e\u003cspan class=\"c\"\u003e# 1-5, 0 = без оценки\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003esource\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Pro Git.pdf\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# исходный файл\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nn\"\u003e---\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eПоля \u003ccode\u003estatus\u003c/code\u003e и \u003ccode\u003erating\u003c/code\u003e управляют отображением карточки. Раздел читает их через \u003ccode\u003e.Params.status\u003c/code\u003e и \u003ccode\u003e.Params.rating\u003c/code\u003e в шаблоне.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"добавить-раздел-в-меню\"\u003e\n  Добавить раздел в меню\n  \u003ca class=\"anchor\" href=\"#%d0%b4%d0%be%d0%b1%d0%b0%d0%b2%d0%b8%d1%82%d1%8c-%d1%80%d0%b0%d0%b7%d0%b4%d0%b5%d0%bb-%d0%b2-%d0%bc%d0%b5%d0%bd%d1%8e\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eВ \u003ccode\u003ehugo.toml\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003emenu\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ename\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Чтение\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eurl\u003c/span\u003e  \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;/reading\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 class=\"heading\" id=\"часть-2-установка-markitdown\"\u003e\n  Часть 2. Установка MarkItDown\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b0%d1%81%d1%82%d1%8c-2-%d1%83%d1%81%d1%82%d0%b0%d0%bd%d0%be%d0%b2%d0%ba%d0%b0-markitdown\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eMarkItDown — инструмент Microsoft для конвертации документов в Markdown. Поддерживает PDF, DOCX, EPUB, PPTX, XLSX, HTML и другие форматы.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"установка-в-изолированный-venv\"\u003e\n  Установка в изолированный venv\n  \u003ca class=\"anchor\" href=\"#%d1%83%d1%81%d1%82%d0%b0%d0%bd%d0%be%d0%b2%d0%ba%d0%b0-%d0%b2-%d0%b8%d0%b7%d0%be%d0%bb%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%bd%d1%8b%d0%b9-venv\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eВажно: не устанавливать глобально, чтобы не конфликтовать с системным Python macOS.\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Создаём venv рядом со скриптами\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 -m venv scripts/.venv\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Устанавливаем с поддержкой PDF\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003escripts/.venv/bin/pip install markitdown unidecode\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003escripts/.venv/bin/pip install \u003cspan class=\"s2\"\u003e\u0026#34;markitdown[pdf]\u0026#34;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eСкрипты автоматически подхватывают этот venv через bootstrap-строки в начале файла:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003esys\u003c/span\u003e\u003cspan class=\"o\"\u003e,\u003c/span\u003e \u003cspan class=\"nn\"\u003eos\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003e_venv\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edirname\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vm\"\u003e__file__\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;.venv\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bin\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;python3\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexists\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_venv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003esys\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexecutable\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"n\"\u003e_venv\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexecv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003e_venv\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003e_venv\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003esys\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eargv\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eТеперь \u003ccode\u003epython3 scripts/import-book.py\u003c/code\u003e работает без ручной активации venv.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"gitignore\"\u003e\n  \u003ccode\u003e.gitignore\u003c/code\u003e\n  \u003ca class=\"anchor\" href=\"#gitignore\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cpre tabindex=\"0\"\u003e\u003ccode\u003eBooks/           # не коммитим большие PDF\nscripts/.venv/   # не коммитим virtualenv\u003c/code\u003e\u003c/pre\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 class=\"heading\" id=\"часть-3-одиночный-импорт-import-bookpy\"\u003e\n  Часть 3. Одиночный импорт: \u003ccode\u003eimport-book.py\u003c/code\u003e\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b0%d1%81%d1%82%d1%8c-3-%d0%be%d0%b4%d0%b8%d0%bd%d0%be%d1%87%d0%bd%d1%8b%d0%b9-%d0%b8%d0%bc%d0%bf%d0%be%d1%80%d1%82-import-bookpy\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eИнтерактивный скрипт для добавления одной книги.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"использование\"\u003e\n  Использование\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%81%d0%bf%d0%be%d0%bb%d1%8c%d0%b7%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 scripts/import-book.py ~/путь/к/книге.pdf\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003ch3 class=\"heading\" id=\"что-происходит\"\u003e\n  Что происходит\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d0%bf%d1%80%d0%be%d0%b8%d1%81%d1%85%d0%be%d0%b4%d0%b8%d1%82\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eКонвертирует файл через MarkItDown → получает Markdown\u003c/li\u003e\n\u003cli\u003eПытается найти в тексте заголовок (\u003ccode\u003e# ...\u003c/code\u003e) и автора (\u003ccode\u003eАвтор: ...\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003eПоказывает найденное и предлагает подтвердить или исправить\u003c/li\u003e\n\u003cli\u003eЗапрашивает статус (читаю / прочитал / в очереди), рейтинг, теги, описание\u003c/li\u003e\n\u003cli\u003eГенерирует slug через транслитерацию (\u003ccode\u003eunidecode\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003eСоздаёт \u003ccode\u003econtent/reading/\u0026lt;slug\u0026gt;.md\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 class=\"heading\" id=\"пример-сессии\"\u003e\n  Пример сессии\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80-%d1%81%d0%b5%d1%81%d1%81%d0%b8%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cpre tabindex=\"0\"\u003e\u003ccode\u003e⏳ Конвертирую Pro Git.pdf... ✅ 947,557 символов\n\n📝 Введи метаданные книги:\n\n  Название [Pro Git]: Pro Git\n  Автор [Scott Chacon]: Скотт Чакон, Бен Штрауб\n  Статус:\n    1) 📖 Читаю\n    2) ✅ Прочитал\n    3) 🕐 В очереди\n  Выбор [3]: 2\n  Рейтинг (0–5) [0]: 5\n  Теги: git, контроль версий, разработка\n  Краткое описание: Официальная книга по Git, доступна бесплатно\n  Черновик? (y/n) [n]: n\n\n✅ Создан файл: content/reading/pro-git.md\u003c/code\u003e\u003c/pre\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 class=\"heading\" id=\"часть-4-пакетный-импорт-batch-import-bookspy\"\u003e\n  Часть 4. Пакетный импорт: \u003ccode\u003ebatch-import-books.py\u003c/code\u003e\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b0%d1%81%d1%82%d1%8c-4-%d0%bf%d0%b0%d0%ba%d0%b5%d1%82%d0%bd%d1%8b%d0%b9-%d0%b8%d0%bc%d0%bf%d0%be%d1%80%d1%82-batch-import-bookspy\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eДля массового добавления папки с книгами.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"использование-1\"\u003e\n  Использование\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%81%d0%bf%d0%be%d0%bb%d1%8c%d0%b7%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5-1\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 scripts/batch-import-books.py Books/\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# или с указанием статуса:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 scripts/batch-import-books.py Books/ --status finished\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# проверить без записи:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 scripts/batch-import-books.py Books/ --dry-run\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003ch3 class=\"heading\" id=\"что-происходит-для-каждого-файла\"\u003e\n  Что происходит для каждого файла\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d0%bf%d1%80%d0%be%d0%b8%d1%81%d1%85%d0%be%d0%b4%d0%b8%d1%82-%d0%b4%d0%bb%d1%8f-%d0%ba%d0%b0%d0%b6%d0%b4%d0%be%d0%b3%d0%be-%d1%84%d0%b0%d0%b9%d0%bb%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eКонвертация через MarkItDown\u003c/li\u003e\n\u003cli\u003eПоиск метаданных в Open Library API (бесплатно, без ключа):\n\u003cdiv class=\"code-block\"\u003e\n  \u003cpre tabindex=\"0\"\u003e\u003ccode\u003eGET https://openlibrary.org/search.json?q=\u0026lt;название\u0026gt;\u0026amp;limit=1\u003c/code\u003e\u003c/pre\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003eИз ответа берёт: автора, год, теги (subjects)\u003c/li\u003e\n\u003cli\u003eСоздаёт \u003ccode\u003econtent/reading/\u0026lt;slug\u0026gt;.md\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 class=\"heading\" id=\"-проблема-неверный-заголовок\"\u003e\n  ⚠️ Проблема: неверный заголовок\n  \u003ca class=\"anchor\" href=\"#-%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-%d0%bd%d0%b5%d0%b2%d0%b5%d1%80%d0%bd%d1%8b%d0%b9-%d0%b7%d0%b0%d0%b3%d0%be%d0%bb%d0%be%d0%b2%d0%be%d0%ba\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eMarkItDown конвертирует книгу как есть. Первый \u003ccode\u003e# заголовок\u003c/code\u003e в PDF — это часто не название книги, а команда или раздел из тела. Например:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eOpenStack-книга → \u003ccode\u003e# yum -y update\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003ePostgreSQL → \u003ccode\u003e# book_ref\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eКомандная строка → \u003ccode\u003e# cd /usr/bin\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eРешение:\u003c/strong\u003e не доверять автовыбору, всегда проверять. При пакетном импорте — запускать \u003ccode\u003efix-reading-metadata.py\u003c/code\u003e с таблицей правильных данных, либо использовать интерактивный \u003ccode\u003eimport-book.py\u003c/code\u003e для каждой книги.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 class=\"heading\" id=\"часть-5-агентский-поиск-метаданных-\"\u003e\n  Часть 5. Агентский поиск метаданных 🤖\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b0%d1%81%d1%82%d1%8c-5-%d0%b0%d0%b3%d0%b5%d0%bd%d1%82%d1%81%d0%ba%d0%b8%d0%b9-%d0%bf%d0%be%d0%b8%d1%81%d0%ba-%d0%bc%d0%b5%d1%82%d0%b0%d0%b4%d0%b0%d0%bd%d0%bd%d1%8b%d1%85-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eАвтоматический поиск через Open Library хорошо работает для известных книг. Для надёжности — дополнительно использовать AI-агента.\u003c/p\u003e\n\u003cp\u003eПромпт для агента (ИИ-ассистент с веб-поиском):\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cpre tabindex=\"0\"\u003e\u003ccode\u003eНайди метаданные для этих книг. Для каждой верни:\n- Точное название\n- Авторы\n- Год первого издания\n- 3-5 тегов на русском\n- 1-2 предложения описания на русском\n\nСписок книг:\n1. \u0026#34;Linux на практике\u0026#34; Бреснахэн/Блум (2017)\n2. \u0026#34;BPF для мониторинга Linux\u0026#34;\n...\u003c/code\u003e\u003c/pre\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eАгент находит корректные данные даже для книг с плохими метаданными в PDF.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 class=\"heading\" id=\"часть-6-постобработка-очистка-html\"\u003e\n  Часть 6. Постобработка: очистка HTML\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b0%d1%81%d1%82%d1%8c-6-%d0%bf%d0%be%d1%81%d1%82%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%ba%d0%b0-%d0%be%d1%87%d0%b8%d1%81%d1%82%d0%ba%d0%b0-html\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eКонвертированные PDF содержат встроенный HTML (теги, JavaScript из интерактивных PDF). Hugo минификатор ломается на них.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСимптом:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cpre tabindex=\"0\"\u003e\u003ccode\u003eERROR: unexpected = in expression on line 4526\nfailed to render pages: failed to process \u0026#34;/reading/book/index.html\u0026#34;\u003c/code\u003e\u003c/pre\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003e\u003cstrong\u003eРешение — очистка перед коммитом:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ere\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003estrip_html\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etext\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ere\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esub\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003er\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;script[^\u0026gt;]*\u0026gt;.*?\u0026lt;/script\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eflags\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003ere\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDOTALL\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ere\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIGNORECASE\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etext\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ere\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esub\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003er\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;style[^\u0026gt;]*\u0026gt;.*?\u0026lt;/style\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e  \u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eflags\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003ere\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eDOTALL\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ere\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eIGNORECASE\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# убираем теги кроме базовых markdown-safe\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esafe\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sa\"\u003er\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;br|hr|em|strong|code|pre|b|i|u|s|sub|sup|table|thead|tbody|tr|td|th|ul|ol|li|p|h[1-6]|blockquote|a|img\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etext\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ere\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esub\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003erf\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u0026lt;(?!(?:\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esafe\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s1\"\u003e)[/ \u0026gt;])[^\u0026gt;]+\u0026gt;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eЗапускать после конвертации и перед \u003ccode\u003ehugo --minify\u003c/code\u003e. Для моих 11 книг убрало от 800 до 370 000 символов лишнего HTML.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 class=\"heading\" id=\"часть-7-особые-случаи\"\u003e\n  Часть 7. Особые случаи\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b0%d1%81%d1%82%d1%8c-7-%d0%be%d1%81%d0%be%d0%b1%d1%8b%d0%b5-%d1%81%d0%bb%d1%83%d1%87%d0%b0%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003ch3 class=\"heading\" id=\"отсканированные-pdf\"\u003e\n  Отсканированные PDF\n  \u003ca class=\"anchor\" href=\"#%d0%be%d1%82%d1%81%d0%ba%d0%b0%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%bd%d1%8b%d0%b5-pdf\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eЕсли PDF создан сканером (картинки страниц), MarkItDown конвертирует в пустую строку — нет извлекаемого текста.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eОпределение:\u003c/strong\u003e \u003ccode\u003elen(text_content) == 0\u003c/code\u003e при ненулевом размере файла.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eВарианты действий:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eOCR через \u003ccode\u003epytesseract\u003c/code\u003e (требует установки Tesseract):\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebrew install tesseract tesseract-lang\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epip install pytesseract pillow pdf2image\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003eСоздать заглушку с описанием вручную и отметить в \u003ccode\u003esummary\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eОставить без текста, только front matter с метаданными\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 class=\"heading\" id=\"djvu\"\u003e\n  DJVU\n  \u003ca class=\"anchor\" href=\"#djvu\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eMarkItDown не поддерживает DJVU. Варианты:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eКонвертировать в PDF: \u003ccode\u003ebrew install djvu2pdf\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eИли создать заглушку, указав в \u003ccode\u003esummary\u003c/code\u003e ссылку на онлайн-версию (если книга открытая)\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 class=\"heading\" id=\"очень-большие-книги\"\u003e\n  Очень большие книги\n  \u003ca class=\"anchor\" href=\"#%d0%be%d1%87%d0%b5%d0%bd%d1%8c-%d0%b1%d0%be%d0%bb%d1%8c%d1%88%d0%b8%d0%b5-%d0%ba%d0%bd%d0%b8%d0%b3%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003ePro Git — 947 000 символов, Командная строка — 943 000. Hugo собирает их без проблем. TOC автоматически генерируется из заголовков. Время сборки растёт, но незначительно.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 class=\"heading\" id=\"часть-8-полный-runbook-шаг-за-шагом\"\u003e\n  Часть 8. Полный runbook: шаг за шагом\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b0%d1%81%d1%82%d1%8c-8-%d0%bf%d0%be%d0%bb%d0%bd%d1%8b%d0%b9-runbook-%d1%88%d0%b0%d0%b3-%d0%b7%d0%b0-%d1%88%d0%b0%d0%b3%d0%be%d0%bc\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003ch3 class=\"heading\" id=\"первый-запуск-один-раз\"\u003e\n  Первый запуск (один раз)\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%b5%d1%80%d0%b2%d1%8b%d0%b9-%d0%b7%d0%b0%d0%bf%d1%83%d1%81%d0%ba-%d0%be%d0%b4%d0%b8%d0%bd-%d1%80%d0%b0%d0%b7\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 1. Создать venv\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 -m venv scripts/.venv\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003escripts/.venv/bin/pip install markitdown unidecode \u003cspan class=\"s2\"\u003e\u0026#34;markitdown[pdf]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 2. Создать структуру раздела (уже в репозитории)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# content/reading/, layouts/reading/, assets/css/reading.css\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 3. Добавить в .gitignore\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Books/\u0026#34;\u003c/span\u003e \u0026gt;\u0026gt; .gitignore\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;scripts/.venv/\u0026#34;\u003c/span\u003e \u0026gt;\u0026gt; .gitignore\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003ch3 class=\"heading\" id=\"добавление-новой-книги-каждый-раз\"\u003e\n  Добавление новой книги (каждый раз)\n  \u003ca class=\"anchor\" href=\"#%d0%b4%d0%be%d0%b1%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%bd%d0%be%d0%b2%d0%be%d0%b9-%d0%ba%d0%bd%d0%b8%d0%b3%d0%b8-%d0%ba%d0%b0%d0%b6%d0%b4%d1%8b%d0%b9-%d1%80%d0%b0%d0%b7\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 1. Положить файл в Books/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecp ~/Downloads/новая-книга.pdf Books/\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 2. Импортировать интерактивно\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 scripts/import-book.py Books/новая-книга.pdf\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# → ввести название, автора, статус, рейтинг, теги\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 3. Проверить результат\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecat content/reading/\u0026lt;slug\u0026gt;.md \u003cspan class=\"p\"\u003e|\u003c/span\u003e head -20\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo server  \u003cspan class=\"c1\"\u003e# открыть /reading локально\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 4. Закоммитить и задеплоить\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit add content/reading/\u0026lt;slug\u0026gt;.md\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit commit -m \u003cspan class=\"s2\"\u003e\u0026#34;Добавил книгу: Название книги\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit push\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003ch3 class=\"heading\" id=\"пакетное-добавление-папки-книг\"\u003e\n  Пакетное добавление папки книг\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%b0%d0%ba%d0%b5%d1%82%d0%bd%d0%be%d0%b5-%d0%b4%d0%be%d0%b1%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5-%d0%bf%d0%b0%d0%bf%d0%ba%d0%b8-%d0%ba%d0%bd%d0%b8%d0%b3\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 1. Пробный прогон\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 scripts/batch-import-books.py Books/ --dry-run\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 2. Реальный импорт\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 scripts/batch-import-books.py Books/ --status finished\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 3. Очистка HTML в конвертированных файлах\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epython3 -c \u003cspan class=\"s2\"\u003e\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003eimport re\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003efrom pathlib import Path\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003edef strip_html(t):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    t = re.sub(r\u0026#39;\u0026lt;script.*?\u0026lt;/script\u0026gt;\u0026#39;, \u0026#39;\u0026#39;, t, flags=re.DOTALL|re.I)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    t = re.sub(r\u0026#39;\u0026lt;style.*?\u0026lt;/style\u0026gt;\u0026#39;, \u0026#39;\u0026#39;, t, flags=re.DOTALL|re.I)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    safe = r\u0026#39;br|hr|em|strong|code|pre|b|i|table|thead|tbody|tr|td|th|ul|ol|li|p|h[1-6]|a|img\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    t = re.sub(rf\u0026#39;\u0026lt;(?!(?:{safe})[/ \u0026gt;])[^\u0026gt;]+\u0026gt;\u0026#39;, \u0026#39;\u0026#39;, t)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    return t\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003efor f in Path(\u0026#39;content/reading\u0026#39;).glob(\u0026#39;*.md\u0026#39;):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    parts = f.read_text().split(\u0026#39;---\u0026#39;, 2)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    if len(parts)==3:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e        clean = strip_html(parts[2])\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e        f.write_text(\u0026#39;---\u0026#39; + parts[1] + \u0026#39;---\u0026#39; + clean)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e        print(f.name)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 4. Проверить сборку\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo --minify\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 5. Вручную исправить метаданные (если нужно)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# открыть файлы с неверными заголовками и поправить title, author\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 6. Закоммитить\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit add content/reading/\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit commit -m \u003cspan class=\"s2\"\u003e\u0026#34;Добавил X книг в раздел Чтение\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit push\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\u003ch2 class=\"heading\" id=\"альтернативы-которые-я-рассматривал\"\u003e\n  Альтернативы, которые я рассматривал\n  \u003ca class=\"anchor\" href=\"#%d0%b0%d0%bb%d1%8c%d1%82%d0%b5%d1%80%d0%bd%d0%b0%d1%82%d0%b8%d0%b2%d1%8b-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b5-%d1%8f-%d1%80%d0%b0%d1%81%d1%81%d0%bc%d0%b0%d1%82%d1%80%d0%b8%d0%b2%d0%b0%d0%bb\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПрежде чем взяться за самописный pipeline, я честно посмотрел несколько готовых инструментов и решил их не брать. На всякий случай оставляю краткий разбор.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCalibre + плагин для веб-публикации.\u003c/strong\u003e Зрелый софт с богатой функциональностью, плагины для экспорта в HTML/EPUB/PDF, поиск по библиотеке. Минус для моего случая: всё работает в десктоп-приложении и тащит за собой Calibre-base. Хотелось максимально статичное публичное представление в той же системе, что и блог (Hugo + Markdown), без второй базы и второй точки отказа.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBookwyrm / OpenReads / другие self-hosted каталоги книг.\u003c/strong\u003e Полноценные сервисы со списками чтения, рейтингами, рецензиями, social-фичами. Перебор для одного человека: лишние сущности, отдельный сервер, бэкапы. Мне нужно было «прочитал → отметил → опубликовал», без social-слоя.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNotion / Obsidian как «база».\u003c/strong\u003e Хорошо для личного учёта, плохо для публичного представления: и эстетика чужая, и контроля над версткой меньше, и привязка к платформе.\u003c/p\u003e\n\u003cp\u003eВ итоге выбор «Hugo + Markdown + Python-скрипт» дал ровно то, что нужно: книга — это просто файл в репозитории, рендерится в общую тему сайта, поиск через Pagefind работает прямо «из коробки», бэкапы — git.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-я-бы-сделал-иначе-при-втором-заходе\"\u003e\n  Что я бы сделал иначе при втором заходе\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%8f-%d0%b1%d1%8b-%d1%81%d0%b4%d0%b5%d0%bb%d0%b0%d0%bb-%d0%b8%d0%bd%d0%b0%d1%87%d0%b5-%d0%bf%d1%80%d0%b8-%d0%b2%d1%82%d0%be%d1%80%d0%be%d0%bc-%d0%b7%d0%b0%d1%85%d0%be%d0%b4%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПосле публикации 13 книг есть несколько мест, где я бы изменил подход:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eСразу класть Books/ в .gitignore.\u003c/strong\u003e Это очевидно в ретроспективе, но я первый коммит сделал с PDF-файлами — пришлось чистить историю. Чем раньше игнорить большие бинарники, тем меньше вероятность случайно их закоммитить.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eДелать страницу-черновик на каждую книгу автоматически, без полного тела.\u003c/strong\u003e Сейчас скрипт сразу импортирует весь текст книги (десятки тысяч слов на страницу). Это даёт большой \u003ccode\u003epublic/\u003c/code\u003e и медленный hugo build. Альтернатива: импортировать только метаданные + \u003ccode\u003esummary\u003c/code\u003e + ссылку на оригинал, а полный текст подгружать ленивым lazy-load\u0026rsquo;ом или хранить как отдельный артефакт.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eРазделить статус-pipeline.\u003c/strong\u003e «Читаю», «прочитал», «в очереди» — три состояния, но переходы между ними сейчас ручные (правка front matter). Можно сделать команду \u003ccode\u003emark-as-finished slug\u003c/code\u003e для одного шага.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eЗаранее решить про OCR.\u003c/strong\u003e Если в коллекции есть отсканированные PDF, лучше один раз настроить Tesseract и пропускать через него все «тихие» файлы, чем потом вручную вспоминать «какие из тех 13 не прошли».\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 class=\"heading\" id=\"итог-и-наблюдения\"\u003e\n  Итог и наблюдения\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%82%d0%be%d0%b3-%d0%b8-%d0%bd%d0%b0%d0%b1%d0%bb%d1%8e%d0%b4%d0%b5%d0%bd%d0%b8%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЗа один прогон опубликовал 13 книг. Что работает хорошо:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eMarkItDown\u003c/strong\u003e отлично справляется с текстовыми PDF: извлекает не только текст, но и структуру (заголовки глав, списки, таблицы)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eOpen Library API\u003c/strong\u003e находит метаданные для известных книг, но для русских переводов иногда возвращает англоязычный оригинал\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eАгентский поиск\u003c/strong\u003e надёжнее — ИИ находит правильные данные даже по транслитерированным именам файлов\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЧто требует внимания:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eПервый заголовок ≠ название книги\u003c/strong\u003e — PDFs часто начинаются с команды или технического термина в теле\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eОтсканированные PDF\u003c/strong\u003e не конвертируются без OCR\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDJVU\u003c/strong\u003e не поддерживается напрямую\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eВстроенный HTML\u003c/strong\u003e в конвертированных файлах ломает Hugo minifier — нужна очистка\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eРаздел «Чтение» живёт по адресу \u003ca href=\"/reading\"\u003ekushnaren.co/reading\u003c/a\u003e. Буду пополнять по мере чтения.\u003c/p\u003e\n","date_modified":"2026-04-10T08:45:00+03:00","date_published":"2026-04-10T08:45:00+03:00","id":"https://kushnaren.co/posts/kak-ya-dobavil-biblioteku-knig-na-sajt/","image":"https://kushnaren.co/og/kak-ya-dobavil-biblioteku-knig-na-sajt.png","summary":"Runbook по добавлению раздела чтения на Hugo-блог: от установки MarkItDown и написания скриптов до обработки крайних случаев (сканы, DJVU) и публикации 13 книг.","tags":["hugo","python","автоматизация","markitdown","runbook"],"title":"Как я добавил библиотеку книг на сайт: runbook от идеи до деплоя 📚","url":"https://kushnaren.co/posts/kak-ya-dobavil-biblioteku-knig-na-sajt/"},{"content_html":"\u003ch2 class=\"heading\" id=\"почему-этот-релиз-стоит-прочитать\"\u003e\n  Почему этот релиз стоит прочитать\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%8d%d1%82%d0%be%d1%82-%d1%80%d0%b5%d0%bb%d0%b8%d0%b7-%d1%81%d1%82%d0%be%d0%b8%d1%82-%d0%bf%d1%80%d0%be%d1%87%d0%b8%d1%82%d0%b0%d1%82%d1%8c\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003e8 апреля 2026 вышел \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.160.1\"\u003eHugo v0.160.1\u003c/a\u003e — патч-релиз, который чинит несколько неприятных panic-сценариев из v0.160.0 (вышедшего четырьмя днями раньше). Вместе они формируют один логический шаг для проектов: 0.160.0 даёт две новые возможности, 0.160.1 закрывает баги, всплывшие сразу после релиза.\u003c/p\u003e\n\u003cp\u003eЕсли коротко: одну фичу (CSS-переменные в \u003ccode\u003ecss.Build\u003c/code\u003e) ждали давно те, кто строит CSS через Hugo Pipes; остальные изменения — улучшения для авторов render hooks и фиксы редких краевых случаев. Никаких breaking changes, никаких депрекейтов.\u003c/p\u003e\n\u003cp\u003eНо фиксы в 0.160.1 — это именно тот случай, когда «пропустил минор и поймал panic в проде». Поэтому пробежать стоит.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"главное-за-один-взгляд\"\u003e\n  Главное за один взгляд\n  \u003ca class=\"anchor\" href=\"#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d0%be%d0%b5-%d0%b7%d0%b0-%d0%be%d0%b4%d0%b8%d0%bd-%d0%b2%d0%b7%d0%b3%d0%bb%d1%8f%d0%b4\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003ev0.160.0 (4 апреля 2026)\u003c/strong\u003e — фичевый минор:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCSS-переменные можно инжектить в стили при сборке через \u003ccode\u003ecss.Build\u003c/code\u003e (например, прокинуть значения из конфига).\u003c/li\u003e\n\u003cli\u003eМетод \u003ccode\u003e.Position\u003c/code\u003e теперь доступен на всех render hooks и стал точнее.\u003c/li\u003e\n\u003cli\u003eНесколько прицельных багфиксов (Position, escape ampersand\u0026rsquo;ов в link URL, stray quotes в partial-декораторе script-контекста).\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003ev0.160.1 (8 апреля 2026)\u003c/strong\u003e — patch:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFix: panic при passthrough-элементах в заголовках.\u003c/li\u003e\n\u003cli\u003eFix: panic при редактировании legacy template names, которые одновременно валидные пути в новой схеме.\u003c/li\u003e\n\u003cli\u003eFix: \u003ccode\u003eRenderShortcodes\u003c/code\u003e утекал context markers при отступах.\u003c/li\u003e\n\u003cli\u003eFix: standalone \u003ccode\u003eRenderShortcodes\u003c/code\u003e теперь снимает nested page context markers.\u003c/li\u003e\n\u003cli\u003eFix: автосоздание root sections в мультиязычных сайтах.\u003c/li\u003e\n\u003cli\u003eЧистка: \u003ccode\u003ecascade._target\u003c/code\u003e → \u003ccode\u003ecascade.target\u003c/code\u003e (в тестах; deprecation осталось в коде).\u003c/li\u003e\n\u003cli\u003eREADME: чинены ссылки.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eДальше — что это значит на практике.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"css-переменные-в-cssbuild-\"\u003e\n  CSS-переменные в css.Build 🎨\n  \u003ca class=\"anchor\" href=\"#css-%d0%bf%d0%b5%d1%80%d0%b5%d0%bc%d0%b5%d0%bd%d0%bd%d1%8b%d0%b5-%d0%b2-cssbuild-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЭто главная фича релиза. Если вы используете Hugo Pipes для сборки CSS (а не просто статически копируете файлы из \u003ccode\u003estatic/\u003c/code\u003e), теперь можно прокидывать значения из Hugo-контекста прямо в CSS на этапе сборки.\u003c/p\u003e\n\u003cp\u003eРаньше для подобного приходилось городить пайплайн через переменные окружения, sed-замены или генерировать \u003ccode\u003e.css\u003c/code\u003e через Hugo template (что ломает синтаксис LSP-плагинов).\u003c/p\u003e\n\u003cp\u003eТеперь это выглядит ближе к декларативно:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$opts\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e:=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003edict\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;vars\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003edict\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;primary\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003esite\u003c/span\u003e\u003cspan class=\"na\"\u003e.Params.brand.primary\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;max-width\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;1200px\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$css\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e:=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eresources\u003c/span\u003e\u003cspan class=\"na\"\u003e.Get\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;css/main.css\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ecss\u003c/span\u003e\u003cspan class=\"na\"\u003e.Build\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$opts\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003elink\u003c/span\u003e \u003cspan class=\"na\"\u003erel\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;stylesheet\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$css\u003c/span\u003e\u003cspan class=\"na\"\u003e.RelPermalink\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eВ CSS можно ссылаться на эти переменные через специальный hook (\u003ccode\u003e@import 'hugo:vars'\u003c/code\u003e или подобный, точный синтаксис — в \u003ca href=\"https://gohugo.io/functions/css/build/\"\u003eдокументации css.Build\u003c/a\u003e).\u003c/p\u003e\n\u003cp\u003eКогда полезно: сайтам с темой, где accent-цвет задаётся в \u003ccode\u003ehugo.toml\u003c/code\u003e и должен применяться к компонентам через CSS переменные; сборкам, где \u003ccode\u003emax-width\u003c/code\u003e или \u003ccode\u003ebreakpoint\u003c/code\u003e-значения управляются из конфига; multi-tenant билдам.\u003c/p\u003e\n\u003cp\u003eНашему блогу это не нужно — у нас ровно один theme и палитра жёстко зафиксирована в \u003ccode\u003e:root\u003c/code\u003e блоке. Но если вы строите тему-конструктор или multi-сайт — стоит изучить.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"точная-позиция-в-render-hooks-\"\u003e\n  Точная позиция в render hooks 📍\n  \u003ca class=\"anchor\" href=\"#%d1%82%d0%be%d1%87%d0%bd%d0%b0%d1%8f-%d0%bf%d0%be%d0%b7%d0%b8%d1%86%d0%b8%d1%8f-%d0%b2-render-hooks-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eВторая фича — расширение \u003ccode\u003e.Position\u003c/code\u003e на все render hooks (image, link, heading, code-block) с одновременным улучшением точности.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e.Position\u003c/code\u003e возвращает позицию исходного Markdown-токена в файле. Для отладки и понимания «откуда пришла эта ссылка» — критично.\u003c/p\u003e\n\u003cp\u003eСценарий использования:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e{{/* render-link.html */}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$url\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e:=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Destination\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esafeURL\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enot\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eurls\u003c/span\u003e\u003cspan class=\"na\"\u003e.Parse\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$url\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"na\"\u003e.IsAbs\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cm\"\u003e{{/* Внутренняя ссылка — проверяем что target существует */}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enot\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"na\"\u003e.Page.GetPage\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003estring\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$url\u003c/span\u003e\u003cspan class=\"o\"\u003e))\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ewarnf\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Битая внутренняя ссылка %q в %s\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$url\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Position\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$url\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Text\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esafeHTML\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eВ warning\u0026rsquo;е теперь будет точное \u003ccode\u003eфайл:строка\u003c/code\u003e исходника поста, а не нечто приблизительное. Проще ловить регрессии после рефакторингов контента.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"багфиксы-01601-\"\u003e\n  Багфиксы 0.160.1 🔧\n  \u003ca class=\"anchor\" href=\"#%d0%b1%d0%b0%d0%b3%d1%84%d0%b8%d0%ba%d1%81%d1%8b-01601-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПять патчей, каждый закрывает свой панический сценарий или утечку.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"panic-при-passthrough-в-заголовках\"\u003e\n  Panic при passthrough в заголовках\n  \u003ca class=\"anchor\" href=\"#panic-%d0%bf%d1%80%d0%b8-passthrough-%d0%b2-%d0%b7%d0%b0%d0%b3%d0%be%d0%bb%d0%be%d0%b2%d0%ba%d0%b0%d1%85\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eHugo Goldmark поддерживает \u003ca href=\"https://gohugo.io/configuration/markup/#passthrough\"\u003epassthrough\u003c/a\u003e — режим, когда определённые символьные блоки (например, LaTeX-формулы \u003ccode\u003e$...$\u003c/code\u003e) пропускаются как есть и обрабатываются на клиенте через MathJax/KaTeX.\u003c/p\u003e\n\u003cp\u003eВ 0.160.0 если passthrough-элемент попадал прямо внутрь заголовка (например, \u003ccode\u003e## Формула $E=mc^2$\u003c/code\u003e), Hugo падал с panic при сборке. 0.160.1 чинит этот случай: теперь рендерится корректный HTML без падения.\u003c/p\u003e\n\u003cp\u003eЕсли вы пишете научный/математический контент с inline-формулами в заголовках — обновление обязательно.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"panic-на-legacy-template-names\"\u003e\n  Panic на legacy template names\n  \u003ca class=\"anchor\" href=\"#panic-%d0%bd%d0%b0-legacy-template-names\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eЕсли в проекте есть старая схема именования шаблонов (\u003ccode\u003e_default/single.html.html\u003c/code\u003e и подобные legacy-варианты), которая одновременно является валидным путём в новой схеме именования Hugo, при редактировании файла Hugo мог падать. 0.160.1 разводит эти кейсы.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"rendershortcodes-и-context-markers\"\u003e\n  RenderShortcodes и context markers\n  \u003ca class=\"anchor\" href=\"#rendershortcodes-%d0%b8-context-markers\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003eRenderShortcodes\u003c/code\u003e — функция для обработки всех шорткодов внутри переданного контента. Она использует внутренние «context markers» для отслеживания вложенности.\u003c/p\u003e\n\u003cp\u003eВ 0.160.0 эти маркеры могли:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eУтекать в финальный HTML, если контент с шорткодами был с отступами (например, внутри списка или blockquote).\u003c/li\u003e\n\u003cli\u003eНе очищаться при standalone-вызове \u003ccode\u003e.RenderShortcodes\u003c/code\u003e через \u003ccode\u003e.Page.Content\u003c/code\u003e после nested-рендеринга.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eОба случая дают видимый «мусор» в выводе (артефакты вида \u003ccode\u003e__hugo_ctx0__\u003c/code\u003e и подобные). 0.160.1 чистит маркеры в обеих ситуациях.\u003c/p\u003e\n\u003cp\u003eЕсли вы используете шорткоды внутри списков или blockquote\u0026rsquo;ов — обновляйтесь.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"auto-creation-root-sections-в-мультиязычных-сайтах\"\u003e\n  Auto-creation root sections в мультиязычных сайтах\n  \u003ca class=\"anchor\" href=\"#auto-creation-root-sections-%d0%b2-%d0%bc%d1%83%d0%bb%d1%8c%d1%82%d0%b8%d1%8f%d0%b7%d1%8b%d1%87%d0%bd%d1%8b%d1%85-%d1%81%d0%b0%d0%b9%d1%82%d0%b0%d1%85\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eВ мультиязычных сайтах Hugo автоматически создаёт «виртуальные» root section страницы для каждого языка. В 0.160.0 эта логика могла давать сбой (root section не создавалась, или создавалась только для default-языка), что ломало навигацию по неосновным языкам.\u003c/p\u003e\n\u003cp\u003eЕсли у вас один язык — пункт мимо. Если несколько — обновление лечит.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"cascadetarget-вместо-cascade_target\"\u003e\n  cascade.target вместо cascade._target\n  \u003ca class=\"anchor\" href=\"#cascadetarget-%d0%b2%d0%bc%d0%b5%d1%81%d1%82%d0%be-cascade_target\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eМелкий, но симптоматичный пункт: ключ \u003ccode\u003e_target\u003c/code\u003e в \u003ca href=\"https://gohugo.io/content-management/front-matter/#cascade\"\u003ecascade-конфигурации\u003c/a\u003e переименован в \u003ccode\u003etarget\u003c/code\u003e (без подчёркивания). Старая форма помечена как deprecated в коде, всё ещё работает.\u003c/p\u003e\n\u003cp\u003eЕсли у вас есть cascade-блоки в front matter:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ecascade\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003e_target\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e                 \u003c/span\u003e\u003cspan class=\"c\"\u003e# ← старая форма\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ekind\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epage\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003efoo\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ebar\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eЗамените на:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ecascade\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e- \u003cspan class=\"nt\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e                  \u003c/span\u003e\u003cspan class=\"c\"\u003e# ← новая форма\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003ekind\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003epage\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003efoo\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ebar\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eТочечная правка, занимает минуту. Запас времени до полного удаления старой формы — обычно несколько релизов.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"чек-лист-обновления-\"\u003e\n  Чек-лист обновления ✅\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b5%d0%ba-%d0%bb%d0%b8%d1%81%d1%82-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eПоднять версию Hugo до 0.160.1\u003c/strong\u003e в Dockerfile / CI-образе или локальном binary. Если вы уже на v0.161.0+ — пропускаем, это пройденная развилка.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eЕсли используете passthrough\u003c/strong\u003e для математики или специальных Markdown-блоков, и они появляются в заголовках — обновление закрывает panic. Без обновления заголовки-формулы могут быть рассадником сюрпризов.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eЕсли используете \u003ccode\u003ecascade._target\u003c/code\u003e\u003c/strong\u003e в front matter, переименовать в \u003ccode\u003ecascade.target\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egrep -rln \u003cspan class=\"s1\"\u003e\u0026#39;_target:\u0026#39;\u003c/span\u003e content/ \u003cspan class=\"p\"\u003e|\u003c/span\u003e xargs sed -i \u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;s/_target:/target:/g\u0026#39;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eЕсли строите CSS через Hugo Pipes\u003c/strong\u003e, посмотреть документацию \u003ccode\u003ecss.Build\u003c/code\u003e с CSS variables — возможно, сможете заменить sed-замены или env-обвязку на встроенный механизм.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eЕсли пишете кастомные render hooks\u003c/strong\u003e, проверить, можно ли использовать \u003ccode\u003e.Position\u003c/code\u003e для лучшей диагностики битых ссылок/изображений в постах.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eСтандартный прогон:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo --minify --gc\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eСравнить количество warning\u0026rsquo;ов до и после. Если новых не появилось — мигрировано чисто.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 class=\"heading\" id=\"итог-\"\u003e\n  Итог 🧠\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%82%d0%be%d0%b3-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eHugo 0.160.x — это не громкий feature drop, а методичное улучшение инфраструктуры: новый удобный механизм для CSS-сборки, более точная диагностика в render hooks, набор фиксов под краевые случаи (passthrough в заголовках, RenderShortcodes с отступами, multilingual sections).\u003c/p\u003e\n\u003cp\u003eГлавная причина обновляться — патч 0.160.1 закрывает несколько panic-сценариев. Если вас они никогда не цепляли, повезло; но «не цепляли» — это не «не зацепят». На статичных сайтах panic при сборке проявляется как сломанный CI и красный билд, что рассинхронизирует прод и репозиторий.\u003c/p\u003e\n\u003cp\u003eЕсли уже сидите на 0.161.0 (как мой блог сейчас), всё это вы уже получили транзитом. Если на 0.159 или раньше — есть смысл прыгать сразу на самую свежую stable, минуя промежуточные.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"источники\"\u003e\n  Источники\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eРелиз 0.160.0: \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.160.0\"\u003egohugoio/hugo v0.160.0\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eРелиз 0.160.1: \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.160.1\"\u003egohugoio/hugo v0.160.1\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eДокументация \u003ccode\u003ecss.Build\u003c/code\u003e: \u003ca href=\"https://gohugo.io/functions/css/build/\"\u003egohugo.io/functions/css/build/\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eДокументация passthrough: \u003ca href=\"https://gohugo.io/configuration/markup/#passthrough\"\u003egohugo.io/configuration/markup/#passthrough\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eДокументация cascade: \u003ca href=\"https://gohugo.io/content-management/front-matter/#cascade\"\u003egohugo.io/content-management/front-matter/#cascade\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","date_modified":"2026-04-08T12:00:00+03:00","date_published":"2026-04-08T12:00:00+03:00","id":"https://kushnaren.co/posts/2026-04-08-new-in-hugo-0-160-1/","image":"https://kushnaren.co/og/2026-04-08-new-in-hugo-0-160-1.png","summary":"Подробный разбор Hugo v0.160.0 (фичи: CSS variable injection в css.Build, accurate Position в render hooks) и патч-релиза v0.160.1 (фиксы panic при passthrough в заголовках, утечки context markers в RenderShortcodes, multilingual root sections). Практические сценарии и чек-лист миграции.","tags":["hugo","релизы","devops"],"title":"Что нового в Hugo v0.160.1: разбор релиза 🚀","url":"https://kushnaren.co/posts/2026-04-08-new-in-hugo-0-160-1/"},{"content_html":"\u003cp\u003eЭтот материал не «теория про ИИ». Это практический разбор конкретной рабочей сессии, где мы последовательно вели блог на Hugo через Codex CLI: писали новые статьи, чинили продовые проблемы, настраивали тему и закрепляли правила процесса.\u003c/p\u003e\n\u003cp\u003eИменно такой формат, на мой взгляд, лучше всего показывает ценность автоматизации: не в красивых обещаниях, а в повторяемом результате. 🔧\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"исходный-контекст\"\u003e\n  Исходный контекст\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%81%d1%85%d0%be%d0%b4%d0%bd%d1%8b%d0%b9-%d0%ba%d0%be%d0%bd%d1%82%d0%b5%d0%ba%d1%81%d1%82\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eНа старте у нас уже был рабочий блог на Hugo с темой \u003ccode\u003etypo\u003c/code\u003e, CI/CD через Forgejo Actions и поиском на \u003ccode\u003ePagefind\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eДальше в рамках одной непрерывной сессии мы сделали:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eНаписали и опубликовали новый пост про Hugo \u003ccode\u003e0.157.0\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eПоймали проблему с невидимостью поста из-за времени публикации.\u003c/li\u003e\n\u003cli\u003eДобавили правила в \u003ccode\u003eAGENTS.md\u003c/code\u003e, чтобы больше не ловить future-ошибки.\u003c/li\u003e\n\u003cli\u003eНастроили и протестировали подсветку кода (в итоге перешли на \u003ccode\u003edracula\u003c/code\u003e).\u003c/li\u003e\n\u003cli\u003eПривели к \u003ccode\u003edracula\u003c/code\u003e цвета выдачи поиска.\u003c/li\u003e\n\u003cli\u003eИсправили некорректные заголовки в \u003ccode\u003ePagefind\u003c/code\u003e (вместо названия поста показывалось имя сайта).\u003c/li\u003e\n\u003cli\u003eСтруктурировали \u003ccode\u003eREADME.md\u003c/code\u003e и \u003ccode\u003eAGENTS.md\u003c/code\u003e как рабочую документацию процесса.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eТо есть это уже не «написать один пост», а полноценный операционный цикл контентного проекта.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"как-выглядел-рабочий-цикл-с-codex-cli\"\u003e\n  Как выглядел рабочий цикл с Codex CLI\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d0%b0%d0%ba-%d0%b2%d1%8b%d0%b3%d0%bb%d1%8f%d0%b4%d0%b5%d0%bb-%d1%80%d0%b0%d0%b1%d0%be%d1%87%d0%b8%d0%b9-%d1%86%d0%b8%d0%ba%d0%bb-%d1%81-codex-cli\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eСамый полезный паттерн из этой сессии:\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"1-чёткая-постановка-задачи\"\u003e\n  1. Чёткая постановка задачи\n  \u003ca class=\"anchor\" href=\"#1-%d1%87%d1%91%d1%82%d0%ba%d0%b0%d1%8f-%d0%bf%d0%be%d1%81%d1%82%d0%b0%d0%bd%d0%be%d0%b2%d0%ba%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eКаждый шаг формулировался как конкретный результат:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e«Напиши новую статью про релиз».\u003c/li\u003e\n\u003cli\u003e«Почему статья не видна? Исправь».\u003c/li\u003e\n\u003cli\u003e«Сделай правило, чтобы проблема не повторялась».\u003c/li\u003e\n\u003cli\u003e«Сделай тему поиска в dracula».\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЧем конкретнее цель, тем меньше «творческого шума» и выше скорость прохождения задачи.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"2-локальная-диагностика-перед-правкой\"\u003e\n  2. Локальная диагностика перед правкой\n  \u003ca class=\"anchor\" href=\"#2-%d0%bb%d0%be%d0%ba%d0%b0%d0%bb%d1%8c%d0%bd%d0%b0%d1%8f-%d0%b4%d0%b8%d0%b0%d0%b3%d0%bd%d0%be%d1%81%d1%82%d0%b8%d0%ba%d0%b0-%d0%bf%d0%b5%d1%80%d0%b5%d0%b4-%d0%bf%d1%80%d0%b0%d0%b2%d0%ba%d0%be%d0%b9\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eПеред изменением файлов Codex проходил короткий аудит:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eчтение текущих шаблонов/конфигов;\u003c/li\u003e\n\u003cli\u003eпроверка фактического рендера;\u003c/li\u003e\n\u003cli\u003eсверка с реальным поведением сайта;\u003c/li\u003e\n\u003cli\u003eпоиск причинно-следственной связи, а не симптомов.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eПример мышления: если цвета не применились, сначала проверить порядок подключения CSS, а не сразу «добавить ещё \u003ccode\u003e!important\u003c/code\u003e».\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"3-точечная-правка--обязательная-валидация\"\u003e\n  3. Точечная правка + обязательная валидация\n  \u003ca class=\"anchor\" href=\"#3-%d1%82%d0%be%d1%87%d0%b5%d1%87%d0%bd%d0%b0%d1%8f-%d0%bf%d1%80%d0%b0%d0%b2%d0%ba%d0%b0--%d0%be%d0%b1%d1%8f%d0%b7%d0%b0%d1%82%d0%b5%d0%bb%d1%8c%d0%bd%d0%b0%d1%8f-%d0%b2%d0%b0%d0%bb%d0%b8%d0%b4%d0%b0%d1%86%d0%b8%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eПосле каждого изменения шёл одинаковый технический минимум:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo list future\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo --minify\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit status --short\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eЭто сильно снижает вероятность того, что в \u003ccode\u003emain\u003c/code\u003e улетит «логически верно, но технически сыро».\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"4-фиксация-в-git-маленькими-коммитами\"\u003e\n  4. Фиксация в git маленькими коммитами\n  \u003ca class=\"anchor\" href=\"#4-%d1%84%d0%b8%d0%ba%d1%81%d0%b0%d1%86%d0%b8%d1%8f-%d0%b2-git-%d0%bc%d0%b0%d0%bb%d0%b5%d0%bd%d1%8c%d0%ba%d0%b8%d0%bc%d0%b8-%d0%ba%d0%be%d0%bc%d0%bc%d0%b8%d1%82%d0%b0%d0%bc%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eВместо одного гигантского коммита по итогам сессии:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eкаждый смысловой шаг фиксировался отдельно;\u003c/li\u003e\n\u003cli\u003eсообщения коммитов были предметными;\u003c/li\u003e\n\u003cli\u003eлюбой этап можно быстро отследить и откатить локально при необходимости.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eДля блога это особенно удобно: контент, стили, шаблоны и правила не смешиваются в «одно большое изменение».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"реальные-проблемы-которые-мы-поймали-и-закрыли\"\u003e\n  Реальные проблемы, которые мы поймали и закрыли\n  \u003ca class=\"anchor\" href=\"#%d1%80%d0%b5%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d1%8b-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b5-%d0%bc%d1%8b-%d0%bf%d0%be%d0%b9%d0%bc%d0%b0%d0%bb%d0%b8-%d0%b8-%d0%b7%d0%b0%d0%ba%d1%80%d1%8b%d0%bb%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003ch3 class=\"heading\" id=\"проблема-1-пост-не-видно-на-сайте\"\u003e\n  Проблема 1: пост «не видно на сайте»\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-1-%d0%bf%d0%be%d1%81%d1%82-%d0%bd%d0%b5-%d0%b2%d0%b8%d0%b4%d0%bd%d0%be-%d0%bd%d0%b0-%d1%81%d0%b0%d0%b9%d1%82%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eСимптом: статья есть в репозитории, но не отображается в списке публикаций.\u003c/p\u003e\n\u003cp\u003eПричина: \u003ccode\u003edate\u003c/code\u003e в фронтматтере оказался в будущем относительно московского времени на момент деплоя.\u003c/p\u003e\n\u003cp\u003eРешение:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eсдвиг даты в прошлое;\u003c/li\u003e\n\u003cli\u003eпроверка \u003ccode\u003ehugo list future\u003c/code\u003e;\u003c/li\u003e\n\u003cli\u003eфиксация правила в \u003ccode\u003eAGENTS.md\u003c/code\u003e: новые посты ставить на 5-10 минут в прошлое и всегда проверять future-список перед коммитом.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eКлючевой эффект: проблема устранена один раз на уровне процесса, а не «подкручена вручную» только для одного поста.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"проблема-2-подсветка-кода-всё-одним-цветом\"\u003e\n  Проблема 2: подсветка кода «всё одним цветом»\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-2-%d0%bf%d0%be%d0%b4%d1%81%d0%b2%d0%b5%d1%82%d0%ba%d0%b0-%d0%ba%d0%be%d0%b4%d0%b0-%d0%b2%d1%81%d1%91-%d0%be%d0%b4%d0%bd%d0%b8%d0%bc-%d1%86%d0%b2%d0%b5%d1%82%d0%be%d0%bc\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eСимптом: HTML уже содержит \u003ccode\u003echroma\u003c/code\u003e-классы, но визуально код почти монохромный.\u003c/p\u003e\n\u003cp\u003eПричина: базовый файл подсветки темы был близок к \u003ccode\u003ebw\u003c/code\u003e-палитре.\u003c/p\u003e\n\u003cp\u003eРешение:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eВключили работу через CSS-классы (\u003ccode\u003enoClasses = false\u003c/code\u003e).\u003c/li\u003e\n\u003cli\u003eДобавили собственный \u003ccode\u003eassets/css/syntax-highlighting.css\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eСначала проверили \u003ccode\u003emonokai\u003c/code\u003e, затем переключили на \u003ccode\u003edracula\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eИтог: читаемая цветная подсветка с контролируемой темой в репозитории.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"проблема-3-выдача-поиска-не-в-стиле-сайта\"\u003e\n  Проблема 3: выдача поиска не в стиле сайта\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-3-%d0%b2%d1%8b%d0%b4%d0%b0%d1%87%d0%b0-%d0%bf%d0%be%d0%b8%d1%81%d0%ba%d0%b0-%d0%bd%d0%b5-%d0%b2-%d1%81%d1%82%d0%b8%d0%bb%d0%b5-%d1%81%d0%b0%d0%b9%d1%82%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eСимптом: поле и результаты поиска выпадали из общей цветовой схемы.\u003c/p\u003e\n\u003cp\u003eПричина: стили \u003ccode\u003ePagefind\u003c/code\u003e подключались после \u003ccode\u003ecustom.css\u003c/code\u003e, и часть переопределений не применялась.\u003c/p\u003e\n\u003cp\u003eРешение:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eсначала добавили CSS-переменные \u003ccode\u003ePagefind\u003c/code\u003e в \u003ccode\u003ecustom.css\u003c/code\u003e;\u003c/li\u003e\n\u003cli\u003eзатем сделали гарантированное переопределение после \u003ccode\u003epagefind-ui.css\u003c/code\u003e прямо в шаблоне \u003ccode\u003elayouts/search/single.html\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЭто важный вывод: иногда нужно исправлять не «цвета», а точку и порядок их применения.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"проблема-4-у-результатов-поиска-заголовок-evgeny-kushnarenko\"\u003e\n  Проблема 4: у результатов поиска заголовок «Evgeny Kushnarenko»\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-4-%d1%83-%d1%80%d0%b5%d0%b7%d1%83%d0%bb%d1%8c%d1%82%d0%b0%d1%82%d0%be%d0%b2-%d0%bf%d0%be%d0%b8%d1%81%d0%ba%d0%b0-%d0%b7%d0%b0%d0%b3%d0%be%d0%bb%d0%be%d0%b2%d0%be%d0%ba-evgeny-kushnarenko\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eСимптом: каждая найденная строка в выдаче имела одинаковый title (имя сайта).\u003c/p\u003e\n\u003cp\u003eПричина: \u003ccode\u003ePagefind\u003c/code\u003e подхватывал \u003ccode\u003eh1\u003c/code\u003e из шапки страницы.\u003c/p\u003e\n\u003cp\u003eРешение:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eвынесли локальный \u003ccode\u003elayouts/partials/header.html\u003c/code\u003e и пометили шапку \u003ccode\u003edata-pagefind-ignore\u003c/code\u003e;\u003c/li\u003e\n\u003cli\u003eдобавили \u003ccode\u003edata-pagefind-meta=\u0026quot;title\u0026quot;\u003c/code\u003e на \u003ccode\u003eh1\u003c/code\u003e поста в \u003ccode\u003elayouts/_default/single.html\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eИтог: выдача начала показывать реальные заголовки материалов.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-это-даёт-в-ежедневной-работе\"\u003e\n  Что это даёт в ежедневной работе\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%8d%d1%82%d0%be-%d0%b4%d0%b0%d1%91%d1%82-%d0%b2-%d0%b5%d0%b6%d0%b5%d0%b4%d0%bd%d0%b5%d0%b2%d0%bd%d0%be%d0%b9-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПосле такого цикла становится заметно:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eВремя между идеей и публикацией сокращается.\u003c/li\u003e\n\u003cli\u003eОшибки переходят из категории «случайные и повторяющиеся» в «редкие и диагностируемые».\u003c/li\u003e\n\u003cli\u003eРепозиторий становится документацией процесса, а не просто папкой с контентом.\u003c/li\u003e\n\u003cli\u003eСнижается когнитивная нагрузка на автора: меньше ручной рутины, больше фокуса на смысле.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eНа длинной дистанции это важнее, чем «сэкономить 5 минут на одной статье».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-точно-не-нужно-автоматизировать\"\u003e\n  Что точно не нужно автоматизировать\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%82%d0%be%d1%87%d0%bd%d0%be-%d0%bd%d0%b5-%d0%bd%d1%83%d0%b6%d0%bd%d0%be-%d0%b0%d0%b2%d1%82%d0%be%d0%bc%d0%b0%d1%82%d0%b8%d0%b7%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d1%82%d1%8c\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПосле пары таких сессий стало ясно: не любую задачу нужно отдавать инструменту. Есть три категории, где автоматизация скорее вредит:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eТональность и личный голос.\u003c/strong\u003e Codex может собрать структурный черновик, но финальный тон — авторская вещь. Если читатель чувствует «это писал не человек», доверие к материалу падает мгновенно. Заменять автора на этом этапе — значит потерять то, ради чего блог вообще существует.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСпорные технические решения.\u003c/strong\u003e Когда в дизайне есть выбор между двумя одинаково обоснованными подходами (положить компонент в \u003ccode\u003elayouts/_default/\u003c/code\u003e или в \u003ccode\u003elayouts/posts/\u003c/code\u003e, использовать ли Hugo Pipes для CSS или статичную копию), Codex даст «средний разумный вариант». Это не плохо, но это не вы. На длинной дистанции архитектурные решения, принятые «средним способом», накапливают трение, которое в одиночку не разглядеть.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eРешения про контент.\u003c/strong\u003e Что писать, что не писать, какие посты убирать, какие оставлять, как переименовать раздел — это редакционные решения. Они должны проходить через автора. Инструмент здесь полезен только как «помощник по реализации», когда решение уже принято.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"размышления-после-сессии\"\u003e\n  Размышления после сессии\n  \u003ca class=\"anchor\" href=\"#%d1%80%d0%b0%d0%b7%d0%bc%d1%8b%d1%88%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%bf%d0%be%d1%81%d0%bb%d0%b5-%d1%81%d0%b5%d1%81%d1%81%d0%b8%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eГлавный вывод после нескольких часов работы в таком режиме: ценность автоматизации не в скорости, а в \u003cstrong\u003eпредсказуемости\u003c/strong\u003e. До регулярной работы с Codex CLI каждая публикация поста была чуть-чуть разной: где-то забыл подвинуть дату, где-то не проверил подсветку, где-то не закоммитил отдельно стилевую правку. После — процесс стал ровным, что освободило внимание для содержания.\u003c/p\u003e\n\u003cp\u003eЭто типичная история про инфраструктуру: когда она работает плохо, она занимает 60% мысленного бюджета на каждое действие. Когда хорошо — становится незаметной, и весь бюджет уходит на смысл.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"практический-шаблон-автоматизации-под-hugo\"\u003e\n  Практический шаблон автоматизации под Hugo\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%b0%d0%ba%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b9-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd-%d0%b0%d0%b2%d1%82%d0%be%d0%bc%d0%b0%d1%82%d0%b8%d0%b7%d0%b0%d1%86%d0%b8%d0%b8-%d0%bf%d0%be%d0%b4-hugo\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЕсли хотите повторить подход, можно взять такой минимум:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eДля каждой задачи: цель в 1-2 предложениях.\u003c/li\u003e\n\u003cli\u003eПеред правкой: чтение текущего состояния (\u003ccode\u003erg\u003c/code\u003e, \u003ccode\u003esed\u003c/code\u003e, шаблоны, конфиги).\u003c/li\u003e\n\u003cli\u003eПосле правки: \u003ccode\u003ehugo list future\u003c/code\u003e + \u003ccode\u003ehugo --minify\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eФиксация: один смысл = один коммит.\u003c/li\u003e\n\u003cli\u003eПубликация: \u003ccode\u003egit push\u003c/code\u003e сразу после проверки.\u003c/li\u003e\n\u003cli\u003eЕсли баг повторяемый: правило в \u003ccode\u003eAGENTS.md\u003c/code\u003e, а не «устная договорённость».\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 class=\"heading\" id=\"набор-команд-который-реально-работал-в-этой-сессии\"\u003e\n  Набор команд, который реально работал в этой сессии\n  \u003ca class=\"anchor\" href=\"#%d0%bd%d0%b0%d0%b1%d0%be%d1%80-%d0%ba%d0%be%d0%bc%d0%b0%d0%bd%d0%b4-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b9-%d1%80%d0%b5%d0%b0%d0%bb%d1%8c%d0%bd%d0%be-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d0%bb-%d0%b2-%d1%8d%d1%82%d0%be%d0%b9-%d1%81%d0%b5%d1%81%d1%81%d0%b8%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Проверки контента и даты\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo list future\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo --minify\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Диагностика\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003erg -n \u003cspan class=\"s2\"\u003e\u0026#34;pagefind|highlight|chroma\u0026#34;\u003c/span\u003e -S .\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esed -n \u003cspan class=\"s1\"\u003e\u0026#39;1,220p\u0026#39;\u003c/span\u003e layouts/search/single.html\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Публикация\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit add \u0026lt;файлы\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit commit -m \u003cspan class=\"s2\"\u003e\u0026#34;Осмысленный коммит по одной задаче\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit push\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eНикакой экзотики: сила не в сложных командах, а в дисциплине последовательности.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"практические-выводы\"\u003e\n  Практические выводы\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%b0%d0%ba%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b5-%d0%b2%d1%8b%d0%b2%d0%be%d0%b4%d1%8b\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eАвтоматизация блога через Codex CLI работает лучше всего, когда вы относитесь к ней как к инженерному процессу:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eЧёткая постановка задач.\u003c/li\u003e\n\u003cli\u003eНаблюдаемость и проверки после каждого шага.\u003c/li\u003e\n\u003cli\u003eМаленькие коммиты с понятной ответственностью.\u003c/li\u003e\n\u003cli\u003eФиксация правил в репозитории.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eТогда Codex становится не «генератором текста», а операционным партнёром, который помогает держать качество и темп публикаций одновременно. ✅\u003c/p\u003e\n","date_modified":"2026-03-05T20:50:00+03:00","date_published":"2026-03-05T20:50:00+03:00","id":"https://kushnaren.co/posts/avtomatizaciya-hugo-bloga-s-codex-cli-realnyj-kejs/","image":"https://kushnaren.co/og/avtomatizaciya-hugo-bloga-s-codex-cli-realnyj-kejs.png","summary":"Показываю на реальном кейсе, как Codex CLI помогает вести Hugo-блог в production-режиме: структура задач, валидации, git-поток, исправление регрессий и устойчивый процесс публикации.","tags":["codex","hugo","автоматизация","devops"],"title":"Автоматизация Hugo-блога с Codex CLI: разбор реальной рабочей сессии 🤖","url":"https://kushnaren.co/posts/avtomatizaciya-hugo-bloga-s-codex-cli-realnyj-kejs/"},{"content_html":"\u003cp\u003e25 февраля 2026 года вышел \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.157.0\"\u003eHugo v0.157.0\u003c/a\u003e. На первый взгляд релиз выглядит как «ещё один минорный апдейт», но внутри есть сразу несколько изменений, которые заметно улучшают работу реальных проектов: особенно если у вас модульная архитектура, контент из нескольких репозиториев и внешние запросы в шаблонах.\u003c/p\u003e\n\u003cp\u003eВ этой статье разберём, что именно изменилось, где может быть практический эффект, и как пройти обновление без сюрпризов. 🚀\u003c/p\u003e\n\u003cp\u003eКому это релевантно сразу: командам, ведущим контент через Hugo Modules; авторам тем, использующим внешние API в шаблонах; всем, у кого билд иногда «висит» из-за медленного источника. Кому можно отложить: одиночный блог без модулей и без внешних запросов получит апгрейд почти прозрачно — обновляйтесь как часть обычной уборки технического долга.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"почему-релиз-01570-важен\"\u003e\n  Почему релиз 0.157.0 важен\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%80%d0%b5%d0%bb%d0%b8%d0%b7-01570-%d0%b2%d0%b0%d0%b6%d0%b5%d0%bd\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eГлавная идея релиза: Hugo становится устойчивее в «боевых» сценариях, где проект не ограничивается одной папкой \u003ccode\u003econtent\u003c/code\u003e и парой шаблонов.\u003c/p\u003e\n\u003cp\u003eКлючевые точки:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003ccode\u003ePage.GitInfo\u003c/code\u003e теперь полноценно поддерживает контент из Git-модулей (через mounts).\u003c/li\u003e\n\u003cli\u003eВ \u003ccode\u003eresources.GetRemote\u003c/code\u003e появился перезапросный \u003ccode\u003etimeout\u003c/code\u003e, который можно задавать прямо в шаблоне.\u003c/li\u003e\n\u003cli\u003eДобавлена частичная поддержка AVIF/HEIF/HEIC (пока только метаданные).\u003c/li\u003e\n\u003cli\u003eУлучшены дефолты обработки WebP.\u003c/li\u003e\n\u003cli\u003eИсправлены ошибки, влияющие на меню, section pages и стабильность сборок.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eЕсли коротко: меньше «магии», больше управляемости. 🎯\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"gitinfo-для-hugo-modules-что-реально-изменилось-\"\u003e\n  GitInfo для Hugo Modules: что реально изменилось 🧩\n  \u003ca class=\"anchor\" href=\"#gitinfo-%d0%b4%d0%bb%d1%8f-hugo-modules-%d1%87%d1%82%d0%be-%d1%80%d0%b5%d0%b0%d0%bb%d1%8c%d0%bd%d0%be-%d0%b8%d0%b7%d0%bc%d0%b5%d0%bd%d0%b8%d0%bb%d0%be%d1%81%d1%8c-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003ch3 class=\"heading\" id=\"проблема-до-01570\"\u003e\n  Проблема до 0.157.0\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d0%b0-%d0%b4%d0%be-01570\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eЕсли вы подключали контент через \u003ccode\u003emodule.imports.mounts\u003c/code\u003e (например, общий набор статей или документацию из отдельного репозитория), \u003ccode\u003eGitInfo\u003c/code\u003e мог работать неполно или нестабильно для таких страниц.\u003c/p\u003e\n\u003cp\u003eЭто мешало:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eкорректно выводить «последнее обновление» для импортированного контента;\u003c/li\u003e\n\u003cli\u003eстроить списки изменений по коммитам;\u003c/li\u003e\n\u003cli\u003eделать единый шаблон мета-блока для локального и модульного контента.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 class=\"heading\" id=\"что-даёт-01570\"\u003e\n  Что даёт 0.157.0\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d0%b4%d0%b0%d1%91%d1%82-01570\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eВ релизе добавлена и доработана поддержка \u003ccode\u003ePage.GitInfo\u003c/code\u003e для контента из Git-модулей. То есть страницы, примонтированные из модулей, теперь могут корректно отдавать данные о коммите: автора, дату, хеш, subject и т.д.\u003c/p\u003e\n\u003cp\u003eЭто особенно полезно для:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003edocs-порталов из нескольких репозиториев;\u003c/li\u003e\n\u003cli\u003eмультисайтов, где контентные команды работают независимо;\u003c/li\u003e\n\u003cli\u003eархитектуры «ядро темы + контент как модуль».\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 class=\"heading\" id=\"практический-шаблон\"\u003e\n  Практический шаблон\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%b0%d0%ba%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b9-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ewith\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.GitInfo\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e \u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;meta\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Обновлено: \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.AuthorDate.Format\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;2006-01-02 15:04\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e ·\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Автор: \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.AuthorName\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e ·\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    Коммит: \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ecode\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.AbbreviatedHash\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ecode\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eВажный operational-момент: в CI должен быть \u003cstrong\u003eне shallow clone\u003c/strong\u003e, иначе Git-данные могут быть неточными.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"resourcesgetremote-получил-timeout-контроль-вместо-зависаний-\"\u003e\n  \u003ccode\u003eresources.GetRemote\u003c/code\u003e получил \u003ccode\u003etimeout\u003c/code\u003e: контроль вместо зависаний ⏱️\n  \u003ca class=\"anchor\" href=\"#resourcesgetremote-%d0%bf%d0%be%d0%bb%d1%83%d1%87%d0%b8%d0%bb-timeout-%d0%ba%d0%be%d0%bd%d1%82%d1%80%d0%be%d0%bb%d1%8c-%d0%b2%d0%bc%d0%b5%d1%81%d1%82%d0%be-%d0%b7%d0%b0%d0%b2%d0%b8%d1%81%d0%b0%d0%bd%d0%b8%d0%b9-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003ch3 class=\"heading\" id=\"что-болело-раньше\"\u003e\n  Что болело раньше\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d0%b1%d0%be%d0%bb%d0%b5%d0%bb%d0%be-%d1%80%d0%b0%d0%bd%d1%8c%d1%88%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eКогда вы тянете внешние данные (RSS/JSON/изображения), один медленный источник мог подвесить сборку. На небольшом сайте это раздражает, а на большом CI-пайплайне превращается в регулярную проблему.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"что-появилось-в-01570\"\u003e\n  Что появилось в 0.157.0\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d0%bf%d0%be%d1%8f%d0%b2%d0%b8%d0%bb%d0%be%d1%81%d1%8c-%d0%b2-01570\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eТеперь в \u003ccode\u003eresources.GetRemote\u003c/code\u003e есть опция \u003ccode\u003etimeout\u003c/code\u003e (строка длительности, например \u003ccode\u003e\u0026quot;10s\u0026quot;\u003c/code\u003e). Её можно задавать для каждого запроса отдельно.\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$url\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e:=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;https://example.org/feed.rss\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$opts\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e:=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003edict\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;timeout\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;10s\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ewith\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003etry\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eresources\u003c/span\u003e\u003cspan class=\"na\"\u003e.GetRemote\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$url\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$opts\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ewith\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Err\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ewarnf\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Не удалось получить feed %q: %s\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$url\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ewith\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Value\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$data\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e:=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003etransform\u003c/span\u003e\u003cspan class=\"na\"\u003e.Unmarshal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cm\"\u003e{{/* дальнейшая работа с данными */}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$data\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ewarnf\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Ресурс %q не найден\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e$url\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eПрактический эффект:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eможно задавать строгий timeout для нестабильных источников;\u003c/li\u003e\n\u003cli\u003eсборка перестаёт «висеть» из-за одного внешнего API;\u003c/li\u003e\n\u003cli\u003eпроще разделить критичные и некритичные источники (где-то \u003ccode\u003eerrorf\u003c/code\u003e, где-то \u003ccode\u003ewarnf\u003c/code\u003e).\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 class=\"heading\" id=\"изображения-что-важно-понимать-про-avifheifheic-и-webp\"\u003e\n  Изображения: что важно понимать про AVIF/HEIF/HEIC и WebP\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d0%b7%d0%be%d0%b1%d1%80%d0%b0%d0%b6%d0%b5%d0%bd%d0%b8%d1%8f-%d1%87%d1%82%d0%be-%d0%b2%d0%b0%d0%b6%d0%bd%d0%be-%d0%bf%d0%be%d0%bd%d0%b8%d0%bc%d0%b0%d1%82%d1%8c-%d0%bf%d1%80%d0%be-avifheifheic-%d0%b8-webp\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eВ 0.157.0 добавили \u003cstrong\u003eчастичную\u003c/strong\u003e поддержку AVIF/HEIF/HEIC: на текущем этапе это поддержка метаданных, а не полноценный новый pipeline обработки.\u003c/p\u003e\n\u003cp\u003eЧто это значит на практике:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eHugo лучше распознаёт такие файлы;\u003c/li\u003e\n\u003cli\u003eможно безопаснее работать с метаданными;\u003c/li\u003e\n\u003cli\u003eно не стоит ожидать полной parity с привычной обработкой JPEG/PNG/WebP прямо «из коробки».\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eТакже в релизе скорректированы настройки обработки WebP. Если вы строите агрессивный image pipeline, стоит прогнать визуальные regression-проверки на ключевых страницах.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"дополнительные-исправления-которые-могут-всплыть-в-проде\"\u003e\n  Дополнительные исправления, которые могут «всплыть» в проде\n  \u003ca class=\"anchor\" href=\"#%d0%b4%d0%be%d0%bf%d0%be%d0%bb%d0%bd%d0%b8%d1%82%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d0%b8%d1%81%d0%bf%d1%80%d0%b0%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b5-%d0%bc%d0%be%d0%b3%d1%83%d1%82-%d0%b2%d1%81%d0%bf%d0%bb%d1%8b%d1%82%d1%8c-%d0%b2-%d0%bf%d1%80%d0%be%d0%b4%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eВ релизе есть не самые заметные, но полезные улучшения:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eфикс \u003ccode\u003emenu pageRef\u003c/code\u003e в multidimensional-настройках;\u003c/li\u003e\n\u003cli\u003eкорректировки в логике section pages;\u003c/li\u003e\n\u003cli\u003eулучшенные сообщения об ошибках модулей (\u003ccode\u003ego mod download\u003c/code\u003e);\u003c/li\u003e\n\u003cli\u003eпропуск пустых taxonomy-ключей/значений в конфиге.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eКаждый пункт по отдельности небольшой, но вместе они снижают количество «странных» инцидентов после релиза.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"сценарий-который-раньше-ломался-контент-в-нескольких-модулях\"\u003e\n  Сценарий, который раньше ломался: контент в нескольких модулях\n  \u003ca class=\"anchor\" href=\"#%d1%81%d1%86%d0%b5%d0%bd%d0%b0%d1%80%d0%b8%d0%b9-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b9-%d1%80%d0%b0%d0%bd%d1%8c%d1%88%d0%b5-%d0%bb%d0%be%d0%bc%d0%b0%d0%bb%d1%81%d1%8f-%d0%ba%d0%be%d0%bd%d1%82%d0%b5%d0%bd%d1%82-%d0%b2-%d0%bd%d0%b5%d1%81%d0%ba%d0%be%d0%bb%d1%8c%d0%ba%d0%b8%d1%85-%d0%bc%d0%be%d0%b4%d1%83%d0%bb%d1%8f%d1%85\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЧтобы понять ценность изменений вокруг GitInfo и section pages, представьте типичную mid-size-конфигурацию: основной репозиторий с темой, плюс два модуля с контентом — \u003ccode\u003edocs/\u003c/code\u003e и \u003ccode\u003eblog/\u003c/code\u003e, каждый в своём Git-репо. Их подмонтировали в основной сайт через \u003ccode\u003emodule.imports.mounts\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eДо 0.157.0 в этой конфигурации возникали мелкие неприятности: даты в footer\u0026rsquo;е импортированных постов отдавали поведение «default-на-momento-сборки» вместо «коммит-автор-дата»; menu иногда теряла pageRef для импортированных страниц при определённых вариантах языковой настройки; ошибки \u003ccode\u003ego mod download\u003c/code\u003e приходили без контекста, и приходилось вручную перебирать модули, чтобы найти проблемный.\u003c/p\u003e\n\u003cp\u003eВ 0.157.0 каждый из этих краевых случаев починен. Если вы держите такой setup, после обновления заметна разница: меньше ручных правок шаблонов под «странности» modular-контента, прозрачнее ошибки при сетевых проблемах с одним из модулей.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"когда-обновляться-можно-отложить\"\u003e\n  Когда обновляться можно отложить\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d1%8f%d1%82%d1%8c%d1%81%d1%8f-%d0%bc%d0%be%d0%b6%d0%bd%d0%be-%d0%be%d1%82%d0%bb%d0%be%d0%b6%d0%b8%d1%82%d1%8c\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЕсли у вас одиночный сайт без модулей, без \u003ccode\u003eresources.GetRemote\u003c/code\u003e в шаблонах, без AVIF/HEIF в pipeline и без активных кастомных меню с \u003ccode\u003epageRef\u003c/code\u003e — этот релиз вас почти не коснётся. Обновитесь в плановом окне (вместе с пересборкой Docker-образа, например), но срочности нет.\u003c/p\u003e\n\u003cp\u003eСрочно обновляться стоит, если: используете \u003ccode\u003eresources.GetRemote\u003c/code\u003e и хотя бы раз попадали на медленный source; ведёте мультимодульный контент; пишете кастомные шаблоны с \u003ccode\u003e.GitInfo\u003c/code\u003e для импортированных страниц.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"чек-лист-обновления-до-hugo-01570-\"\u003e\n  Чек-лист обновления до Hugo 0.157.0 ✅\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b5%d0%ba-%d0%bb%d0%b8%d1%81%d1%82-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%b4%d0%be-hugo-01570-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eОбновите бинарь/образ Hugo до \u003ccode\u003e0.157.0\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eЕсли используете \u003ccode\u003eGitInfo\u003c/code\u003e, проверьте \u003ccode\u003eenableGitInfo: true\u003c/code\u003e в конфигурации.\u003c/li\u003e\n\u003cli\u003eУбедитесь, что CI делает глубокий клон репозитория (\u003ccode\u003efetch-depth: 0\u003c/code\u003e в GitHub Actions или эквивалент).\u003c/li\u003e\n\u003cli\u003eДля всех \u003ccode\u003eresources.GetRemote\u003c/code\u003e проставьте осмысленный \u003ccode\u003etimeout\u003c/code\u003e и единообразный \u003ccode\u003etry\u003c/code\u003e-обработчик.\u003c/li\u003e\n\u003cli\u003eПрогоните smoke-тесты меню, section pages и страниц с импортированным модульным контентом.\u003c/li\u003e\n\u003cli\u003eПроверьте image pipeline (особенно WebP) на визуальные отличия.\u003c/li\u003e\n\u003cli\u003eСравните длительность сборки до/после обновления.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 class=\"heading\" id=\"мини-план-миграции-на-30-минут\"\u003e\n  Мини-план миграции на 30 минут\n  \u003ca class=\"anchor\" href=\"#%d0%bc%d0%b8%d0%bd%d0%b8-%d0%bf%d0%bb%d0%b0%d0%bd-%d0%bc%d0%b8%d0%b3%d1%80%d0%b0%d1%86%d0%b8%d0%b8-%d0%bd%d0%b0-30-%d0%bc%d0%b8%d0%bd%d1%83%d1%82\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЕсли проект уже в хорошем состоянии, переход можно сделать быстро:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e10 минут: поднять версию и собрать сайт локально без warning/error.\u003c/li\u003e\n\u003cli\u003e10 минут: проверить шаблоны с \u003ccode\u003e.GitInfo\u003c/code\u003e и блоки с \u003ccode\u003eresources.GetRemote\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e10 минут: прогнать CI и открыть несколько критичных страниц руками.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eОбычно этого достаточно, чтобы перейти на релиз без ночных откатов.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"итог-\"\u003e\n  Итог 🧠\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%82%d0%be%d0%b3-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eHugo 0.157.0 не про «громкие фичи ради фич», а про зрелость платформы: лучшее поведение в модульных проектах, более предсказуемая работа с внешними запросами и аккуратные улучшения инфраструктурного уровня.\u003c/p\u003e\n\u003cp\u003eЕсли у вас контент разбит на модули, а сборка зависит от внешних данных, обновление стоит делать в ближайшем цикле.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"источники\"\u003e\n  Источники\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eОфициальный релиз: \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.157.0\"\u003egohugoio/hugo v0.157.0\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eДокументация \u003ccode\u003ePage.GitInfo\u003c/code\u003e: \u003ca href=\"https://gohugo.io/methods/page/gitinfo/\"\u003egohugo.io/methods/page/gitinfo\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eДокументация \u003ccode\u003eresources.GetRemote\u003c/code\u003e: \u003ca href=\"https://gohugo.io/functions/resources/getremote/\"\u003egohugo.io/functions/resources/getremote\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","date_modified":"2026-03-05T19:50:00+03:00","date_published":"2026-03-05T19:50:00+03:00","id":"https://kushnaren.co/posts/hugo-0-157-0-gitinfo-moduli-timeouty-getremote/","image":"https://kushnaren.co/og/hugo-0-157-0-gitinfo-moduli-timeouty-getremote.png","summary":"Разбираю ключевые нововведения Hugo v0.157.0 и показываю, как безопасно обновиться: что проверить в CI, шаблонах, модулях и интеграциях с внешними API.","tags":["hugo","релизы","devops"],"title":"Hugo 0.157.0: GitInfo для модулей, timeout в GetRemote и практическая миграция 🚀","url":"https://kushnaren.co/posts/hugo-0-157-0-gitinfo-moduli-timeouty-getremote/"},{"content_html":"\u003ch2 class=\"heading\" id=\"зачем-вообще-читать-про-минорный-релиз\"\u003e\n  Зачем вообще читать про минорный релиз\n  \u003ca class=\"anchor\" href=\"#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%b2%d0%be%d0%be%d0%b1%d1%89%d0%b5-%d1%87%d0%b8%d1%82%d0%b0%d1%82%d1%8c-%d0%bf%d1%80%d0%be-%d0%bc%d0%b8%d0%bd%d0%be%d1%80%d0%bd%d1%8b%d0%b9-%d1%80%d0%b5%d0%bb%d0%b8%d0%b7\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003e18 февраля 2026 вышел \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.156.0\"\u003eHugo v0.156.0\u003c/a\u003e. Если поверхностно прочитать changelog, это очередной минор: пара новых методов, несколько депрекейтов, опция конфига. Звучит как «обновлюсь когда руки дойдут».\u003c/p\u003e\n\u003cp\u003eНо именно из таких минорных релизов и копится технический долг. Депрекейт API сегодня — это broken-build через год, когда вы решите перейти на крупную версию. Минута, потраченная сейчас, чтобы привести шаблоны в соответствие, экономит вечер разбирательств позже.\u003c/p\u003e\n\u003cp\u003eВ этом посте — что реально изменилось, что заметно в шаблонах, и короткий чек-лист, который проходится за 30 минут.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"главное-за-один-взгляд\"\u003e\n  Главное за один взгляд\n  \u003ca class=\"anchor\" href=\"#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d0%be%d0%b5-%d0%b7%d0%b0-%d0%be%d0%b4%d0%b8%d0%bd-%d0%b2%d0%b7%d0%b3%d0%bb%d1%8f%d0%b4\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eРелиз 0.156.0 содержит шесть значимых изменений:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eДобавлены новые API: \u003ccode\u003ehugo.Data\u003c/code\u003e и \u003ccode\u003ehugo.Sites\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eУстаревшими объявлены \u003ccode\u003esite.Data\u003c/code\u003e и \u003ccode\u003esite.Sites\u003c/code\u003e (пока работают для обратной совместимости).\u003c/li\u003e\n\u003cli\u003eВ контекст link render hooks добавлены методы \u003ccode\u003eIsBlock\u003c/code\u003e и \u003ccode\u003eOrdinal\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eДобавлен новый метод \u003ccode\u003esite.Home.Translations\u003c/code\u003e; \u003ccode\u003esite.Home.AllTranslations\u003c/code\u003e помечен как deprecated.\u003c/li\u003e\n\u003cli\u003eПоявилась опция конфигурации \u003ccode\u003edisableSetLocale\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eПомечен как deprecated путь \u003ccode\u003e_vendor/github.com/gohugoio/locales\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eДальше — по каждому пункту, что это значит на практике и что менять.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"переезд-sitedata--hugodata-\"\u003e\n  Переезд site.Data → hugo.Data 🧱\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%b5%d1%80%d0%b5%d0%b5%d0%b7%d0%b4-sitedata--hugodata-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eСамое заметное смысловое изменение — переезд глобальных коллекций данных и сайтов с пространства имён \u003ccode\u003esite.*\u003c/code\u003e на \u003ccode\u003ehugo.*\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eСейчас в шаблонах вы могли писать:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003erange\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esite\u003c/span\u003e\u003cspan class=\"na\"\u003e.Data.authors\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eПосле 0.156 рекомендуется:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003erange\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ehugo\u003c/span\u003e\u003cspan class=\"na\"\u003e.Data.authors\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eВерсия со старым \u003ccode\u003esite.Data\u003c/code\u003e пока работает — ради совместимости. Но в release notes явно сказано: новые методы предпочтительнее, старые помечены как deprecated. Когда придёт следующая major-версия (Hugo любит чистить депрекейты), \u003ccode\u003esite.Data\u003c/code\u003e могут совсем убрать.\u003c/p\u003e\n\u003cp\u003eАналогичная история с \u003ccode\u003esite.Sites\u003c/code\u003e → \u003ccode\u003ehugo.Sites\u003c/code\u003e. Используется реже, в основном на мультисайтах с подмножеством общих ресурсов.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eЧто делать:\u003c/strong\u003e массовый поиск-замена в шаблонах темы и проекта:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egrep -rn \u003cspan class=\"s1\"\u003e\u0026#39;site\\.Data\\|site\\.Sites\u0026#39;\u003c/span\u003e layouts/ themes/*/layouts/ 2\u0026gt;/dev/null\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eЕсли хитов нет — вы никак не зависите от этого изменения. Если есть — заменить на \u003ccode\u003ehugo.Data\u003c/code\u003e и \u003ccode\u003ehugo.Sites\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"render-hooks-isblock-и-ordinal-\"\u003e\n  Render hooks: IsBlock и Ordinal 🔗\n  \u003ca class=\"anchor\" href=\"#render-hooks-isblock-%d0%b8-ordinal-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЕсли у вас есть кастомные \u003ca href=\"https://gohugo.io/render-hooks/links/\"\u003elink render hooks\u003c/a\u003e (файл \u003ccode\u003elayouts/_default/_markup/render-link.html\u003c/code\u003e или подобный), в их контексте теперь доступны два новых метода:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eIsBlock\u003c/code\u003e — true, если ссылка стоит как самостоятельный блок (например, \u003ccode\u003e\u0026lt;p\u0026gt;[link](url)\u0026lt;/p\u0026gt;\u003c/code\u003e где это единственный элемент абзаца), а не inline внутри текста.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOrdinal\u003c/code\u003e — порядковый номер ссылки в документе, начиная с 0.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЗачем это нужно:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e{{/* render-link.html */}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.IsBlock\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cm\"\u003e{{/* Самостоятельная ссылка — рендерим как карточку */}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e \u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;link-card\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Destination\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esafeURL\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003espan\u003c/span\u003e \u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;link-card__num\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e№\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eadd\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Ordinal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003e1\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003espan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003espan\u003c/span\u003e \u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;link-card__text\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Text\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esafeHTML\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003espan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cm\"\u003e{{/* Inline-ссылка — обычный \u0026lt;a\u0026gt; */}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Destination\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esafeURL\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Text\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esafeHTML\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eЭто удобно для блогов с активным использованием ссылок: можно по-разному оформлять «отдельные ссылки на источники» и «inline-упоминания».\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eOrdinal\u003c/code\u003e помогает строить нумерованные списки источников или footnote-style references без ручной разметки.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"мультиязычность-sitehometranslations-\"\u003e\n  Мультиязычность: site.Home.Translations 🌐\n  \u003ca class=\"anchor\" href=\"#%d0%bc%d1%83%d0%bb%d1%8c%d1%82%d0%b8%d1%8f%d0%b7%d1%8b%d1%87%d0%bd%d0%be%d1%81%d1%82%d1%8c-sitehometranslations-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eМетод \u003ccode\u003esite.Home.AllTranslations\u003c/code\u003e помечен как deprecated. Заменяет его более узкий \u003ccode\u003esite.Home.Translations\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eРазница:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eAllTranslations\u003c/code\u003e возвращает \u003cstrong\u003eвсе\u003c/strong\u003e языковые версии главной страницы, \u003cstrong\u003eвключая текущую\u003c/strong\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eTranslations\u003c/code\u003e возвращает только \u003cstrong\u003eдругие\u003c/strong\u003e языки, без текущей версии.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eСтарый код:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003erange\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esite\u003c/span\u003e\u003cspan class=\"na\"\u003e.Home.AllTranslations\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ene\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Lang\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e$.Lang\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Permalink\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Language.LanguageName\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eПосле 0.156 ту же логику можно записать прямее:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go-html-template\" data-lang=\"go-html-template\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003erange\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esite\u003c/span\u003e\u003cspan class=\"na\"\u003e.Home.Translations\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Permalink\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"na\"\u003e.Language.LanguageName\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e{{\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eend\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"cp\"\u003e}}\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eМеньше кода, понятнее намерение. Если вы вели мультиязычный сайт с языковым переключателем — самое время заменить.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"опция-disablesetlocale-\"\u003e\n  Опция disableSetLocale ⚙️\n  \u003ca class=\"anchor\" href=\"#%d0%be%d0%bf%d1%86%d0%b8%d1%8f-disablesetlocale-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eВ конфиге появилась новая опция:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edisableSetLocale\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eПо умолчанию Hugo пытается установить системную локаль операционной системы согласно языку текущего сайта. Это влияет на форматирование дат, чисел, сортировку строк (\u003ccode\u003estrings.Sort\u003c/code\u003e).\u003c/p\u003e\n\u003cp\u003eИногда это поведение нежелательно. Например, в CI-контейнере может не быть нужной локали, и Hugo молча падает на дефолт. Или вы хотите, чтобы поведение было одинаковым на всех машинах сборки независимо от языка контента.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003edisableSetLocale = true\u003c/code\u003e отключает эту попытку. Hugo использует значения из конфига и встроенные форматтеры без обращения к OS-уровню \u003ccode\u003esetlocale()\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eКогда нужно: CI-сборки на минимальных образах (alpine, debian-slim), где локали могут отсутствовать; репозитории с воспроизводимой сборкой; ситуации, когда вы заметили различия в форматах между локальной сборкой и CI.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"депрекейт-пути-_vendorgithubcomgohugoiolocales\"\u003e\n  Депрекейт пути _vendor/github.com/gohugoio/locales\n  \u003ca class=\"anchor\" href=\"#%d0%b4%d0%b5%d0%bf%d1%80%d0%b5%d0%ba%d0%b5%d0%b9%d1%82-%d0%bf%d1%83%d1%82%d0%b8-_vendorgithubcomgohugoiolocales\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПуть \u003ccode\u003e_vendor/github.com/gohugoio/locales\u003c/code\u003e помечен как устаревший. Если вы его явно использовали (например, импортировали как submodule в проект), стоит начать миграцию на встроенные локали Hugo — релиз поддерживает их через стандартный механизм.\u003c/p\u003e\n\u003cp\u003eБольшинство пользователей этот путь не трогали, поэтому пункт скорее для разработчиков тем, чем для авторов сайтов.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"чек-лист-обновления-за-30-минут-\"\u003e\n  Чек-лист обновления за 30 минут ✅\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b5%d0%ba-%d0%bb%d0%b8%d1%81%d1%82-%d0%be%d0%b1%d0%bd%d0%be%d0%b2%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d0%b7%d0%b0-30-%d0%bc%d0%b8%d0%bd%d1%83%d1%82-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЧтобы пройти миграцию без сюрпризов:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eПоднять версию Hugo\u003c/strong\u003e до 0.161.0 (если ещё на старой) или 0.156.0 как промежуточный шаг. Если работаете в Docker, обновить \u003ccode\u003eARG HUGO_VERSION\u003c/code\u003e и пересобрать образ.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eПоиск устаревших API в шаблонах:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egrep -rn \u003cspan class=\"s1\"\u003e\u0026#39;site\\.Data\\|site\\.Sites\\|AllTranslations\u0026#39;\u003c/span\u003e layouts/ themes/*/layouts/\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eЕсли хиты есть — заменить на \u003ccode\u003ehugo.Data\u003c/code\u003e, \u003ccode\u003ehugo.Sites\u003c/code\u003e, \u003ccode\u003eTranslations\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eПроверить кастомные render hooks\u003c/strong\u003e, если они есть. Можно ли получить пользу от новых \u003ccode\u003eIsBlock\u003c/code\u003e и \u003ccode\u003eOrdinal\u003c/code\u003e?\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eРешить про \u003ccode\u003edisableSetLocale\u003c/code\u003e.\u003c/strong\u003e Если CI-сборки иногда дают разные форматы дат в зависимости от хоста, добавить опцию в конфиг.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eПрогнать сборку:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo --minify --gc\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eНе должно быть warning\u0026rsquo;ов. Если появились — каждый из них чем-то да полезен, не игнорировать.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eОткрыть несколько критичных страниц локально\u003c/strong\u003e, проверить, что меню, переводы, render hooks работают.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eЗакоммитить и задеплоить.\u003c/strong\u003e На статичном Hugo-сайте откат — это \u003ccode\u003egit revert\u003c/code\u003e плюс пересборка, минут 5.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 class=\"heading\" id=\"итог-\"\u003e\n  Итог 🧠\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%82%d0%be%d0%b3-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eHugo 0.156.0 — не «громкий релиз ради хайпа». Это пакет аккуратных архитектурных изменений: переезд на новые API, расширение возможностей для тех, кто пишет render hooks, чистка мультиязычной части.\u003c/p\u003e\n\u003cp\u003eГлавная польза от такого релиза — возможность снять немного технического долга прямо сейчас, пока депрекейты ещё работают. Пропустишь — через год будешь разбираться, почему что-то перестало рендериться.\u003c/p\u003e\n\u003cp\u003eЕсли у вас минимальный шаблонный код (стандартная тема без кастомных hook\u0026rsquo;ов и без мультиязычности), переход почти прозрачен. Если у вас активные render hooks или мультиязык — стоит выделить полчаса на ревизию.\u003c/p\u003e\n\u003cp\u003eИ да: каждый минор-релиз стоит читать. Не как обязательное домашнее задание, но как короткую инвестицию в собственный код.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"источники\"\u003e\n  Источники\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%81%d1%82%d0%be%d1%87%d0%bd%d0%b8%d0%ba%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eОфициальный релиз: \u003ca href=\"https://github.com/gohugoio/hugo/releases/tag/v0.156.0\"\u003egohugoio/hugo v0.156.0\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eДокументация по новым API: \u003ca href=\"https://gohugo.io/methods/hugo/data/\"\u003ehugo.Data\u003c/a\u003e, \u003ca href=\"https://gohugo.io/methods/hugo/sites/\"\u003ehugo.Sites\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eДокументация по переводам: \u003ca href=\"https://gohugo.io/methods/page/translations/\"\u003esite.Home.Translations\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eДокументация link render hooks: \u003ca href=\"https://gohugo.io/render-hooks/links/\"\u003eRender hooks: links\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","date_modified":"2026-02-18T21:00:00+03:00","date_published":"2026-02-18T21:00:00+03:00","id":"https://kushnaren.co/posts/hugo-0-156-0-obzor-novovvedenij/","image":"https://kushnaren.co/og/hugo-0-156-0-obzor-novovvedenij.png","summary":"Подробный разбор Hugo v0.156.0 по официальным release notes: переход на новые API hugo.Data/hugo.Sites, изменения в link render hooks, переводы site.Home, опция disableSetLocale, практический чек-лист обновления.","tags":["hugo","релизы","devops"],"title":"Hugo 0.156.0: что нового и что проверить после обновления 🚀","url":"https://kushnaren.co/posts/hugo-0-156-0-obzor-novovvedenij/"},{"content_html":"\u003ch2 class=\"heading\" id=\"зачем-вообще-делать-колбасу-дома\"\u003e\n  Зачем вообще делать колбасу дома\n  \u003ca class=\"anchor\" href=\"#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%b2%d0%be%d0%be%d0%b1%d1%89%d0%b5-%d0%b4%d0%b5%d0%bb%d0%b0%d1%82%d1%8c-%d0%ba%d0%be%d0%bb%d0%b1%d0%b0%d1%81%d1%83-%d0%b4%d0%be%d0%bc%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eМагазинная колбаса — продукт, который терпит много претензий: непрозрачный состав, переизбыток фосфатов, переменное качество от партии к партии, дискомфорт от того, что «там вообще мясо или нет». Домашняя — наоборот: ты сам решаешь, какое мясо, сколько соли, какие специи, как готовить, что положить в оболочку.\u003c/p\u003e\n\u003cp\u003eЭто не «дешевле магазинной» (по факту — нет, особенно с учётом времени). Это «честнее и вкуснее, если потратить время на освоение базовых пропорций». А пропорции — самое важное в колбасе. Если соль/специи/влага в норме, даже простой кусок мяса превращается в нормальный продукт.\u003c/p\u003e\n\u003cp\u003eЭтот пост — рабочая памятка с пропорциями, которые я использую как отправную точку, плюс короткое объяснение, зачем там каждый ингредиент. Без претензий на «единственно верный рецепт».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"соль-и-добавки-на-1-кг-\"\u003e\n  Соль и добавки на 1 кг 🧂\n  \u003ca class=\"anchor\" href=\"#%d1%81%d0%be%d0%bb%d1%8c-%d0%b8-%d0%b4%d0%be%d0%b1%d0%b0%d0%b2%d0%ba%d0%b8-%d0%bd%d0%b0-1-%d0%ba%d0%b3-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eБазовый набор:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eСоль:\u003c/strong\u003e 18-20 г.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eОптимальный вариант:\u003c/strong\u003e 16 г обычной соли + 2-3 г нитритной соли.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eСахар:\u003c/strong\u003e 5 г (примерно 1 ч. л.).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eЧёрный перец:\u003c/strong\u003e 1 ч. л.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eЧеснок:\u003c/strong\u003e 2 зубчика (около 8-10 г).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eМускатный орех:\u003c/strong\u003e щепотка.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eЛёд или ледяная вода:\u003c/strong\u003e 80-100 мл.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЭтот набор использую как отправную точку. Дальше корректирую специи под конкретное мясо и личный вкус.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"зачем-каждое-из-этого\"\u003e\n  Зачем каждое из этого\n  \u003ca class=\"anchor\" href=\"#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%ba%d0%b0%d0%b6%d0%b4%d0%be%d0%b5-%d0%b8%d0%b7-%d1%8d%d1%82%d0%be%d0%b3%d0%be\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЧтобы рецепт не казался магической формулой, коротко по каждому пункту:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСоль — 18-20 г на 1 кг.\u003c/strong\u003e Это не «по вкусу». Это пороговая концентрация: меньше — фарш плохо «связывается» (соль вытягивает миофибриллярные белки, которые удерживают воду), больше — переедено и опасно. Норма для большинства колбас 1.8-2%.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eНитритная соль — 2-3 г.\u003c/strong\u003e Главный вопрос. Нитрит даёт характерный розовый цвет, узнаваемый «колбасный» вкус и — главное — подавляет рост ботулинических бактерий. Без него любая колбаса с длительной выдержкой или копчением рискует превратиться в источник опасных токсинов. Не «химия, которую нужно избегать», а функциональный консервант, который существует не зря. Концентрация 2-3 г нитритной соли (а не чистого нитрита) — безопасный диапазон, давно стандартизированный.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСахар — 5 г.\u003c/strong\u003e Не для сладости. Для микробиоты — она «съедает» сахар и даёт лёгкое подкисление, плюс смягчает солёность.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eЧёрный перец, чеснок, мускат.\u003c/strong\u003e Базовая «колбасная» ароматическая основа. Каждый компонент можно менять под вкус (паприка, горчица в зёрнах, кориандр, тмин), но эта тройка — спокойная отправная точка.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eЛедяная вода — 80-100 мл.\u003c/strong\u003e Самое неочевидное для новичков. Вода не «разбавляет», а наоборот — вместе с белками образует эмульсию, которая держит структуру колбасы при нагреве. Если воду не добавить, колбаса получится сухой и зернистой. Если добавить тёплую — белки свернутся раньше времени, эмульсия не сложится. Лёд гарантирует, что фарш во время вымешивания остаётся холодным.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"технология-процесса\"\u003e\n  Технология процесса\n  \u003ca class=\"anchor\" href=\"#%d1%82%d0%b5%d1%85%d0%bd%d0%be%d0%bb%d0%be%d0%b3%d0%b8%d1%8f-%d0%bf%d1%80%d0%be%d1%86%d0%b5%d1%81%d1%81%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eКратко, без излишних деталей, чтобы держать целое в голове:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eПодготовка мяса.\u003c/strong\u003e Охладить до 1-3°C (не замораживать). Срезать жёсткие плёнки и хрящи, нарезать кусками удобного размера.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eПомол.\u003c/strong\u003e Через мясорубку с решёткой 4-6 мм. Чем мельче, тем «нежнее» структура; чем крупнее — тем «деревенская» подача.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eВнесение соли и нитрита.\u003c/strong\u003e На холодный фарш, тщательно перемешать.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eСпеции и сахар.\u003c/strong\u003e Добавить, перемешать снова.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eЛёд/ледяная вода.\u003c/strong\u003e Добавлять постепенно, продолжая вымешивать. Цель — получить однородную массу, в которой видна вязкость («тянется» при отрыве).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eХолодная пауза.\u003c/strong\u003e 1-3 часа в холодильнике, чтобы соль распределилась и фарш «созрел».\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eНабивка.\u003c/strong\u003e В оболочку (натуральную или коллагеновую), без воздушных пузырей.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eТермообработка.\u003c/strong\u003e Зависит от типа: варка при 75-80°C до внутренней температуры 70-72°C, копчение, запекание.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eЭта последовательность — каркас. Каждый шаг можно тонко донастраивать, но если убрать любой из них, результат заметно проседает.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"типичные-ошибки-начинающих-\"\u003e\n  Типичные ошибки начинающих 🪤\n  \u003ca class=\"anchor\" href=\"#%d1%82%d0%b8%d0%bf%d0%b8%d1%87%d0%bd%d1%8b%d0%b5-%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b8-%d0%bd%d0%b0%d1%87%d0%b8%d0%bd%d0%b0%d1%8e%d1%89%d0%b8%d1%85-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eСамые частые сбои, которые легко избежать, если знать заранее:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСлишком тёплый фарш.\u003c/strong\u003e Если на любом этапе фарш нагрелся выше 12-15°C, эмульсия не сложится. Жир «вытопится» при варке, в продукте появятся жирные карманы. Лёд обязателен.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eПереизбыток соли.\u003c/strong\u003e «Я люблю солёное» не работает. Превысил 25 г на кг — продукт не съесть, а ребалансировать сложно: соль из готовой колбасы не уберёшь.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eНедомес.\u003c/strong\u003e Если фарш не дошёл до состояния «вязкий, тянется» — структура развалится. Вымешивание — главное, что делает колбасу колбасой, а не «фарш в кишке».\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eВоздух в оболочке.\u003c/strong\u003e Пузыри = места, где может развиваться нежелательная микрофлора. Прокалывать иголкой при набивке — нормальная практика.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eРезкий перегрев при варке.\u003c/strong\u003e Внутри колбасы должно быть 70-72°C, не больше. Перегрев = выплавление жира + сухость. Для контроля — кулинарный термометр; «на глаз» в этом деле подводит.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eСпешка с дегустацией.\u003c/strong\u003e Колбасе нужно «созреть» в холодильнике хотя бы 12-24 часа после варки. Свежевыкипяченная — это не финальный вкус.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"варианты-по-виду-мяса\"\u003e\n  Варианты по виду мяса\n  \u003ca class=\"anchor\" href=\"#%d0%b2%d0%b0%d1%80%d0%b8%d0%b0%d0%bd%d1%82%d1%8b-%d0%bf%d0%be-%d0%b2%d0%b8%d0%b4%d1%83-%d0%bc%d1%8f%d1%81%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eБазовый рецепт работает для свинины и для смесей. Несколько типичных вариаций:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eСвинина (60% мякоти + 40% жирной части).\u003c/strong\u003e Классика, сбалансированный вкус, хороший для новичков.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eСвинина + говядина (70/30).\u003c/strong\u003e Более «мясной» характер, плотнее структура, чуть жёстче.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eТолько говядина.\u003c/strong\u003e Суше, требует больше воды и аккуратности с эмульсией; вкус интенсивнее.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eПтица (грудка + бедро).\u003c/strong\u003e Нежнее, ниже жирность; нужно поднять долю воды и тщательнее вымешивать.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eСоль и нитрит — те же. Воды иногда нужно немного больше для постного мяса.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"безопасность-\"\u003e\n  Безопасность ⚠️\n  \u003ca class=\"anchor\" href=\"#%d0%b1%d0%b5%d0%b7%d0%be%d0%bf%d0%b0%d1%81%d0%bd%d0%be%d1%81%d1%82%d1%8c-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eНесколько моментов, о которых стоит знать обязательно:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eНитритная соль ≠ чистый нитрит.\u003c/strong\u003e Это уже разбавленная смесь (обычно 0.5-0.6% NaNO₂ в обычной соли). Использовать строго по дозировке для нитритной соли, не для чистого вещества.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eБотулизм.\u003c/strong\u003e Главная причина существования нитрита в колбасном деле. При длительной выдержке без нитрита риск реален; не «древняя страшилка».\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eКонтроль температуры.\u003c/strong\u003e Внутренняя температура готового продукта 70-72°C — это не «совет», это безопасность.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eСроки хранения.\u003c/strong\u003e Домашняя колбаса без консервантов хранится в холодильнике значительно меньше магазинной. Замораживание — ок, но текстура немного меняется.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЕсли делаете колбасу впервые — стоит прочитать пару проверенных источников по технологии (например, Stanley Marianski «Home Production of Quality Meats and Sausages» или книги по колбасному делу на русском). Это не та область, где «методом тыка» — оптимальный путь обучения.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"куда-дальше-если-втянулся\"\u003e\n  Куда дальше, если втянулся\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d1%83%d0%b4%d0%b0-%d0%b4%d0%b0%d0%bb%d1%8c%d1%88%d0%b5-%d0%b5%d1%81%d0%bb%d0%b8-%d0%b2%d1%82%d1%8f%d0%bd%d1%83%d0%bb%d1%81%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЕсли базовый цикл стал привычным, есть много направлений для развития:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eСухое созревание\u003c/strong\u003e (колбаса как salami). Долгий процесс, требует контроля температуры и влажности.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eКопчение\u003c/strong\u003e (горячее или холодное). Меняет вкус и продлевает хранение.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eСвои смеси специй.\u003c/strong\u003e Эксперименты с паприкой, фенхелем, кориандром, перцем чили — ровно по чайной ложке, чтобы не «перегрузить».\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eНатуральные оболочки.\u003c/strong\u003e Свиная черева, говяжьи синюги, бараньи кишки — каждая дает свою текстуру.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eНо это уже отдельная история. Базовый рецепт хорошо работает сам по себе и закрывает большую часть домашних запросов.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"итоги\"\u003e\n  Итоги\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%82%d0%be%d0%b3%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eКолбаса — простой по сути продукт, который очень чувствителен к деталям. Соль и нитрит обеспечивают безопасность и связку, лёд держит эмульсию, специи дают характер. Если эти три блока в порядке, всё остальное — настройка под вкус.\u003c/p\u003e\n\u003cp\u003eЭтот рецепт — мой рабочий минимум. Точные пропорции вы со временем подгоните под себя, но 18-20 г соли, 80-100 мл ледяной воды, обязательная нитритная составляющая и аккуратное вымешивание — фундамент, на котором всё остальное держится. 🍴\u003c/p\u003e\n","date_modified":"2026-02-11T12:00:00+03:00","date_published":"2026-02-11T12:00:00+03:00","id":"https://kushnaren.co/posts/domashnyaya-kolbasa-bazovyj-recept-na-1-kg/","image":"https://kushnaren.co/og/domashnyaya-kolbasa-bazovyj-recept-na-1-kg.png","summary":"Рабочая памятка по домашней колбасе на 1 кг: соль, нитрит, специи, ледяная вода, а также технология фарш-набивка-термообработка и типичные ошибки начинающих.","tags":["рецепт","колбаса","домашняя кухня"],"title":"Домашняя колбаса: базовый рецепт на 1 кг 🥓","url":"https://kushnaren.co/posts/domashnyaya-kolbasa-bazovyj-recept-na-1-kg/"},{"content_html":"\u003ch2 class=\"heading\" id=\"почему-вообще-в-это-ввязались\"\u003e\n  Почему вообще в это ввязались\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d0%b2%d0%be%d0%be%d0%b1%d1%89%d0%b5-%d0%b2-%d1%8d%d1%82%d0%be-%d0%b2%d0%b2%d1%8f%d0%b7%d0%b0%d0%bb%d0%b8%d1%81%d1%8c\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eМы с моим другом Андреем давно обсуждали идею «разобраться, как это работает на самом деле». Самогоноварение показалось подходящим испытанием: техническая часть, химия, дисциплина, плюс шанс сделать что-то необычное своими руками. Не для того, чтобы «гнать в промышленных масштабах», а чтобы пройти процесс целиком и понять, где спрятаны нюансы, о которых снаружи не догадаешься.\u003c/p\u003e\n\u003cp\u003eТак в нашей жизни появилась ректификационная колонна «Дом Спирт 2» и дерзкая цель — попробовать сделать абсент. Через несколько подходов мы получили результат, который можно назвать «похожим на абсент», и значительно больше — наблюдений о самом процессе.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-такое-ректификационная-колонна-упрощённо\"\u003e\n  Что такое ректификационная колонна (упрощённо)\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%82%d0%b0%d0%ba%d0%be%d0%b5-%d1%80%d0%b5%d0%ba%d1%82%d0%b8%d1%84%d0%b8%d0%ba%d0%b0%d1%86%d0%b8%d0%be%d0%bd%d0%bd%d0%b0%d1%8f-%d0%ba%d0%be%d0%bb%d0%be%d0%bd%d0%bd%d0%b0-%d1%83%d0%bf%d1%80%d0%be%d1%89%d1%91%d0%bd%d0%bd%d0%be\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eКолонна — это вертикальная труба, в которой пары спиртосодержащей жидкости поднимаются вверх, проходят через насадку (мелкие металлические или керамические элементы), частично конденсируются и стекают обратно. Это «ректификация» — многоступенчатая дистилляция в одном устройстве.\u003c/p\u003e\n\u003cp\u003eЭффект: получается высокая степень очистки, спирт-ректификат может быть крепче 90%, без значительной части примесей, которые делают «обычный самогон» неприятным.\u003c/p\u003e\n\u003cp\u003e«Дом Спирт 2» — российская модель домашней ректификационной колонны. Она не самая мощная и не самая компактная, но достаточно «честная»: достать, освоить базовый цикл, понять, как ведёт себя пар, давление, температура — реально для технически грамотного человека за несколько вечеров.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"первые-впечатления-техника--это-не-половина-дела-а-почти-всё\"\u003e\n  Первые впечатления: техника — это не половина дела, а почти всё\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d0%b5%d1%80%d0%b2%d1%8b%d0%b5-%d0%b2%d0%bf%d0%b5%d1%87%d0%b0%d1%82%d0%bb%d0%b5%d0%bd%d0%b8%d1%8f-%d1%82%d0%b5%d1%85%d0%bd%d0%b8%d0%ba%d0%b0--%d1%8d%d1%82%d0%be-%d0%bd%d0%b5-%d0%bf%d0%be%d0%bb%d0%be%d0%b2%d0%b8%d0%bd%d0%b0-%d0%b4%d0%b5%d0%bb%d0%b0-%d0%b0-%d0%bf%d0%be%d1%87%d1%82%d0%b8-%d0%b2%d1%81%d1%91\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eОказалось, что в этом хобби важна не только «железка», но и терпение. Ожидания были простыми: собрал, запустил, получил результат. Реальность — другая: время, аккуратность, много мелких нюансов и постоянное чувство, что упускаешь какую-то деталь.\u003c/p\u003e\n\u003cp\u003eНесколько вещей, которых мы не ожидали:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eДлительность процесса.\u003c/strong\u003e Полный цикл от загрузки до получения готового продукта — это много часов, иногда вся суббота.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eЧувствительность к деталям.\u003c/strong\u003e Изменение скорости отбора, температуры, охлаждения дефлегматора — каждое влияет на результат сильнее, чем казалось из видеообзоров.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eГигиена.\u003c/strong\u003e «Чисто» в обычном смысле и «чисто» для процесса — разные понятия.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eДисциплина протокола.\u003c/strong\u003e Если делать «на глаз» — результаты непредсказуемы. Если фиксировать каждый шаг — повторяемость растёт.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 class=\"heading\" id=\"чем-абсент-сложнее-обычного-дистиллята\"\u003e\n  Чем абсент сложнее «обычного дистиллята»\n  \u003ca class=\"anchor\" href=\"#%d1%87%d0%b5%d0%bc-%d0%b0%d0%b1%d1%81%d0%b5%d0%bd%d1%82-%d1%81%d0%bb%d0%be%d0%b6%d0%bd%d0%b5%d0%b5-%d0%be%d0%b1%d1%8b%d1%87%d0%bd%d0%be%d0%b3%d0%be-%d0%b4%d0%b8%d1%81%d1%82%d0%b8%d0%bb%d0%bb%d1%8f%d1%82%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eКлассический спирт-ректификат — это, грубо, очищенный этанол с водой. Абсент сложнее устроен:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eМацерация.\u003c/strong\u003e Травы (полынь, анис, фенхель и менее афишированные растения) настаиваются на готовом спирте.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eПерегонка с травами.\u003c/strong\u003e Полученный настой перегоняется заново — часть ароматических компонентов переходит в дистиллят, часть остаётся в кубе.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eФинальное окрашивание.\u003c/strong\u003e Дополнительная мацерация на свежих травах даёт характерный зелёный или жёлтый оттенок.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eКаждый из этих шагов имеет свои переменные: какие травы, в какой пропорции, при какой температуре, как долго. Мелкая ошибка на любом шаге даёт ощутимое отклонение в финальном вкусе. И если «обычный самогон» прощает многое, абсент прощает мало.\u003c/p\u003e\n\u003cp\u003eДополнительно — травы дают так называемые «термолабильные» компоненты, которые легко разрушаются при перегреве. Это даёт парадоксальную ситуацию: вы технически умеете гнать спирт, но при попытке сделать абсент получаете либо «горький эликсир», либо «спиртовая вода с лёгким намёком на полынь» — без середины.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-получилось\"\u003e\n  Что получилось\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d0%bf%d0%be%d0%bb%d1%83%d1%87%d0%b8%d0%bb%d0%be%d1%81%d1%8c\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eМы разобрались с базовой логикой процесса и перестали «бояться» оборудования.\u003c/li\u003e\n\u003cli\u003eПолучили результат, который можно оценивать — не идеально, но уже похоже на задуманное.\u003c/li\u003e\n\u003cli\u003eПоняли, какие этапы требуют больше контроля, чем казалось на старте.\u003c/li\u003e\n\u003cli\u003eНаучились читать поведение колонны: давление, температура, скорость капельного отбора.\u003c/li\u003e\n\u003cli\u003eСформировали собственный «лог сессии», который помогает повторять удачные попытки.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 class=\"heading\" id=\"что-не-получилось\"\u003e\n  Что не получилось\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d0%bd%d0%b5-%d0%bf%d0%be%d0%bb%d1%83%d1%87%d0%b8%d0%bb%d0%be%d1%81%d1%8c\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eСтабильность.\u003c/strong\u003e Один и тот же подход давал разный результат от подхода к подходу.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eПредсказуемость вкуса и аромата.\u003c/strong\u003e Это тонкая, почти ювелирная работа.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eТерпение.\u003c/strong\u003e Иногда проще поставить эксперимент на паузу, чем пытаться «додавить» его усталостью.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eТочная имитация классического абсента.\u003c/strong\u003e До «как в Швейцарии» далеко; пока ближе к «дегустационному образцу собственного производства».\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 class=\"heading\" id=\"главный-вывод\"\u003e\n  Главный вывод\n  \u003ca class=\"anchor\" href=\"#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d1%8b%d0%b9-%d0%b2%d1%8b%d0%b2%d0%be%d0%b4\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eСамогоноварение — это не «про напиток», а про процесс и дисциплину. Это про внимание к деталям и честность с самим собой: если хочешь качественный результат, придётся уважать все этапы, а не только финал.\u003c/p\u003e\n\u003cp\u003eЛюбая часть, которую попытаешься «срезать», даст видимый результат: либо во вкусе, либо в стабильности, либо в безопасности. Это, наверное, и есть главная ценность таких хобби: они напоминают, что качественный результат складывается из мелочей, которые поодиночке кажутся незначительными.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"безопасность-важное\"\u003e\n  Безопасность (важное)\n  \u003ca class=\"anchor\" href=\"#%d0%b1%d0%b5%d0%b7%d0%be%d0%bf%d0%b0%d1%81%d0%bd%d0%be%d1%81%d1%82%d1%8c-%d0%b2%d0%b0%d0%b6%d0%bd%d0%be%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eНесколько вещей, которые нужно проговорить отдельно:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eГолов и хвостов нельзя пить.\u003c/strong\u003e Первые и последние фракции содержат метанол, сивушные масла и другие соединения, потенциально опасные. Их отделяют по проверенной методике.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eВысокая концентрация спирта = высокая огнеопасность.\u003c/strong\u003e Колонна греется, пары горючи, помещение должно быть вентилируемым.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eЛицензирование и закон.\u003c/strong\u003e В разных странах правила производства алкоголя для личного потребления отличаются. Ответственность всегда на изготовителе.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eДегустация.\u003c/strong\u003e Не «глотнуть и надеяться». Малая проба, оценка по органолептике, никаких больших порций неотлежавшегося продукта.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЯ не даю инструкций и рецептов в этом посте сознательно. Это пост про опыт и наблюдения, а не учебник.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"зачем-писать-об-этом\"\u003e\n  Зачем писать об этом\n  \u003ca class=\"anchor\" href=\"#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%bf%d0%b8%d1%81%d0%b0%d1%82%d1%8c-%d0%be%d0%b1-%d1%8d%d1%82%d0%be%d0%bc\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПотому что это хороший пример живого эксперимента: мы не профессионалы, а просто люди, которым интересно разобраться. И именно в таких историях — ценность. Не в том, чтобы «повторить», а в том, чтобы увидеть путь, ошибки и выводы.\u003c/p\u003e\n\u003cp\u003eЛюбое сложное хобби работает по похожей траектории: первые попытки выглядят неуклюже, потом приходит понимание базовых механик, дальше — наблюдение за деталями, и только потом становится возможным «сознательно делать вкус», а не «случайно получать что-то близкое».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-дальше\"\u003e\n  Что дальше\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d0%b4%d0%b0%d0%bb%d1%8c%d1%88%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eМы продолжаем экспериментировать. Возможно, через пару месяцев будет вторая часть — с более стабильным результатом и ясными выводами. Пока же — фиксируем этап и идём дальше.\u003c/p\u003e\n\u003cp\u003eЕсли резюмировать одной фразой: самое важное, что я понял за эти подходы — терпение в этом хобби стоит больше, чем оборудование. Хорошая колонна не сделает за тебя дисциплину. А плохая дисциплина не оправдается дорогой колонной. 🧪\u003c/p\u003e\n","date_modified":"2026-02-04T12:00:00+03:00","date_published":"2026-02-04T12:00:00+03:00","id":"https://kushnaren.co/posts/samogonovarenie-absent-opyt-i-vyvody/","image":"https://kushnaren.co/og/samogonovarenie-absent-opyt-i-vyvody.png","summary":"Размышления после первого опыта с домашней ректификацией: почему вообще ввязались, что узнали про процесс, чем абсент сложнее обычного дистиллята, и какие выводы из этого сделали. Без рецептов и инструкций.","tags":["личный опыт","эксперименты","хобби","абсент"],"title":"Самогоноварение как эксперимент: наш опыт с абсентом 🍸","url":"https://kushnaren.co/posts/samogonovarenie-absent-opyt-i-vyvody/"},{"content_html":"\u003cp\u003eКогда блог ведётся регулярно, главная проблема часто не в том, «о чём писать», а в том, сколько мелкой рутины появляется вокруг каждой публикации: структура, фронтматтер, теги, формат даты, локальная проверка, коммит, пуш.\u003c/p\u003e\n\u003cp\u003eВ какой-то момент я понял простую вещь: если не автоматизировать технические повторяющиеся шаги, то темп публикаций будет определяться не качеством идей, а запасом терпения. Именно здесь Codex даёт максимальную пользу. 🚀\u003c/p\u003e\n\u003cp\u003eВажно уточнить позицию сразу: Codex не заменяет автора и редактора. Он ускоряет производство черновика, стабилизирует техническую часть и снимает «операционную усталость», чтобы энергия оставалась на смыслы.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"где-именно-теряется-время-при-выпуске-поста\"\u003e\n  Где именно теряется время при выпуске поста\n  \u003ca class=\"anchor\" href=\"#%d0%b3%d0%b4%d0%b5-%d0%b8%d0%bc%d0%b5%d0%bd%d0%bd%d0%be-%d1%82%d0%b5%d1%80%d1%8f%d0%b5%d1%82%d1%81%d1%8f-%d0%b2%d1%80%d0%b5%d0%bc%d1%8f-%d0%bf%d1%80%d0%b8-%d0%b2%d1%8b%d0%bf%d1%83%d1%81%d0%ba%d0%b5-%d0%bf%d0%be%d1%81%d1%82%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eДо автоматизации у меня стабильно проседали одни и те же этапы:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eПереход от идеи к первому цельному черновику.\u003c/li\u003e\n\u003cli\u003eРучное оформление фронтматтера без ошибок.\u003c/li\u003e\n\u003cli\u003eПоддержание одинакового уровня качества для \u003ccode\u003esummary\u003c/code\u003e и \u003ccode\u003edescription\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eПовторяемые технические действия перед публикацией.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eСами шаги простые, но они фрагментируют внимание. После двух-трёх переключений между «текстом» и «технической обвязкой» уже сложнее держать мысль статьи плотной и последовательной.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"какую-роль-я-отдаю-codex-а-какую-оставляю-себе\"\u003e\n  Какую роль я отдаю Codex, а какую оставляю себе\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d0%b0%d0%ba%d1%83%d1%8e-%d1%80%d0%be%d0%bb%d1%8c-%d1%8f-%d0%be%d1%82%d0%b4%d0%b0%d1%8e-codex-%d0%b0-%d0%ba%d0%b0%d0%ba%d1%83%d1%8e-%d0%be%d1%81%d1%82%d0%b0%d0%b2%d0%bb%d1%8f%d1%8e-%d1%81%d0%b5%d0%b1%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЧтобы автоматизация была полезной, роли должны быть разделены жёстко.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eЗа мной остаётся:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eтезис и позиция статьи;\u003c/li\u003e\n\u003cli\u003eфинальная логика аргументации;\u003c/li\u003e\n\u003cli\u003eличный опыт, выводы и тональность.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eCodex получает:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eсборку структурированного черновика из тезисов;\u003c/li\u003e\n\u003cli\u003eтехнически корректный фронтматтер;\u003c/li\u003e\n\u003cli\u003eконтроль повторяющихся формальностей;\u003c/li\u003e\n\u003cli\u003eподготовку git-части к публикации.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eТакая схема позволяет не спорить с инструментом о «вкусе текста», а использовать его там, где важнее точность и скорость.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"рабочий-конвейер-от-идеи-до-публикации\"\u003e\n  Рабочий конвейер: от идеи до публикации\n  \u003ca class=\"anchor\" href=\"#%d1%80%d0%b0%d0%b1%d0%be%d1%87%d0%b8%d0%b9-%d0%ba%d0%be%d0%bd%d0%b2%d0%b5%d0%b9%d0%b5%d1%80-%d0%be%d1%82-%d0%b8%d0%b4%d0%b5%d0%b8-%d0%b4%d0%be-%d0%bf%d1%83%d0%b1%d0%bb%d0%b8%d0%ba%d0%b0%d1%86%d0%b8%d0%b8\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eНиже процесс, который у меня реально работает в блоге на Hugo.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"1-формулирую-цель-поста-в-двух-фразах\"\u003e\n  1. Формулирую цель поста в двух фразах\n  \u003ca class=\"anchor\" href=\"#1-%d1%84%d0%be%d1%80%d0%bc%d1%83%d0%bb%d0%b8%d1%80%d1%83%d1%8e-%d1%86%d0%b5%d0%bb%d1%8c-%d0%bf%d0%be%d1%81%d1%82%d0%b0-%d0%b2-%d0%b4%d0%b2%d1%83%d1%85-%d1%84%d1%80%d0%b0%d0%b7%d0%b0%d1%85\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eПеред стартом фиксирую:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eдля кого материал;\u003c/li\u003e\n\u003cli\u003eкакое практическое действие читатель сможет сделать после прочтения.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЕсли это не ясно на старте, статья почти всегда расползается.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"2-даю-codex-каркас-будущего-текста\"\u003e\n  2. Даю Codex каркас будущего текста\n  \u003ca class=\"anchor\" href=\"#2-%d0%b4%d0%b0%d1%8e-codex-%d0%ba%d0%b0%d1%80%d0%ba%d0%b0%d1%81-%d0%b1%d1%83%d0%b4%d1%83%d1%89%d0%b5%d0%b3%d0%be-%d1%82%d0%b5%d0%ba%d1%81%d1%82%d0%b0\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eМинимальный вход:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eрабочий заголовок;\u003c/li\u003e\n\u003cli\u003e5-8 смысловых пунктов;\u003c/li\u003e\n\u003cli\u003e2-3 конкретных примера;\u003c/li\u003e\n\u003cli\u003eжелаемая глубина (коротко / подробно / разбор).\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eНа этом этапе я не прошу «красивый текст», я прошу структуру, которую удобно усиливать.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"3-собираю-длинный-черновик-и-сразу-проверяю-логические-провалы\"\u003e\n  3. Собираю длинный черновик и сразу проверяю логические провалы\n  \u003ca class=\"anchor\" href=\"#3-%d1%81%d0%be%d0%b1%d0%b8%d1%80%d0%b0%d1%8e-%d0%b4%d0%bb%d0%b8%d0%bd%d0%bd%d1%8b%d0%b9-%d1%87%d0%b5%d1%80%d0%bd%d0%be%d0%b2%d0%b8%d0%ba-%d0%b8-%d1%81%d1%80%d0%b0%d0%b7%d1%83-%d0%bf%d1%80%d0%be%d0%b2%d0%b5%d1%80%d1%8f%d1%8e-%d0%bb%d0%be%d0%b3%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d0%b2%d0%b0%d0%bb%d1%8b\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eОбычно я смотрю:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eнет ли повторов между разделами;\u003c/li\u003e\n\u003cli\u003eесть ли причинно-следственная связка между блоками;\u003c/li\u003e\n\u003cli\u003eне превращаются ли выводы в общие фразы.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЕсли провалы есть, лучше исправить их до стилистической шлифовки.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"4-редактирую-смысл-вручную\"\u003e\n  4. Редактирую смысл вручную\n  \u003ca class=\"anchor\" href=\"#4-%d1%80%d0%b5%d0%b4%d0%b0%d0%ba%d1%82%d0%b8%d1%80%d1%83%d1%8e-%d1%81%d0%bc%d1%8b%d1%81%d0%bb-%d0%b2%d1%80%d1%83%d1%87%d0%bd%d1%83%d1%8e\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eЭто самый важный шаг. Здесь я:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eвырезаю «воду»;\u003c/li\u003e\n\u003cli\u003eдобавляю реальные наблюдения;\u003c/li\u003e\n\u003cli\u003eуточняю формулировки, где можно понять двояко;\u003c/li\u003e\n\u003cli\u003eусиливаю практическую часть (чек-листы, сценарии, ограничения).\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eИменно здесь текст перестаёт быть «генерированным» и становится авторским.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"5-передаю-codex-техническое-оформление\"\u003e\n  5. Передаю Codex техническое оформление\n  \u003ca class=\"anchor\" href=\"#5-%d0%bf%d0%b5%d1%80%d0%b5%d0%b4%d0%b0%d1%8e-codex-%d1%82%d0%b5%d1%85%d0%bd%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%be%d0%b5-%d0%be%d1%84%d0%be%d1%80%d0%bc%d0%bb%d0%b5%d0%bd%d0%b8%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eПосле смысловой правки Codex приводит материал к правилам блога:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eкорректный фронтматтер;\u003c/li\u003e\n\u003cli\u003eосмысленные \u003ccode\u003esummary\u003c/code\u003e и \u003ccode\u003edescription\u003c/code\u003e без дублирования;\u003c/li\u003e\n\u003cli\u003eрелевантные теги;\u003c/li\u003e\n\u003cli\u003eструктура с оглавлением и читабельными секциями;\u003c/li\u003e\n\u003cli\u003eуместные эмодзи для акцентов.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 class=\"heading\" id=\"6-финальная-техническая-валидация\"\u003e\n  6. Финальная техническая валидация\n  \u003ca class=\"anchor\" href=\"#6-%d1%84%d0%b8%d0%bd%d0%b0%d0%bb%d1%8c%d0%bd%d0%b0%d1%8f-%d1%82%d0%b5%d1%85%d0%bd%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b0%d1%8f-%d0%b2%d0%b0%d0%bb%d0%b8%d0%b4%d0%b0%d1%86%d0%b8%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eПеред публикацией использую обязательный минимум:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo list future\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo --minify\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eЕсли пост попал в \u003ccode\u003efuture\u003c/code\u003e, дата сдвигается назад (МСК, \u003ccode\u003e+03:00\u003c/code\u003e), затем повторная проверка.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"7-публикация\"\u003e\n  7. Публикация\n  \u003ca class=\"anchor\" href=\"#7-%d0%bf%d1%83%d0%b1%d0%bb%d0%b8%d0%ba%d0%b0%d1%86%d0%b8%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eПосле проверки Codex помогает зафиксировать изменения:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit add .\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit commit -m \u003cspan class=\"s2\"\u003e\u0026#34;Осмысленный коммит на русском по теме поста\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egit push\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eНа этом этапе уже не нужно держать в голове «не забыть бы пуш» или «как назвать коммит».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-реально-улучшилось-после-внедрения\"\u003e\n  Что реально улучшилось после внедрения\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d1%80%d0%b5%d0%b0%d0%bb%d1%8c%d0%bd%d0%be-%d1%83%d0%bb%d1%83%d1%87%d1%88%d0%b8%d0%bb%d0%be%d1%81%d1%8c-%d0%bf%d0%be%d1%81%d0%bb%d0%b5-%d0%b2%d0%bd%d0%b5%d0%b4%d1%80%d0%b5%d0%bd%d0%b8%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЯ получил не «магическое ускорение», а более важную вещь: предсказуемость процесса.\u003c/p\u003e\n\u003cp\u003eПрактические эффекты:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eменьше технических ошибок во фронтматтере;\u003c/li\u003e\n\u003cli\u003eстабильнее качество мета-описаний;\u003c/li\u003e\n\u003cli\u003eниже время между идеей и публикацией;\u003c/li\u003e\n\u003cli\u003eменьше усталости от повторяющихся операций.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eГлавный результат: я пишу чаще и ровнее по качеству, потому что внимание уходит в содержание, а не в микрорутину.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"типовые-ошибки-при-работе-с-codex-и-как-их-избежать\"\u003e\n  Типовые ошибки при работе с Codex (и как их избежать)\n  \u003ca class=\"anchor\" href=\"#%d1%82%d0%b8%d0%bf%d0%be%d0%b2%d1%8b%d0%b5-%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b8-%d0%bf%d1%80%d0%b8-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b5-%d1%81-codex-%d0%b8-%d0%ba%d0%b0%d0%ba-%d0%b8%d1%85-%d0%b8%d0%b7%d0%b1%d0%b5%d0%b6%d0%b0%d1%82%d1%8c\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003ch3 class=\"heading\" id=\"ошибка-1-просить-сразу-финальный-пост\"\u003e\n  Ошибка 1: просить «сразу финальный пост»\n  \u003ca class=\"anchor\" href=\"#%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b0-1-%d0%bf%d1%80%d0%be%d1%81%d0%b8%d1%82%d1%8c-%d1%81%d1%80%d0%b0%d0%b7%d1%83-%d1%84%d0%b8%d0%bd%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%b9-%d0%bf%d0%be%d1%81%d1%82\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eТак почти всегда получается текст без авторской позиции. Лучше идти через этапы: каркас → черновик → ручная редактура → техническая сборка.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"ошибка-2-не-задавать-ограничения\"\u003e\n  Ошибка 2: не задавать ограничения\n  \u003ca class=\"anchor\" href=\"#%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b0-2-%d0%bd%d0%b5-%d0%b7%d0%b0%d0%b4%d0%b0%d0%b2%d0%b0%d1%82%d1%8c-%d0%be%d0%b3%d1%80%d0%b0%d0%bd%d0%b8%d1%87%d0%b5%d0%bd%d0%b8%d1%8f\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eЕсли не проговорить глубину, аудиторию и формат, модель заполняет пробелы усреднёнными формулировками. Чем точнее входные параметры, тем полезнее выход.\u003c/p\u003e\n\u003ch3 class=\"heading\" id=\"ошибка-3-пропускать-финальный-ручной-проход\"\u003e\n  Ошибка 3: пропускать финальный ручной проход\n  \u003ca class=\"anchor\" href=\"#%d0%be%d1%88%d0%b8%d0%b1%d0%ba%d0%b0-3-%d0%bf%d1%80%d0%be%d0%bf%d1%83%d1%81%d0%ba%d0%b0%d1%82%d1%8c-%d1%84%d0%b8%d0%bd%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%b9-%d1%80%d1%83%d1%87%d0%bd%d0%be%d0%b9-%d0%bf%d1%80%d0%be%d1%85%d0%be%d0%b4\"\u003e#\u003c/a\u003e\n\u003c/h3\u003e\n\u003cp\u003eБез авторской правки текст может быть «правильным», но без нервов и фактуры. Читатель это считывает мгновенно.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"мини-шаблон-запроса-который-у-меня-работает\"\u003e\n  Мини-шаблон запроса, который у меня работает\n  \u003ca class=\"anchor\" href=\"#%d0%bc%d0%b8%d0%bd%d0%b8-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd-%d0%b7%d0%b0%d0%bf%d1%80%d0%be%d1%81%d0%b0-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b9-%d1%83-%d0%bc%d0%b5%d0%bd%d1%8f-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d0%b5%d1%82\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЯ обычно даю Codex примерно такую рамку:\u003c/p\u003e\n\u003cdiv class=\"code-block\"\u003e\n  \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eНужен пост для Hugo-блога на русском.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eФормат: длинная статья, глубокая проработка, практические выводы.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eСтруктура: вступление, 6-8 разделов, финальный чек-лист.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eОбязательно: осмысленные summary/description, корректный фронтматтер, уместные эмодзи.\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eПосле текста: проверить техническую часть публикации (hugo list future, hugo --minify, commit, push).\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n  \u003cbutton class=\"copy-code-button\"\u003ecopy\u003c/button\u003e\n\u003c/div\u003e\n\u003cp\u003eЭтого достаточно, чтобы стартовый результат был рабочим, а не «общей заготовкой».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"когда-codex-точно-не-помогает\"\u003e\n  Когда Codex точно не помогает\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-codex-%d1%82%d0%be%d1%87%d0%bd%d0%be-%d0%bd%d0%b5-%d0%bf%d0%be%d0%bc%d0%be%d0%b3%d0%b0%d0%b5%d1%82\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПосле пары месяцев работы в этом режиме можно честно перечислить ситуации, где автоматизация скорее мешает:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eКогда тема ещё не оформилась.\u003c/strong\u003e Если у вас в голове только смутное «хочется написать про X», Codex выдаст структуру вокруг этой смутности — формально стройную, по сути пустую. Лучше потратить полчаса на бумажный набросок, выписать 3-5 ключевых тезисов, и только потом включать инструмент. Код первым приходит туда, где есть запрос; в текстах этот запрос — авторская позиция.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eЛичные посты.\u003c/strong\u003e Эссе про опыт, наблюдение, ошибку — Codex здесь не нужен. Структуру эссе диктует не методическая разбивка на h2-разделы, а внутренняя логика рассказа. Прогон через шаблон «вступление → 6 разделов → чек-лист» убивает живость текста. Технические руководства — да; рефлексия — нет.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eКогда ответ нужно проверить руками.\u003c/strong\u003e Если вы пишете про инструмент, который сами не запускали (например, обзор библиотеки по чужим текстам), Codex усилит риски: соберёт правдоподобный текст с фактическими ошибками. Точность здесь обеспечивает только личное прохождение сценария, до буквы команд.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"что-меняется-в-процессе-через-3-6-месяцев\"\u003e\n  Что меняется в процессе через 3-6 месяцев\n  \u003ca class=\"anchor\" href=\"#%d1%87%d1%82%d0%be-%d0%bc%d0%b5%d0%bd%d1%8f%d0%b5%d1%82%d1%81%d1%8f-%d0%b2-%d0%bf%d1%80%d0%be%d1%86%d0%b5%d1%81%d1%81%d0%b5-%d1%87%d0%b5%d1%80%d0%b5%d0%b7-3-6-%d0%bc%d0%b5%d1%81%d1%8f%d1%86%d0%b5%d0%b2\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЛюбой workflow эволюционирует. Если поначалу Codex заметно ускоряет каждую публикацию, дальше эффект становится тише и переезжает в другую плоскость:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eВы начинаете писать чаще, потому что нет «трения старта».\u003c/li\u003e\n\u003cli\u003eКачество публикаций становится ровным — нет «слабых» постов из-за усталости от рутины.\u003c/li\u003e\n\u003cli\u003eПоявляется привычка фиксировать правила процесса (\u003ccode\u003eAGENTS.md\u003c/code\u003e, шаблоны, чек-листы) сразу, а не «когда-нибудь».\u003c/li\u003e\n\u003cli\u003eПерестаёте откладывать посты из-за мелких технических придирок (формат даты, длина summary, теги).\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЭто не «магия ИИ». Это просто разделение труда между «чем я уникально полезен» и «что можно поручить инструменту».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"практические-выводы\"\u003e\n  Практические выводы\n  \u003ca class=\"anchor\" href=\"#%d0%bf%d1%80%d0%b0%d0%ba%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b5-%d0%b2%d1%8b%d0%b2%d0%be%d0%b4%d1%8b\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЕсли коротко, рабочая модель такая:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eСмысл и позиция остаются у автора.\u003c/li\u003e\n\u003cli\u003eCodex берёт структуру и операционную рутину.\u003c/li\u003e\n\u003cli\u003eКачество растёт там, где есть дисциплина процесса, а не просто «сильный промпт». ✅\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eАвтоматизация в блоге полезна не потому, что «пишет быстрее», а потому что защищает внимание автора. А внимание, в долгую, важнее любой единичной публикации.\u003c/p\u003e\n","date_modified":"2026-02-02T12:00:00+03:00","date_published":"2026-02-02T12:00:00+03:00","id":"https://kushnaren.co/posts/pishem-posty-s-codex-avtomatizaciya-nashe-vse/","image":"https://kushnaren.co/og/pishem-posty-s-codex-avtomatizaciya-nashe-vse.png","summary":"Подробная схема работы с Codex для Hugo-блога: подготовка идеи, сборка черновика, редактура, техническая валидация, публикация и контроль качества.","tags":["codex","автоматизация","hugo"],"title":"Пишем посты с Codex: от идеи до публикации без лишней рутины ✍️","url":"https://kushnaren.co/posts/pishem-posty-s-codex-avtomatizaciya-nashe-vse/"},{"content_html":"\u003ch2 class=\"heading\" id=\"введение\"\u003e\n  Введение\n  \u003ca class=\"anchor\" href=\"#%d0%b2%d0%b2%d0%b5%d0%b4%d0%b5%d0%bd%d0%b8%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eТема здоровья легко превращается в гонку гаджетов, протоколов и баночек. Зашёл в любой телеграм-канал «про биохак», полистал — и через неделю на полке стоят семь новых БАДов, на руке трекер сна, на диване книга про митохондрии. А базовые вещи — режим сна, ежедневное движение, нормальный завтрак — продолжают проседать.\u003c/p\u003e\n\u003cp\u003eЯ хочу держать тему в более приземлённом поле: сначала базовые привычки, потом аккуратные эксперименты, и только затем — добавки, если есть для этого основания. Этот пост — не медицинская рекомендация и не «топ-10 must-have БАДов». Это попытка нарисовать карту, по которой можно ориентироваться, не теряя голову.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"база-без-которой-не-работает-ничего-\"\u003e\n  База, без которой не работает ничего 🛏\n  \u003ca class=\"anchor\" href=\"#%d0%b1%d0%b0%d0%b7%d0%b0-%d0%b1%d0%b5%d0%b7-%d0%ba%d0%be%d1%82%d0%be%d1%80%d0%be%d0%b9-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d0%b5%d1%82-%d0%bd%d0%b8%d1%87%d0%b5%d0%b3%d0%be-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eСамые надёжные улучшения лежат в простых вещах, и они банальны до зевоты:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eСон по расписанию.\u003c/strong\u003e Одинаковое время отбоя и подъёма (включая выходные), темнота в спальне, ограничение экранов за час до сна. Не «8 часов любой ценой», а ровные 7-8 часов в одни и те же часы.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eДвижение каждый день.\u003c/strong\u003e Прогулки, силовые, растяжка — не важно что именно, важно регулярно. Дневная норма шагов даже без зала уже даёт большой кусок пользы.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eРацион с адекватным белком, клетчаткой и достаточной калорийностью.\u003c/strong\u003e Не «суперфуды», не «чистое питание» в религиозном смысле, а понятный рацион без хронического дефицита.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eУправление стрессом.\u003c/strong\u003e Простые ритуалы (вечерняя прогулка, регулярные паузы), ограничение перегруза, цифровая гигиена. Не «медитация 1 час в день», а «не работать вечером».\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЕсли эти пункты не закрыты, «биохакинг» превращается в красивую упаковку поверх системных дыр. Купить адаптоген, чтобы компенсировать хронический недосып — это не про здоровье, это про прокрастинацию здоровья.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"биохакинг-как-дисциплина-а-не-как-шоу\"\u003e\n  Биохакинг как дисциплина, а не как шоу\n  \u003ca class=\"anchor\" href=\"#%d0%b1%d0%b8%d0%be%d1%85%d0%b0%d0%ba%d0%b8%d0%bd%d0%b3-%d0%ba%d0%b0%d0%ba-%d0%b4%d0%b8%d1%81%d1%86%d0%b8%d0%bf%d0%bb%d0%b8%d0%bd%d0%b0-%d0%b0-%d0%bd%d0%b5-%d0%ba%d0%b0%d0%ba-%d1%88%d0%be%d1%83\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eДля меня биохакинг — это не про сверхчеловеческие цели типа «жить до 120 лет», а про наблюдение и микро-настройку под собственные особенности. Три принципа, без которых тема превращается в магазин:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eИзмерять только то, что будет влиять на решения.\u003c/strong\u003e Если данные не меняют поведение, это не данные, это шум.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eМенять по одному фактору за раз.\u003c/strong\u003e Иначе невозможно понять, что именно сработало (и сработало ли). Ввели магний, омегу и витамин D одновременно — улучшение есть, но причина неизвестна.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eДержать горизонт не в днях, а в месяцах.\u003c/strong\u003e Большинство значимых изменений в самочувствии копится медленно. «Через неделю стало лучше» — обычно эффект новизны, а не реальный сдвиг.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eЭти три правила сами по себе отсекают 80% эзотерических протоколов из соцсетей. Если автор предлагает «попробуй и сразу почувствуешь разницу» — он не про дисциплину, он про маркетинг.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"анализы-и-данные-когда-они-действительно-нужны-\"\u003e\n  Анализы и данные: когда они действительно нужны 📊\n  \u003ca class=\"anchor\" href=\"#%d0%b0%d0%bd%d0%b0%d0%bb%d0%b8%d0%b7%d1%8b-%d0%b8-%d0%b4%d0%b0%d0%bd%d0%bd%d1%8b%d0%b5-%d0%ba%d0%be%d0%b3%d0%b4%d0%b0-%d0%be%d0%bd%d0%b8-%d0%b4%d0%b5%d0%b9%d1%81%d1%82%d0%b2%d0%b8%d1%82%d0%b5%d0%bb%d1%8c%d0%bd%d0%be-%d0%bd%d1%83%d0%b6%d0%bd%d1%8b-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eСлепая сдача десятков анализов «потому что в соседнем чате посоветовали» редко помогает. Полезнее идти от вопросов:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eЧто именно я хочу улучшить или понять?\u003c/li\u003e\n\u003cli\u003eЕсть ли симптомы или жалобы?\u003c/li\u003e\n\u003cli\u003eЧто из результатов повлияет на конкретные действия?\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЕсли ответов нет — анализы становятся способом успокоиться, а не инструментом управления.\u003c/p\u003e\n\u003cp\u003eНесколько маркеров, которые часто имеют смысл проверять (без претензии на исчерпывающий список):\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eВитамин D (25-OH).\u003c/strong\u003e В умеренных широтах хронический дефицит у большинства, особенно зимой.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eФерритин.\u003c/strong\u003e Маркер запасов железа; низкий часто объясняет «беспричинную» усталость.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eB12 и фолат.\u003c/strong\u003e Особенно важно для тех, кто ограничивает мясо.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eМагний.\u003c/strong\u003e Анализ малоинформативен (магний в основном внутри клеток), но если есть симптомы (судороги, аритмии) — стоит обсудить с врачом.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eТТГ + Т4 свободный.\u003c/strong\u003e Базовая щитовидка, влияет на энергию, вес, настроение.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЭто не «обязательная панель», это набор «если есть жалобы — есть смысл начать отсюда».\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"бады-инструмент-а-не-спасение\"\u003e\n  БАДы: инструмент, а не спасение\n  \u003ca class=\"anchor\" href=\"#%d0%b1%d0%b0%d0%b4%d1%8b-%d0%b8%d0%bd%d1%81%d1%82%d1%80%d1%83%d0%bc%d0%b5%d0%bd%d1%82-%d0%b0-%d0%bd%d0%b5-%d1%81%d0%bf%d0%b0%d1%81%d0%b5%d0%bd%d0%b8%d0%b5\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eДобавки могут быть уместны, когда есть \u003cstrong\u003eдефицит\u003c/strong\u003e, \u003cstrong\u003eособенности питания\u003c/strong\u003e или \u003cstrong\u003eмедицинские показания\u003c/strong\u003e. В большинстве случаев они не перекрывают плохой сон, хаотичное питание и хронический стресс.\u003c/p\u003e\n\u003cp\u003eПринципы, которых я стараюсь придерживаться:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eСначала проверка базовых привычек.\u003c/li\u003e\n\u003cli\u003eЗатем чёткая цель и ожидаемый эффект.\u003c/li\u003e\n\u003cli\u003eИ только после этого — минимальная дозировка и наблюдение за эффектом.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eЕсли БАД нельзя описать в формате «принимаю X для Y, эффект жду через Z недель» — лучше не принимать. Это правило отсеивает «модные новинки», которые завозят все блогеры разом.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"как-выбирать-добавки-разумно\"\u003e\n  Как выбирать добавки разумно\n  \u003ca class=\"anchor\" href=\"#%d0%ba%d0%b0%d0%ba-%d0%b2%d1%8b%d0%b1%d0%b8%d1%80%d0%b0%d1%82%d1%8c-%d0%b4%d0%be%d0%b1%d0%b0%d0%b2%d0%ba%d0%b8-%d1%80%d0%b0%d0%b7%d1%83%d0%bc%d0%bd%d0%be\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eПростая схема, которая работает:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eОпределить цель.\u003c/strong\u003e Сон, энергия, восстановление, дефицит по анализу. Что-то одно, не «всё сразу».\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eПроверить безопасность и совместимость.\u003c/strong\u003e С текущими лекарствами, состояниями, образом жизни. Это отдельная работа, не пропускать.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eНачать с одной добавки и фиксировать изменения.\u003c/strong\u003e Дневник в три строки в день: сон, энергия, настроение. Этого достаточно.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eОтменить, если эффекта нет или он сомнителен.\u003c/strong\u003e Через 4-8 недель, не раньше.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eИ да: это не медицинская рекомендация. При сомнениях, симптомах или сложных состояниях — обсуждать с врачом, не с интернетом.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"типичные-ловушки-\"\u003e\n  Типичные ловушки 🪤\n  \u003ca class=\"anchor\" href=\"#%d1%82%d0%b8%d0%bf%d0%b8%d1%87%d0%bd%d1%8b%d0%b5-%d0%bb%d0%be%d0%b2%d1%83%d1%88%d0%ba%d0%b8-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eСамые частые ошибки, в которые попадаются почти все, кто заходит в тему:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eПокупать всё сразу.\u003c/strong\u003e Под влиянием статьи или подкаста заказывают 5-7 добавок. В итоге не понимают, что работает, а что нет.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eНадеяться на «волшебную таблетку».\u003c/strong\u003e Если есть проблема со сном — никакая добавка не починит то, что чинится за неделю гигиены сна.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eЗабывать про доказательную базу.\u003c/strong\u003e Многие популярные добавки имеют слабые или противоречивые исследования. «Все вокруг пьют» — не критерий.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eИгнорировать побочки.\u003c/strong\u003e Любой БАД может давать эффект, который вы спишете на «обычный день». Дневник помогает поймать.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eДоверять ярким инфлюенсерам.\u003c/strong\u003e Биохакинг — благодатная почва для красивых историй и плохих советов. Чем громче «у меня всё работает», тем выше вероятность маркетинга или selection bias.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"минимальный-подход\"\u003e\n  Минимальный подход\n  \u003ca class=\"anchor\" href=\"#%d0%bc%d0%b8%d0%bd%d0%b8%d0%bc%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%b9-%d0%bf%d0%be%d0%b4%d1%85%d0%be%d0%b4\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЯ склоняюсь к минимальному набору: меньше банок, больше дисциплины. Часто достаточно навести порядок в рутине, чтобы необходимость в добавках исчезла или сильно сократилась.\u003c/p\u003e\n\u003cp\u003eЕсли совсем коротко: спать в одно время, гулять каждый день, есть не «правильно», а сбалансированно, и не пить больше двух-трёх БАДов одновременно. Этого хватит большинству людей в большинстве периодов жизни.\u003c/p\u003e\n\u003ch2 class=\"heading\" id=\"итоги-\"\u003e\n  Итоги 🧠\n  \u003ca class=\"anchor\" href=\"#%d0%b8%d1%82%d0%be%d0%b3%d0%b8-\"\u003e#\u003c/a\u003e\n\u003c/h2\u003e\n\u003cp\u003eЗдоровье — это не витрина из биохакинга, а устойчивость, которая складывается из маленьких повторяющихся решений. Добавки могут быть частью этой системы, но только после того, как база отстроена и цели ясны.\u003c/p\u003e\n\u003cp\u003eБиохакинг как идея полезен ровно до тех пор, пока он остаётся \u003cstrong\u003eдисциплиной наблюдения\u003c/strong\u003e. Как только он превращается в магазин баночек или гонку метрик — он перестаёт работать на здоровье и начинает работать на чувство «я делаю что-то важное», что сильно дешевле и проще, чем реально менять привычки.\u003c/p\u003e\n\u003cp\u003eСамый честный «биохакинг» — это лечь спать на час раньше. Бесплатно, без побочек, эффект через неделю.\u003c/p\u003e\n","date_modified":"2026-01-19T10:17:04+03:00","date_published":"2026-01-19T10:17:04+03:00","id":"https://kushnaren.co/posts/zdorove-biohaking-bady/","image":"https://kushnaren.co/og/zdorove-biohaking-bady.png","summary":"Без культа, без магии, без обещаний: разбор базовых привычек, роли анализов, принципов выбора БАДов и типичных ловушек, в которые попадает любой, кто впервые погружается в тему биохакинга.","tags":["здоровье","биохакинг","бад","привычки"],"title":"Здоровье, биохакинг и БАДы: как не потерять голову 💊","url":"https://kushnaren.co/posts/zdorove-biohaking-bady/"}],"language":"ru-RU","title":"Evgeny Kushnarenko","version":"https://jsonfeed.org/version/1.1"}