Как автоматизировать скриншоты App Store в CI/CD
Практический сквозной конвейер для генерации скриншотов App Store и Google Play из вашей CI-системы с использованием Fastlane, GitHub Actions и Bitrise — с конкретными конфигурациями, которые можно вставить в ваш репозиторий.
Кратко
Захватывайте необработанные скриншоты приложения в тест-раннере, отправляйте их в 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 рендеринга в одно действие. Установите его из корня вашего проекта:
fastlane add_plugin screenshotsliveЗатем добавьте lane в ваш Fastfile, который захватывает, рендерит и записывает отрендеренный результат:
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, чтобы запустить новый рендеринг.
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:
- 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:
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-строчный шаг линтинга, который ловит это до отправки:
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