Перейти к содержимому
Все руководства
Практическое руководство45 мин чтения

Как автоматизировать скриншоты App Store в CI/CD

Практический сквозной конвейер для генерации скриншотов App Store и Google Play из вашей CI-системы с использованием Fastlane, GitHub Actions и Bitrise — с конкретными конфигурациями, которые можно вставить в ваш репозиторий.

Eric Isensee
Eric IsenseeFounder · Last updated 5 мая 2026 г.

Кратко

Захватывайте необработанные скриншоты приложения в тест-раннере, отправляйте их в API Screenshots.live с идентификатором шаблона и списком локалей, а Fastlane записывает отрендеренный, оформленный и локализованный результат обратно в ваш репозиторий. Fastlane deliver / supply отправляют их в магазины. Весь конвейер выполняется при пуше тега или ночному cron-расписанию менее чем за пять минут.

Зачем вообще автоматизировать скриншоты?

Большинство команд относятся к скриншотам App Store и Google Play как к одноразовому, вручную созданному ассету: дизайнер делает макет в Figma, маркетолог загружает его в App Store Connect, и до следующего крупного редизайна никто их не трогает. Эта модель ломается в момент, когда вы выпускаетесь более чем на одной локали. С 13 поддерживаемыми локалями, тремя обязательными размерами iPhone, двумя размерами iPad плюс телефоном и планшетом для Google Play вы получаете более 100 PNG-файлов на один релиз. Умножьте это на каждое изменение текста, каждую промо-акцию, каждый вариант A/B-теста — и арифметика перестаёт сходиться.

Автоматизация в CI/CD превращает скриншоты в артефакт сборки, как и любой другой бинарник. Они версионируются, воспроизводятся и привязаны к коммиту. Когда маркетинг меняет подпись, конвейер пересобирает каждую локаль и загружает их — без ручных сессий в Photoshop, без расхождения ассетов между языками, без отказа при подаче из-за того, что кто-то забыл вариант для 6,7-дюймового iPhone.

Как на самом деле выглядит конвейер?

Чистый CI-конвейер для скриншотов состоит из трёх этапов: захват, рендеринг и доставка. Захват — это ваш существующий набор UI-тестов: XCUITest на iOS, Espresso на Android, Detox или Maestro для React Native. Рендеринг — это один вызов API Screenshots.live на каждую локаль, который компонует ваши исходные кадры на шаблоне, разработанном вами один раз. Доставка — это уже знакомые действия Fastlane deliver и supply, которые вы уже используете для отправки бинарников.

REST API рендеринга Screenshots.live — ключевой элемент. Вы отправляете необработанные скриншоты один раз и получаете обратно все размеры устройств, все локали и все варианты — полностью оформленные, с подписями и локализованные. Никакого headless-браузера, никакого Puppeteer, никакого ручного изменения размеров.

Как подключить это с Fastlane?

Fastlane — канонический инструмент автоматизации релизов для iOS и Android. Мы поставляем официальный плагин Fastlane, который оборачивает API рендеринга в одно действие. Установите его из корня вашего проекта:

terminal
bash
fastlane add_plugin screenshotslive

Затем добавьте lane в ваш Fastfile, который захватывает, рендерит и записывает отрендеренный результат:

fastlane/Fastfile
ruby
lane :render_screenshots do
  # 1. Capture raw frames with XCUITest
  capture_screenshots(
    workspace: "MyApp.xcworkspace",
    scheme: "MyAppUITests",
    devices: ["iPhone 16 Pro Max", "iPad Pro 12.9-inch"],
    languages: ["en-US", "de-DE", "es-ES", "fr-FR", "pt-BR"]
  )

  # 2. Send raw frames to Screenshots.live, get framed PNGs back
  screenshotslive_render(
    api_token: ENV["SCREENSHOTSLIVE_API_TOKEN"],
    template_id: "tpl_app_release_v3",
    input_dir: "./fastlane/screenshots",
    output_dir: "./fastlane/rendered",
    locales: ["en", "de", "es", "fr", "pt"],
    devices: ["iphone-6.7", "iphone-6.1", "ipad-12.9"]
  )

  # 3. Push to App Store Connect
  deliver(
    screenshots_path: "./fastlane/rendered",
    skip_binary_upload: true,
    skip_metadata: false
  )
end

Плагин обрабатывает загрузку, опрос завершения и скачивание параллельно. Типичный рендеринг для 5 локалей и 3 устройств занимает 60–90 секунд.

Как запустить это в GitHub Actions?

Поместите lane в файл workflow. Приведённый ниже workflow запускается при каждом пуше тега, соответствующего v*, и по ночному расписанию, поэтому достаточно вручную выполнить git tag v1.2.0 && git push --tags, чтобы запустить новый рендеринг.

.github/workflows/screenshots.yml
yaml
name: Render Store Screenshots

on:
  push:
    tags: ["v*"]
  schedule:
    - cron: "0 4 * * *"  # nightly at 04:00 UTC
  workflow_dispatch:

jobs:
  render:
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4

      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.2"
          bundler-cache: true

      - name: Render screenshots
        env:
          SCREENSHOTSLIVE_API_TOKEN: ${{ secrets.SCREENSHOTSLIVE_API_TOKEN }}
          FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
          FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
        run: bundle exec fastlane render_screenshots

      - name: Upload rendered artifacts
        uses: actions/upload-artifact@v4
        with:
          name: store-screenshots
          path: fastlane/rendered/

Для команд, предпочитающих GitHub Action без Fastlane, мы также публикуем отдельный GitHub Action, который напрямую обращается к API:

.github/workflows/screenshots-direct.yml
yaml
- uses: Screenshots-Live/render-screenshots-action@v1
  with:
    api-token: ${{ secrets.SCREENSHOTSLIVE_API_TOKEN }}
    template-id: tpl_app_release_v3
    locales: en,de,es,fr,pt,it,nl
    devices: iphone-6.7,iphone-6.1,ipad-12.9
    output-dir: ./screenshots

А что насчёт Bitrise?

Пользователи Bitrise получают тот же lane через существующий шаг Fastlane. Добавьте секрет SCREENSHOTSLIVE_API_TOKEN в ваше рабочее пространство Bitrise, затем вставьте это в ваш bitrise.yml:

bitrise.yml
yaml
workflows:
  render-screenshots:
    steps:
    - activate-ssh-key@4: {}
    - git-clone@8: {}
    - script@1:
        title: Install Fastlane plugins
        inputs:
        - content: |-
            #!/usr/bin/env bash
            bundle install
            bundle exec fastlane add_plugin screenshotslive
    - fastlane@3:
        inputs:
        - lane: render_screenshots
        - work_dir: "$BITRISE_SOURCE_DIR"
    - deploy-to-bitrise-io@2:
        inputs:
        - deploy_path: ./fastlane/rendered

Как сохранить низкое время сборки?

Медленная часть любого CI-конвейера для скриншотов — это рендеринг, а не загрузка. Уловка в том, чтобы кэшировать рендеры по хешу контента, чтобы неизменённые локали повторно использовали предыдущий результат. Screenshots.live возвращает стабильный renderHash в ответе — используйте его как ключ кэша:

  • Версия шаблона + локаль + хеш варианта → ключ кэша
  • Байты отрендеренного PNG → значение кэша
  • TTL кэша: 30 дней или до повторной публикации шаблона

В GitHub Actions используйте actions/cache@v4 с ключом, производным от идентификатора вашего шаблона и списка локалей. Большинство релизных сборок полностью пропускают рендеринг и пересобирают только ту локаль, которая действительно изменилась.

Как валидировать результат перед отправкой?

Apple и Google молча отклоняют скриншоты, если они не проходят проверки формата: PNG с альфа-каналом, JPEG, закодированный как CMYK, или скриншот для 6,7-дюймового iPhone, отличающийся на один пиксель. Создайте 20-строчный шаг линтинга, который ловит это до отправки:

fastlane/lint_screenshots.rb
ruby
require "chunky_png"

Dir.glob("./fastlane/rendered/**/*.png").each do |path|
  png = ChunkyPNG::Image.from_file(path)
  raise "alpha channel: #{path}" if png.metadata["ColorType"] == 6
  raise "too large: #{path}"     if File.size(path) > 30 * 1024 * 1024
  raise "wrong size: #{path}"    if png.width < 1242
end

Стоит ли запускать это по расписанию?

Да. Ночной cron означает, что скриншоты регенерируются каждый раз, когда меняется ваш шаблон — будь то изменение от дизайнера в редакторе, обновление текста от маркетинга или добавление новой локали. Без расписания листинги в магазине расходятся с вашим реальным продуктом, и кому-то нужно помнить о пуше свежей сборки перед каждой подачей.

Совместите расписание с оповещением в Slack или по email при сбое. Сломанный рендер в 04:00 UTC — это уведомление в 07:00 по местному времени, а не блокировщик релиза, обнаруженный в 17:00 в день, когда вы хотели выпуститься.

А что с Android, React Native и Flutter?

Конвейер платформонезависим со стороны рендеринга — меняется только этап захвата. Для Android замените capture_screenshots на Fastlane screengrab и ваш набор Espresso. Для React Native используйте снимки Detox. Для Flutter используйте скриншоты integration_test. Все они производят необработанные PNG, которые Screenshots.live обрабатывает одинаково.

Подробнее в нашем руководстве по поддержке нескольких платформ для конфигурации захвата на каждом стеке.

Куда двигаться дальше?

Как только ваш CI-конвейер надёжно отправляет скриншоты, следующий вопрос — какие скриншоты выигрывают. Объедините этот конвейер с руководством по A/B-тестированию, чтобы выпускать варианты по расписанию и измерять конверсию. Совместите его с руководством по локализации, чтобы добавить 30+ локалей без 30+ конвейеров.

Авторитетные источники для дальнейшего чтения: документация Fastlane по скриншотам iOS, действие Fastlane deliver и официальные спецификации скриншотов App Store Connect.

Попробуйте на реальном репозитории

Создавайте все эти размеры автоматически

Перестаньте менять размер скриншотов вручную. Создайте один шаблон и получите все размеры, устройства и локали одним вызовом API.

Начните бесплатно — попробуйте Screenshots.live