Пайплайн автопубликации статей
Введение
Одна из главных проблем при ведении технического блога — это трение между процессом написания и процессом публикации. Обычно это выглядит так: написал в Markdown → сконвертировал → скопировал в CMS → вставил → отформатировал → опубликовал. На каждую статью уходит 5-10 минут «технической работы», которая отвлекает от главного — контента. Мы решили эту проблему кардинально: статья появляется в блоге через 2 секунды после сохранения файла в Obsidian. В этой статье я расскажу, как устроен этот пайплайн, какие технологии используются и как вы можете повторить нечто подобное для своих проектов.
Как это работает: общая схема
Пайплайн состоит из следующих основных компонентов:
graph LR
A[Obsidian] -->|fswatch| B[Git]
B -->|push| C[GitHub]
C -->|webhook| D[VPS]
D -->|docker| E[Django]
E -->|render| F[Blog]
Obsidian → fswatch → Git → GitHub → Webhook → VPS → Django → Блог
Давайте разберём каждый шаг.
1. Локальная часть: Obsidian + fswatch + Git
На моём Mac запущен процесс fswatch, который мониторит папку articles/ в хранилище Obsidian. Как только файл создан, изменён или удалён, происходит следующее:
fswat
```ch -o uch-docs/blog/articles/ | while read f; do
git add uch-docs/blog/articles/
git commit -m "pub: новая статья $(date '+%H:%M:%S')"
git push origin main
done
Важные детали:
- Мониторинг рекурсивный (отслеживаются изменения во всех вложенных папках)
- Коммит делается с временной меткой для удобства отладки
- Пуш происходит автоматически в ветку
main
2. Промежуточное звено: GitHub
Репозиторий my-digital-garden-content выступает как прозрачный брокер. Он:
- Хранит все статьи в исходном Markdown
- Принимает push от локального
fswatch - Отправляет webhook на VPS
Webhook настроен на событие push и указывает на эндпоинт http://109.196.98.128/webhook.
3. Серверная часть: VPS + Webhook + Docker
На VPS крутится простой Python-сервер, который слушает входящие webhook'и:
class WebhookHandler(BaseHTTPRequestHandler):
def do_POST(self):
if self.path == "/webhook":
os.chdir("/opt/uch/blog-content-git")
subprocess.run(["git", "pull"])
subprocess.run(["docker", "exec", "uch-blog", "python", "manage.py",
"import_obsidian", "/opt/uch/blog-content-git/uch-docs/blog/articles",
"--author=admin", "--update-existing"])
# ... очистка удалённых статей
Что здесь происходит:
- Получен webhook →
git pullв папку с репозиторием - Запуск Django-команды
import_obsidianвнутри Docker-контейнера - Автоматическая очистка статей, которые были удалены из файловой системы
4. Бэкенд: Django + импортер
Сердце пайплайна — кастомная Django-команда import_obsidian. Она:
- Читает все
.mdфайлы из указанной директории - Парсит frontmatter (YAML-шапку файла)
- Создаёт или обновляет статьи в базе данных
- Генерирует slug из заголовка (с поддержкой кириллицы)
- Устанавливает статус
published
Ключевая логика импортёра
def safe_slugify(value):
"""Транслитерация кириллицы в латиницу для URL"""
# 'Начало блога' → 'nachalo-bloga'
...
Особенности реализации
Поддержка кириллицы в URL
Одна из первых проблем, с которой мы столкнулись — Django плохо дружит с кириллицей в слагах. Решение: транслитерация прямо внутри импортёра.
def safe_slugify(value):
mapping = {
'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd',
'е': 'e', 'ё': 'e', 'ж': 'zh', 'з': 'z', 'и': 'i',
# ... полное соответствие
}
result = ''.join(mapping.get(c, c) for c in value.lower())
return re.sub(r'[^a-z0-9]+', '-', result).strip('-')
Автоматическая очистка
Если файл статьи удалён из Obsidian — он должен исчезнуть из блога. Это реализовано через проверку существования файла для каждой записи в БД.
Тактический дизайн
Статьи оформлены в тактическом стиле:
- Тёмный фон с сеткой в перспективе
- Зелёные акценты (
#00ff9d) - Анимированные звёзды на фоне (не отвлекают от чтения)
- Моноширинный терминальный стиль для мета-информации
Что дальше?
Пайплайн работает стабильно, но планы по развитию:
- Поддержка изображений (копирование медиа на VPS)
- Встраивание графов знаний в статьи
- Сниппеты с результатами выполнения кода
- Автоматическое связывание статей через теги
Заключение
Пайплайн автопубликации — это пример того, как правильная архитектура может свести к нулю операционные издержки. Теперь я могу полностью сосредоточиться на содержании, а не на публикации.
Ключевые выводы:
fswatch+ Git — отличная пара для автоматизации на локальной машине- Webhook + Docker — гибкое решение для серверной части
- Кастомный импортёр даёт полный контроль над логикой
- Транслитерация решает проблему кириллицы в URL
Весь код открыт и доступен в репозитории. Используйте, форкайте, улучшайте.
Создано в Obsidian Digital Garden, импортировано в UCH Blog за 2 секунды