CodePush закрыли. Чем заменить.

Microsoft закрыл Visual Studio App Center, а вместе с ним CodePush, 31 марта 2025 года. 20 мая 2025 года оба репозитория заархивированы и переведены в режим только для чтения: microsoft/react-native-code-push — клиентская библиотека, и microsoft/code-push-server — отдельный сервер, который Microsoft опубликовал, чтобы команды могли поднять его сами.

В README этого сервера прямо написано, что именно вы берёте на себя: «This code is provided "as is", because of that Microsoft will not provide support services for it» — код предоставляется как есть, поддержки не будет.

Так что вопрос уже не в том, переезжать ли. Вопрос в том, куда — и во сколько это обойдётся вашему приложению.

Четыре честных варианта

1. Оставить CodePush и держать его самим

Развернуть code-push-server, направить на него CLI, в приложении не менять ничего. Сегодня это самая маленькая правка, а завтра — самая большая ответственность: заархивированный сервер и заархивированный клиентский SDK, ни один из которых не получает исправлений, стоят между вашими пользователями и каждым релизом. Когда очередная смена архитектуры React Native сломает нативный модуль, чинить его сверху будет некому.

Разумно, если вам нужны недели, а не годы, и вы уже переезжаете куда-то ещё.

2. EAS Update

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

3. Хостинговый сервис со своим SDK

Место закрывшегося CodePush заняли несколько продуктов, и у каждого свой React Native SDK. Они работают. Но вы уходите от одной проприетарной клиентской библиотеки к другой — то есть ровно в то положение, из-за которого март 2025-го оказался дорогим. Остановится этот вендор — переезжать снова.

4. Сервис на открытом протоколе Expo Updates

expo-updates — публичная клиентская библиотека, реализующая опубликованный протокол. Любой сервер, который на нём говорит, может обслуживать ваше приложение, а переход между такими серверами — это URL в конфигурации, а не замена SDK. Otapush — один из таких серверов. EAS Update — тоже. Это сделано намеренно: два аргумента ниже — единственные, которые конкурент не может просто скопировать.

Сравнение

Свой CodePush EAS Update Otapush
Клиентская библиотека react-native-code-push (архив) expo-updates expo-updates
Кто поддерживает клиент никто Expo Expo
Кто держит сервер вы Expo мы
Протокол CodePush, проприетарный Expo Updates v1 Expo Updates v1
Нужен аккаунт Expo нет да нет
Цена ухода переписать клиент один URL один URL
Оплата в рублях, счёт для юрлиц ваш счёт за хостинг нет да

Вопрос, который на самом деле всё решает

Не функции. Кому принадлежит библиотека внутри вашего бинарника.

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

expo-updates — единственный клиент в этой нише, который не принадлежит поставщику обновлений. Выбрав сервер, говорящий на его протоколе, вы делаете следующий переезд — в том числе от нас — правкой конфигурации.

Переход с react-native-code-push

Работа сводится к замене клиентской библиотеки. Серверная часть — это URL.

1. Убрать CodePush

Отвяжите и удалите react-native-code-push, снимите обёртку codePush() с корневого компонента и уберите нативную конфигурацию: CodePushDeploymentKey в Info.plist, записи CodePush в strings.xml и MainApplication.

2. Поставить expo-updates

В bare-проекте React Native:

npx install-expo-modules@latest
npm install expo-updates
cd ios && pod install

install-expo-modules добавляет ядро Expo-модулей, без которого expo-updates не работает. Managed-проектом Expo ваш проект от этого не становится — что именно меняется, а что нет, разобрано в Bare React Native.

3. Направить его на ваше приложение

npm install -g otapush
otapush login
otapush init

otapush init прописывает URL обновлений, runtime version и сертификат подписи вашего приложения в AndroidManifest.xml и Expo.plist (или в app.json для managed-проектов Expo). Managed-случай разобран в настройке Expo.

4. Собрать один раз и публиковать

URL обновлений и сертификат подписи лежат в бинарнике, поэтому переезд требует одного релиза в сторе. Дальше публикация выглядит так:

otapush publish --channel prod --platform ios

Что меняется в вашем JavaScript

codePush.sync() делал проверку, загрузку и перезапуск одним вызовом. expo-updates разделяет их: Updates.checkForUpdateAsync(), Updates.fetchUpdateAsync(), Updates.reloadAsync() — плюс хук useUpdates(), если состоянием обновления нужно управлять из интерфейса. По умолчанию библиотека проверяет обновления при запуске и применяет их при следующем — то есть ведёт себя так, как и было настроено большинство установок CodePush.

Ключи развёртывания превращаются в каналы. Там, где у CodePush были Staging и Production, у приложения в Otapush есть dev, staging и prod, а перевод проверенной сборки между ними — одна команда, см. справочник CLI.

Оплата из России

Именно из-за этого нам пишет большинство команд.

Expo выставляет счета через международные платёжные сервисы. Otapush принимает карты и СБП в рублях через российский эквайринг, а юрлица могут оплатить по счёту на российскую компанию. Цены — в разделе тарифов, тарификация по MAU (уникальные устройства, которые за календарный месяц проверили или скачали обновление), а превышение лимита не останавливает раздачу обновлений.

Если вы за пределами России, самостоятельной оплаты пока нет — напишите нам, договоримся.

Вопросы

Придётся ли переходить на Expo, чтобы использовать expo-updates?

Нет. expo-updates работает в bare-проекте React Native. Ему нужно ядро Expo-модулей, которое добавляет install-expo-modules, но проект сохраняет свою нативную сборку, свой CI и свой процесс релизов. Аккаунт Expo при этом не участвует.

Можно ли остаться на CodePush и решить позже?

Можно, и на коротком горизонте это защитимый выбор. Вы ставите на то, что ничто в React Native не сломает неподдерживаемый нативный модуль раньше, чем вы переедете. С каждым релизом эта ставка становится хуже.

Разрешает ли это App Store?

Да, для обновлений JavaScript и ассетов. Лицензионный договор Apple (§3.3.1(B)) разрешает загружать интерпретируемый код, пока он не меняет заявленное назначение приложения и не обходит защиту ОС; в Google Play есть такое же исключение для интерпретируемых языков. Нативные изменения по-прежнему требуют релиза через стор. Обе нормы процитированы полностью в правилах сторов.

Что будет, если ваш сервер упадёт?

expo-updates запустит бандл, который уже лежит на устройстве, — последнее скачанное обновление или версию из сборки, — и повторит попытку при следующем запуске. Недоступность сервера обновлений не роняет приложение. Это свойство клиента, и у CodePush оно было таким же.

Чем это отличается от сервиса со своим SDK, который я уже почти выбрал?

Механически — почти ничем: оба доставляют JavaScript-бандл. Разница проявляется в тот день, когда вы захотите уйти. От нас уход стоит одного URL, потому что клиент не наш.

Выпустите первое OTA-обновление сегодня

Бесплатный тариф — 10 MAU, то есть уникальных устройств в месяц. Карта не нужна.