Итог статьи 3 - и первые сомнения#
В третьей статье цикла я объединил два репозитория - prohomelab-content и prohomelab-site - в один репозиторий prohomelab-pages-new, поставил self-hosted GitHub Actions раннер прямо на LXC, и в итоге весь цикл - от заметки в Obsidian до опубликованной статьи - заработал полностью автоматически. На бумаге это было улучшением. Вроде бы. Один репозиторий вместо двух, никакого workflow_dispatch-моста между ними.
На практике за один вечер я получил: self-hosted раннер, который отказывается стартовать от root; путаницу между встроенным Jekyll-автосборщиком GitHub и своим workflow; проблему в самом Hugo с полем published; и - самое неприятное - NTFS junction, который Obsidian просто не показывает, из-за чего на несколько минут мне показалось, что пропали все статьи. Ничего не потерялось безвозвратно (git-история никуда не девается), но эмоционально это была самая нервная часть всего цикла.
Когда все заработало, я честно спросил себя: а стоило ли оно того? Сравнил оба варианта по пунктам:
| Вариант 3 (статья): self-hosted GH Actions, один репозиторий | Вариант 2 (статьи 1-2): два репозитория на Forgejo | |
|---|---|---|
| Репозитории | 1 (prohomelab-pages-new) | 2 (prohomelab-content, prohomelab-site) |
| Кто собирает | self-hosted раннер на LXC, зарегистрированный в GitHub | forgejo-runner на том же LXC, другой процесс |
| Безопасность раннера | риск через PR у публичных репо (у меня нет pull_request-триггера, но все-таки) | Forgejo приватный - риска с публичными форками нет |
| Obsidian | sparse-checkout, лишний уровень вложенности content/posts | обычный клон, плоская структура, без вложенности |
| Проверено в бою | да, но всего один вечер | да, это ровно то, что уже стабильно работало раньше |
| Хрупкие места | PAT-токены на Windows, systemd-сервис раннера, TOML руками | workflow_dispatch между репозиториями, git push из CI в GitHub Pages |
Вывод получился не в пользу нового варианта. Он дал заметно скромный выигрыш (один репозиторий вместо двух) ценой ощутимо большего числа мест, которые могут сломаться. Старая схема надежнее.
Возвращаем контент в Forgejo#
Первая проблема отката - за время эксперимента я продолжал писать в Obsidian, и весь новый контент (правки в доброй паре десятков статей, черновики «Docker vs Podman vs Kubernetes», «Quadlets vs Podlets», вторая статья цикла, исправленная дата в посте про Arcane) существовал только в prohomelab-pages-new на GitHub - в prohomelab-content на Forgejo его не было.
Решение - обычный rsync без флага --delete, чтобы гарантированно ничего не стереть, только добавить:
cd /home/prohomelab
git pull github main
cd /home
git clone https://forgejo.stilicho.ru/stilicho/prohomelab-content.git prohomelab-content-sync
rsync -av /home/prohomelab/content/posts/ /home/prohomelab-content-sync/
cd /home/prohomelab-content-sync
git statusgit status перед коммитом - обязательный шаг, а не формальность. В моем случае он показал не только ожидаемые правки, но и пару неожиданностей - задвоенные файлы с суффиксом " 1" (похоже на типичное поведение Obsidian при конфликте одной и той же заметки в двух местах) и лишнюю вложенную папку. Ничего критичного, но все-таки лучше увидеть это до коммита, чем после.
Раннер и деплой - проблема в старом workflow#
Включил forgejo-runner обратно (systemctl enable/start) - и он тут же сработал сам, по старому межрепозиторному триггеру, который все это время оставался живым на стороне Forgejo.
Дальше начал править .forgejo/workflows/deploy.yml, готовясь заменить FTP-деплой на git push - и обнаружил, что деплой уже был переделан на git push в GitHub Pages еще раньше, до всей этой истории с третьей статьей. Просто указывал на prohomelab-pages (без -new) - тот самый репозиторий, который я удалил в самом начале эксперимента с self-hosted раннером. Отсюда и была самая первая ошибка сегодняшнего дня: repository 'prohomelab-pages.git' not found.
Смержил обе версии workflow (у меня в локальной копии была свежая с git push, на Forgejo - старая, тоже с git push, но в другой репозиторий и с деплоем в main, а не в отдельную ветку). Итоговый шаг деплоя:
- name: Deploy to GitHub Pages
env:
GH_PAGES_TOKEN: ${{ secrets.GH_PAGES_TOKEN }}
run: |
cd public
git init -q
git checkout -q -b gh-pages
git -c user.name="prohomelab-bot" -c user.email="bot@prohomelab.com" add -A
git -c user.name="prohomelab-bot" -c user.email="bot@prohomelab.com" commit -q -m "Deploy $(date -u +%Y-%m-%dT%H:%M:%SZ)"
git push --force "https://stilicho2011:${GH_PAGES_TOKEN}@github.com/stilicho2011/prohomelab-pages-new.git" gh-pages:gh-pagesОдин нюанс, который я не сразу заметил. Шаг сборки в моем собственном примере выше - hugo -D --minify. Флаг -D («build drafts») заставляет Hugo включать в сборку все статьи с draft: true, независимо от buildDrafts = false в конфиге - флаг командной строки перебивает настройку конфига. То есть с этим флагом любой черновик, лежащий в репозитории, тут же публикуется на живом сайте. Убрал -D из реального workflow, как только заметил:
sed -i 's/hugo -D --minify/hugo --minify/' .forgejo/workflows/deploy.ymlМелочь, но именно из таких мелочей и складывается ощущение «вроде все работает, но как-то не так» - сайт собирался и деплоился без единой ошибки, просто заодно публиковал то, что публиковаться не должно.
Ключевое решение - деплоить именно в отдельную ветку gh-pages, не в main: в main prohomelab-pages-new теперь лежат исходники Hugo (тема, конфиг), а не собранный сайт. Затереть их собранным HTML было бы шагом назад.
Секрет GH_PAGES_TOKEN пришлось пересоздать - старый был выдан под удаленный репозиторий и не годился. В Forgejo секреты нельзя отредактировать на месте, только удалить и создать заново с тем же именем.
GitHub Pages. Снова путаница с Source#
После первого успешного деплоя зашел в Settings → Pages - и увидел то же самое «currently being built from the main branch», что и в начале истории с self-hosted раннером. Выпадающий список веток при сохранении почему-то не подхватил gh-pages, оставшись на дефолтной main. Пришлось выбрать явно. Мелочь, но второй раз за один цикл статей наступить на одни и те же грабли с source-настройками - забавное совпадение.
Obsidian. Назад к плоской структуре#
Финальный шаг - вернуть Obsidian к обычному клону prohomelab-content, без sparse-checkout и без вложенности:
Rename-Item Prohomelab Prohomelab-github-experiment-backup
git clone https://forgejo.stilicho.ru/stilicho/prohomelab-content.git ProhomelabТут я поймал еще один мелкий, но раздражающий баг - Windows Credential Manager закэшировал refresh_token для forgejo.stilicho.ru, который к этому моменту протух, и Git тихо подставлял его вместо того, чтобы спросить заново. Симптом - Authentication failed, при том что сам токен для ручного использования был рабочим. Вылечил точечным удалением конкретной записи:
cmdkey /list | Select-String "forgejo"
cmdkey /delete:LegacyGeneric:target=git:https://refresh_token.forgejo.stilicho.ruПосле этого клонирование прошло с первого раза, структура вернулась ровно такой, какой была до всех экспериментов - категории статей прямо в корне Prohomelab, без content/posts.
Итоговая схема#
Obsidian (Windows) → push
│
▼
Forgejo: prohomelab-content
│ workflow_dispatch (notify.yml)
▼
Forgejo: prohomelab-site
│ forgejo-runner (LXC)
│ checkout → sync-content.sh → hugo --minify → git push (gh-pages)
▼
GitHub: prohomelab-pages-new, ветка gh-pages
│ Pages Source: Deploy from a branch
▼
prohomelab.comВнешне это почти то же самое, с чем цикл начинался в первой и второй статьях - только вместо FTP на smartape деплой идет git push собранного сайта в отдельную ветку GitHub-репозитория. Self-hosted раннер, объединенный репозиторий и sparse-checkout из статьи 3 оставлены как пройденный, задокументированный, но не выбранный для продакшена путь.
Стоило ли вообще пробовать#
Ну скорее да, чем нет - при том что закончилось все откатом. Телодвижения, описанные в статье 3, не были ошибкой. Без них я бы не узнал, что self-hosted раннер требует непривилегированного пользователя, что Obsidian не переваривает NTFS junction, и что published в Hugo - заминированное поле. Эти находки остаются полезными сами по себе, независимо от того, какая архитектура в итоге прижилась. Ну то есть знания точно обогатил. Но как постоянная схема для личного блога - два скучных репозитория на Forgejo с проверенным git push оказались правильнее одного модного self-hosted GitHub Actions. По крайней мере для меня это оказался в итоге самый оптимальный вариант.




