@novacraft-engineering/pulsar-cli
v1.4.1
Published
Pulsar OTA release CLI — bundle, hash, and ship React Native updates over-the-air.
Maintainers
Readme
@novacraft-engineering/pulsar-cli
Release CLI for Pulsar — the free, self-hostable CodePush alternative for React Native. Bundle your JavaScript, hash it exactly the way the on-device client does, and ship it over-the-air to live apps in seconds. No app-store review.
Microsoft App Center CodePush was retired in March 2025. Pulsar speaks the same CodePush protocol, so migrating is a deployment key and a server URL — not a rewrite. See usepulsar.dev/codepush-alternative.
Install
npm i -g @novacraft-engineering/pulsar-cli
# or run without installing:
npx @novacraft-engineering/pulsar-cli login https://api.usepulsar.dev<apiUrl> is whatever Pulsar server you release against. api.usepulsar.dev
is the hosted one; point it at your own if you self-host. The CLI talks to the
Pulsar API and nothing else, so it has no host compiled into it.
Commands
pulsar login <apiUrl> # authenticate (API key or browser), saved to ~/.pulsar/config.json
# --api-key paste a key from Settings → API keys
# --browser sign in via the dashboard, no key to manage
pulsar apps # list your apps and deployment keys
pulsar release <app> <deployment> --platform ios|android --target <ver> \
[--rollout 25] [--mandatory] [--disabled] [--devices id1,id2,...] [--description "fix crash"]
pulsar promote <app> <from> <to> [--rollout 100]
pulsar rollback <app> <deployment>
pulsar deployment ls <app> # list deployments + keys
pulsar deployment add <app> <name> # create a new deployment (e.g. QA)
pulsar history <app> <deployment> # release history
pulsar patch <app> <deployment> <label> [--rollout N] [--mandatory] [--disable] [--enable] [--devices id1,id2,...]
pulsar migrate --from <serverUrl> --access-key <key> # move apps from RevoPush / code-push-server (see below)Targeting a release's reach
--rollout N— serve to a deterministic N% of devices (byclient_unique_id).--devices id1,id2— serve only to those exact device ids (most granular). Empty = all devices.--disabled— upload the bundle but don't serve it (flip on later withpulsar patch … --enable).--target <range>— semver range of native binary versions the release applies to.
How a release works
release runs the React Native bundler, computes the CodePush package hash (the exact
path:sha256 manifest algorithm the on-device client uses — so a device that already has the
bundle correctly reports "up to date"), zips the output, and uploads it to Pulsar. The client
then downloads it in the background and swaps it in on next launch.
Auth is read from PULSAR_API_URL / PULSAR_TOKEN env, or ~/.pulsar/config.json (written by pulsar login). Nothing is committed.
Migrating from RevoPush / code-push-server
pulsar migrate copies apps from any server that speaks the CodePush management API
(RevoPush, a self-hosted microsoft/code-push-server) into Pulsar:
pulsar migrate --from https://<your-codepush-server> --access-key <source access key> \
[--apps MyApp-iOS,MyApp-Android] [--org "My Org"] [--platform ios|android] \
[--releases latest|none] [--dry-run]- Each source app becomes a Pulsar app in
--org(optional when you belong to one org), or reuses the Pulsar app with the same name and platform. Deployments keep their names; existing ones are reused. - The platform comes from the source app's
os, or its name (*-iOS,*-Android). Pass--platformfor apps named neither way. --releases latest(default) downloads each deployment's current release, re-hashes and signs it the waypulsar releasedoes, and publishes it with the same target version, description, mandatory flag, rollout and disabled state. A release that can't be downloaded or unzipped is reported and skipped. Re-running is safe: a bundle already on the Pulsar deployment is not re-uploaded.--dry-runreads both servers and prints the plan without writing anything to Pulsar.
The run ends with a table of source key → new Pulsar key per deployment. Devices keep checking the old server until each app is rebuilt with its new key and Pulsar's URL:
<!-- iOS Info.plist -->
<key>CodePushDeploymentKey</key><string>NEW_PULSAR_KEY</string>
<key>CodePushServerURL</key><string>https://api.usepulsar.dev</string>
<!-- Android res/values/strings.xml -->
<string moduleConfig="true" name="CodePushDeploymentKey">NEW_PULSAR_KEY</string>
<string moduleConfig="true" name="CodePushServerUrl">https://api.usepulsar.dev</string>Use it from CI
Both env vars are all a GitHub Actions / GitLab CI job needs:
- run: npm i -g @novacraft-engineering/pulsar-cli
- run: pulsar release MyApp Production --platform ios --target 1.4.0 --rollout 25
env:
PULSAR_API_URL: https://api.usepulsar.dev
PULSAR_TOKEN: ${{ secrets.PULSAR_TOKEN }}Or connect the repo in the dashboard and fire a release for any commit without leaving the browser.
Related
@novacraft-engineering/pulsar-code-push— the React Native client this CLI ships to- Pulsar vs CodePush vs RevoPush
- How React Native OTA updates actually work
- Staged rollouts and instant rollback
License
MIT © Novacraft Engineering
