UCH Tactical Blog

tactical v1.0
UPDATED UPDATED

Пайплайн автопубликации статей

SYS://TIMESTAMP UTC+3
USER://admin
CHANNEL://Блог, Аналитика, Лайфстайл
STATUS://OPERATIONAL

Пайплайн автопубликации статей

Введение

Одна из главных проблем при ведении технического блога — это трение между процессом написания и процессом публикации. Обычно это выглядит так: написал в 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"])
            # ... очистка удалённых статей

Что здесь происходит:

  1. Получен webhook → git pull в папку с репозиторием
  2. Запуск Django-команды import_obsidian внутри Docker-контейнера
  3. Автоматическая очистка статей, которые были удалены из файловой системы

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)
  • Встраивание графов знаний в статьи
  • Сниппеты с результатами выполнения кода
  • Автоматическое связывание статей через теги

Заключение

Пайплайн автопубликации — это пример того, как правильная архитектура может свести к нулю операционные издержки. Теперь я могу полностью сосредоточиться на содержании, а не на публикации.

Ключевые выводы:

  1. fswatch + Git — отличная пара для автоматизации на локальной машине
  2. Webhook + Docker — гибкое решение для серверной части
  3. Кастомный импортёр даёт полный контроль над логикой
  4. Транслитерация решает проблему кириллицы в URL

Весь код открыт и доступен в репозитории. Используйте, форкайте, улучшайте.


Создано в Obsidian Digital Garden, импортировано в UCH Blog за 2 секунды

← RETURN TO BASE