A React Native Update command line tool for bundling, uploading native packages, publishing OTA versions, and managing apps/packages.
- Single
pushy/crescCLI entrypoint - Backward-compatible command set
- Programmatic provider API for build scripts and CI/CD
- TypeScript type definitions
npm install -g react-native-update-cliThe command-line tool is installed globally. Invoke pushy or cresc directly; do not use a project-local CLI or prefix commands with npx.
pushy help
pushy list
pushy bundle --platform ios
pushy publish --platform ios --name 1.0.0
pushy uploadIpa ./app.ipaThe Provider API is a library integration, separate from the globally installed CLI. Add the package to build-tool dependencies only when importing this API; command-line usage continues to use the global pushy / cresc binaries.
import { CLIProviderImpl } from 'react-native-update-cli';
const provider = new CLIProviderImpl();
const bundleResult = await provider.bundle({
platform: 'ios',
dev: false,
sourcemap: true,
});
if (!bundleResult.success) {
throw new Error(bundleResult.error);
}
const publishResult = await provider.publish({
filePath: '.pushy/output/ios.ppk',
platform: 'ios',
name: 'v1.2.3',
description: 'Bug fixes and improvements',
rollout: 100,
});bundle: Bundle JavaScript code and optionally publishhdiff: Generate hdiff between two PPK fileshdiffFromApk: Generate hdiff from APK fileshdiffFromApp: Generate hdiff from APP fileshdiffFromIpa: Generate hdiff from IPA files
Hermes projects: bundle always runs hermesc with -output-source-map, so the debug info section is stripped from the bytecode (15–40% smaller, same as React Native's own release builds). The Hermes sourcemap stays in the intermediate directory (.pushy/intermedia/<platform>/<bundle>.map, never packed into the ppk); with --sourcemap it is composed with the packager map as before. Keep it if you want to symbolicate address at crash stacks later.
Hermes delta mode (-base-bytecode): by default (--hermesBase auto) bundle compiles against the previous HBC of the same app, which keeps Hermes string IDs stable and makes hot-update patches 5–30× smaller. The base comes from the server (GET /app/:id/hermesBase), verified by sha256 and kept in a local cache (.pushy/cache/<sha256>, 500 MB / 20 files, PUSHY_CACHE_DIR / --cacheMaxMb to tune, pushy cache [clean] to inspect or clear). --hermesBase none disables it; --hermesBase <file.hbc|.ppk|.apk|.ipa> uses a local artifact (for example the store build). --verifyHermesBase (default on) additionally compiles without the base and compares both disassemblies; on any mismatch or failure the CLI silently falls back to the plain compile, so the feature can never block a release. Only hermesc builds that include the upstream delta-mode fix are used (classic react-native/sdks/hermesc, or hermes-compiler ≥ 250829098).
publish: Publish a new versionversions: List versionsupdate: Update version package rulesupdateVersionInfo: Update version metadatadeleteVersion: Delete a version
createApp: Create an appapps: List appsselectApp: Select an appdeleteApp: Delete an app
uploadIpa: Upload IPA filesuploadApk: Upload APK filesuploadAab: Upload AAB filesuploadApp: Upload APP filesparseApp: Parse APP file informationparseIpa: Parse IPA file informationparseApk: Parse APK file informationparseAab: Parse AAB file informationextractApk: Extract a universal APK from an AABpackages: List packagesdeletePackage: Delete a package
login: Loginlogout: Logoutme: Show current user information
interface CLIProvider {
bundle(options: BundleOptions): Promise<CommandResult>;
publish(options: PublishOptions): Promise<CommandResult>;
upload(options: UploadOptions): Promise<CommandResult>;
getSelectedApp(
platform?: Platform,
): Promise<{ appId: string; platform: Platform }>;
listApps(platform?: Platform): Promise<CommandResult>;
createApp(name: string, platform: Platform): Promise<CommandResult>;
listVersions(appId: string): Promise<CommandResult>;
updateVersion(
appId: string,
versionId: string,
updates: Partial<Version>,
): Promise<CommandResult>;
getPlatform(platform?: Platform): Promise<Platform>;
loadSession(): Promise<Session>;
}publish requires a generated .ppk path via filePath. Provider list methods return data in CommandResult.data and do not enter interactive paging.
export PUSHY_REGISTRY=https://your-api-endpoint.com
export NO_INTERACTIVE=trueWhen ios/sentry.properties or android/sentry.properties exists, bundle uploads sourcemaps for OTA packages. The default matching path is Sentry Debug IDs; the CLI no longer infers release/dist from the native package.
React Native projects should use @sentry/react-native/metro in metro.config.js so the generated bundle and sourcemap share the same Debug ID. For Hermes, the CLI copies the packager sourcemap Debug ID to the composed Hermes sourcemap and uploads with:
sentry-cli sourcemaps upload --debug-id-referenceFor older self-hosted Sentry versions or older @sentry/cli versions without Debug ID support, pass explicit legacy release/dist values:
pushy bundle --platform android --name "4.1" --sentry-release "com.example@1.0.0+10+pushy:4.1" --sentry-dist "pushy:4.1"In legacy mode, the app runtime must report exactly the same Sentry release and dist values.
Create update.json in your React Native project:
{
"ios": {
"appId": "your-ios-app-id",
"appKey": "your-ios-app-key"
},
"android": {
"appId": "your-android-app-id",
"appKey": "your-android-app-key"
}
}All provider methods return CommandResult; check success before consuming data. The CLI supports ios, android, and harmony platforms.