Богатые Ссылки для Ленивых Разработчиков
Когда я делюсь ссылкой на блог в Twitter или Slack, она отображается как обычный текст. Без изображения предварительного просмотра. Просто URL вроде granda.org/en/2026/01/02/claude-code-on-the-go/.
Мне нужны были изображения Open Graph. Стандартный подход: вручную создавать изображение 1200x630 для каждого поста. Это утомительно. Я попросил Claude автоматизировать это.
flowchart LR
Push[git push] --> GHA[GitHub Actions]
GHA --> Hugo[Hugo Server]
GHA --> PW[Playwright]
PW -->|screenshot| Hugo
PW --> IMG[OG Image]
IMG --> Commit[git commit]
Commit --> Deploy[Deploy]
flowchart TB
Push[git push] --> GHA[GitHub Actions]
GHA --> Hugo[Hugo Server]
GHA --> PW[Playwright]
PW -->|screenshot| Hugo
PW --> IMG[OG Image]
IMG --> Commit[git commit]
Commit --> Deploy[Deploy]
Настройка
Я объяснил Claude проблему: постам нужны изображения для социальных сетей, но я не хочу создавать их вручную. Сделать скриншот содержимого статьи, сохранить его как OG-изображение, автоматически обновить метаданные.
Что Построил Claude
| Компонент | Назначение |
|---|---|
generate-og-image.js | Скрипт Playwright для создания скриншотов постов |
generate-og-images.yml | GitHub Action, запускаемый при изменениях постов |
Изменения в baseof.html | Условные мета-теги og:image |
Скрипт Playwright запускает Chrome без интерфейса, переходит к посту, принудительно включает светлый режим, скрывает шапку и подвал, и делает скриншот размером 1200x630:
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1200, height: 630 },
deviceScaleFactor: 2, // Retina for crisp text
});
// Force light mode, hide nav
await page.evaluate(() => {
document.documentElement.setAttribute("data-theme", "light");
document.querySelector("header").style.display = "none";
document.querySelector("footer").style.display = "none";
});
await page.screenshot({ path: outputPath, type: "png" });
GitHub Action запускается при любом изменении файлов .en.md, запускает Hugo, выполняет скрипт и фиксирует сгенерированное изображение обратно в репозиторий:
on:
push:
branches: [main]
paths:
- "content/posts/**/*.md"
steps:
- name: Start Hugo server
run: hugo server --bind 0.0.0.0 --port 1313 &
- name: Generate OG images
run: node scripts/generate-og-image.js "$file"
- name: Commit changes
run: |
git add static/images/posts/ content/posts/
git commit -m "Generate Open Graph images for blog posts"
git push
Шаблон условно включает изображение, когда в метаданных есть поле image:
{{- if .Params.image -}}
<meta property="og:image" content="{{ $canonical }}{{ .Params.image }}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="{{ $canonical }}{{ .Params.image }}">
{{- else -}}
<meta name="twitter:card" content="summary">
{{- end -}}
404: Slug Не Найден
Первый запуск. Playwright сделал скриншот пустой страницы. URL вернул 404.
Скрипт построил URL из имени файла: automatic-blog-translations.en.md → /en/2025/12/23/automatic-blog-translations/
Но Hugo не использует имя файла для slug. Он использует заголовок. Фактический URL был /en/2025/12/23/automatic-blog-translations-with-claude-and-github-actions/.
Я мог бы переимплементировать алгоритм slugify Hugo в JavaScript. Вместо этого: спросить Hugo.
const output = execSync("hugo list all", { encoding: "utf8" });
// CSV output includes the exact permalink Hugo generates
Одна строка. Никаких граничных случаев. Hugo уже знает свои собственные URL.
Что Дальше
Текущая реализация генерирует изображения только для английских постов. Переведенные версии ссылаются на тот же путь к изображению, что работает, но это означает, что OG-изображение всегда показывает английский текст, даже при публикации испанской или японской версии.
Настоящая поддержка i18n появится в следующем PR. Пока что каждый пост получает социальное изображение, что лучше, чем ничего.
Вывод
Это заняло 14 минут на создание и отладку. Теперь каждый пост автоматически получает изображение предварительного просмотра для социальных сетей. Никакой ручной работы на пост. Автоматизация окупается после нескольких постов.
Выпускайте рабочую версию, находите пробелы, итерируйте.
Связанные посты:
- Автоматические Переводы Блога с Claude и GitHub Actions - тот же стек, другая автоматизация
- Мой QA-Инженер - это LLM - позволяем Claude проверять код, который он пишет
- Создание Собственной Рассылки с Claude - ещё одна 30-минутная сборка