Богатые Ссылки для Ленивых Разработчиков

Когда я делюсь ссылкой на блог в 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.ymlGitHub 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 минут на создание и отладку. Теперь каждый пост автоматически получает изображение предварительного просмотра для социальных сетей. Никакой ручной работы на пост. Автоматизация окупается после нескольких постов.

Выпускайте рабочую версию, находите пробелы, итерируйте.


Связанные посты: