@novacraft-engineering/pulsar-code-push 1.5.5 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CodePush.js +17 -1
- package/README.md +28 -4
- package/ios/CodePush/CodePush.m +3 -11
- package/package.json +8 -5
- package/request-fetch-adapter.js +27 -2
package/CodePush.js
CHANGED
|
@@ -9,6 +9,12 @@ import { setTargetingInfo, getTargetingInfo } from "./targeting";
|
|
|
9
9
|
let NativeCodePush = require("react-native").NativeModules.CodePush;
|
|
10
10
|
const PackageMixins = require("./package-mixins")(NativeCodePush);
|
|
11
11
|
|
|
12
|
+
// Apps that choose their deployment key in JS (per environment) leave the native one empty, so the
|
|
13
|
+
// launch-time status report has no key. Fall back to the first key the app checks with (its primary
|
|
14
|
+
// backend) and send a report that was held for want of one as soon as that key is known.
|
|
15
|
+
let fallbackDeployment = null;
|
|
16
|
+
let reportAwaitingKey = null;
|
|
17
|
+
|
|
12
18
|
async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchCallback = null, serverUrl = null) {
|
|
13
19
|
/*
|
|
14
20
|
* Before we ask the server if an update exists, we
|
|
@@ -39,6 +45,14 @@ async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchC
|
|
|
39
45
|
...(deploymentKey ? { deploymentKey } : {}),
|
|
40
46
|
...(serverUrl ? { serverUrl } : {}),
|
|
41
47
|
};
|
|
48
|
+
if (config.deploymentKey && !fallbackDeployment) {
|
|
49
|
+
fallbackDeployment = { deploymentKey: config.deploymentKey, serverUrl: config.serverUrl };
|
|
50
|
+
if (reportAwaitingKey) {
|
|
51
|
+
const heldReport = reportAwaitingKey;
|
|
52
|
+
reportAwaitingKey = null;
|
|
53
|
+
tryReportStatus(heldReport);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
42
56
|
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
|
|
43
57
|
|
|
44
58
|
// Use dynamically overridden getCurrentPackage() during tests.
|
|
@@ -193,7 +207,8 @@ async function notifyApplicationReadyInternal() {
|
|
|
193
207
|
}
|
|
194
208
|
|
|
195
209
|
async function tryReportStatus(statusReport, retryOnAppResume) {
|
|
196
|
-
const
|
|
210
|
+
const nativeConfig = await getConfiguration();
|
|
211
|
+
const config = nativeConfig.deploymentKey || !fallbackDeployment ? nativeConfig : { ...nativeConfig, ...fallbackDeployment };
|
|
197
212
|
const previousLabelOrAppVersion = statusReport.previousLabelOrAppVersion;
|
|
198
213
|
const previousDeploymentKey = statusReport.previousDeploymentKey || config.deploymentKey;
|
|
199
214
|
try {
|
|
@@ -201,6 +216,7 @@ async function tryReportStatus(statusReport, retryOnAppResume) {
|
|
|
201
216
|
log(`Reporting binary update (${statusReport.appVersion})`);
|
|
202
217
|
|
|
203
218
|
if (!config.deploymentKey) {
|
|
219
|
+
reportAwaitingKey = statusReport;
|
|
204
220
|
throw new Error("Deployment key is missed");
|
|
205
221
|
}
|
|
206
222
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @novacraft-engineering/pulsar-code-push
|
|
2
2
|
|
|
3
|
-
The React Native client for [Pulsar](https://
|
|
3
|
+
The React Native client for [Pulsar](https://usepulsar.dev) — **over-the-air JavaScript updates** for your app, no app-store review. A free, self-hostable **CodePush alternative** for React Native.
|
|
4
4
|
|
|
5
5
|
This is a full CodePush client (native iOS + Android modules), not a wrapper. It is a fork of
|
|
6
6
|
[`@revopush/react-native-code-push`](https://github.com/revopush/react-native-code-push) — itself the
|
|
@@ -8,6 +8,13 @@ New-Architecture-maintained continuation of Microsoft's original
|
|
|
8
8
|
[`react-native-code-push`](https://github.com/microsoft/react-native-code-push) (archived March 2025).
|
|
9
9
|
It speaks the CodePush protocol, so it works against any CodePush-compatible server; it defaults to Pulsar.
|
|
10
10
|
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
Microsoft App Center CodePush shut down in March 2025 and Expo's EAS Update only covers Expo-managed
|
|
14
|
+
projects. Pulsar keeps the CodePush protocol every bare React Native app already knows, adds staged
|
|
15
|
+
rollouts, instant rollback, differential updates and per-release health metrics, and can be self-hosted.
|
|
16
|
+
Read the full comparison at [usepulsar.dev/codepush-alternative](https://usepulsar.dev/codepush-alternative).
|
|
17
|
+
|
|
11
18
|
## Install
|
|
12
19
|
|
|
13
20
|
```bash
|
|
@@ -25,7 +32,7 @@ Autolinking registers the native module on both platforms (RN 0.70+, New Archite
|
|
|
25
32
|
<key>CodePushDeploymentKey</key>
|
|
26
33
|
<string>YOUR_DEPLOYMENT_KEY</string>
|
|
27
34
|
<key>CodePushServerURL</key>
|
|
28
|
-
<string>https://api.
|
|
35
|
+
<string>https://api.usepulsar.dev</string>
|
|
29
36
|
```
|
|
30
37
|
|
|
31
38
|
**Android** — `android/app/src/main/res/values/strings.xml`:
|
|
@@ -33,7 +40,7 @@ Autolinking registers the native module on both platforms (RN 0.70+, New Archite
|
|
|
33
40
|
```xml
|
|
34
41
|
<resources>
|
|
35
42
|
<string moduleConfig="true" name="CodePushDeploymentKey">YOUR_DEPLOYMENT_KEY</string>
|
|
36
|
-
<string moduleConfig="true" name="CodePushServerURL">https://api.
|
|
43
|
+
<string moduleConfig="true" name="CodePushServerURL">https://api.usepulsar.dev</string>
|
|
37
44
|
</resources>
|
|
38
45
|
```
|
|
39
46
|
|
|
@@ -56,14 +63,31 @@ The full CodePush API is available — `codePush.sync()`, `codePush.checkForUpda
|
|
|
56
63
|
`InstallMode`, `CheckFrequency`, `notifyAppReady()`, rollout %, mandatory updates, binary-version
|
|
57
64
|
targeting, differential updates, automatic rollback, and download/install telemetry (reported to Pulsar).
|
|
58
65
|
|
|
66
|
+
## Migrating from CodePush or RevoPush
|
|
67
|
+
|
|
68
|
+
Swap the import and point `CodePushServerURL` at Pulsar — the native modules, the JS API and the
|
|
69
|
+
package-hash algorithm are unchanged, so devices already running your current bundle correctly
|
|
70
|
+
report "up to date" instead of re-downloading it.
|
|
71
|
+
|
|
72
|
+
```diff
|
|
73
|
+
-import codePush from 'react-native-code-push';
|
|
74
|
+
+import codePush from '@novacraft-engineering/pulsar-code-push';
|
|
75
|
+
```
|
|
76
|
+
|
|
59
77
|
## Releasing
|
|
60
78
|
|
|
61
79
|
Use the Pulsar CLI ([`@novacraft-engineering/pulsar-cli`](https://github.com/Novacraft-Africa/pulsar-cli)):
|
|
62
80
|
|
|
63
81
|
```bash
|
|
64
|
-
npx pulsar release MyApp Staging --platform ios --target 1.0.0
|
|
82
|
+
npx @novacraft-engineering/pulsar-cli release MyApp Staging --platform ios --target 1.0.0
|
|
65
83
|
```
|
|
66
84
|
|
|
85
|
+
## Related
|
|
86
|
+
|
|
87
|
+
- [Pulsar vs CodePush vs RevoPush](https://usepulsar.dev/blog/codepush-vs-revopush-vs-pulsar)
|
|
88
|
+
- [Microsoft CodePush shutdown: what to move to](https://usepulsar.dev/blog/microsoft-codepush-shutdown-alternatives)
|
|
89
|
+
- [How React Native OTA updates actually work](https://usepulsar.dev/blog/how-react-native-ota-updates-work)
|
|
90
|
+
|
|
67
91
|
## License
|
|
68
92
|
|
|
69
93
|
MIT. Original CodePush © Microsoft; RevoPush fork © Revopush; this Pulsar distribution © Novacraft Engineering.
|
package/ios/CodePush/CodePush.m
CHANGED
|
@@ -570,19 +570,11 @@ static NSString *const LatestRollbackCountKey = @"count";
|
|
|
570
570
|
} else {
|
|
571
571
|
CPLog(@"Attempted to perform a rollback when there is no current update");
|
|
572
572
|
}
|
|
573
|
-
|
|
574
|
-
//
|
|
575
|
-
|
|
576
|
-
// in NSUserDefaults and survives. Reloading here re-enters this same
|
|
577
|
-
// path on the new bridge, so the app reload-loops on a blank screen
|
|
578
|
-
// until it is killed. Drop the stale flag and keep the loaded bundle.
|
|
579
|
-
[CodePush removePendingUpdate];
|
|
580
|
-
return;
|
|
573
|
+
} else {
|
|
574
|
+
// Write the current package's metadata to the "failed list"
|
|
575
|
+
[self saveFailedUpdate:failedPackage];
|
|
581
576
|
}
|
|
582
577
|
|
|
583
|
-
// Write the current package's metadata to the "failed list"
|
|
584
|
-
[self saveFailedUpdate:failedPackage];
|
|
585
|
-
|
|
586
578
|
// Rollback to the previous version and de-register the new update
|
|
587
579
|
[CodePushPackage rollbackPackage];
|
|
588
580
|
[CodePush removePendingUpdate];
|
package/package.json
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novacraft-engineering/pulsar-code-push",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "Pulsar OTA client for React Native — over-the-air JavaScript updates. A CodePush-compatible client (RevoPush lineage) pointed at Pulsar.",
|
|
5
5
|
"main": "CodePush.js",
|
|
6
6
|
"typings": "typings/react-native-code-push.d.ts",
|
|
7
|
-
"homepage": "https://
|
|
7
|
+
"homepage": "https://usepulsar.dev",
|
|
8
8
|
"keywords": [
|
|
9
|
-
"react-native",
|
|
10
9
|
"code-push",
|
|
11
10
|
"codepush",
|
|
12
|
-
"
|
|
11
|
+
"codepush-alternative",
|
|
12
|
+
"hot-update",
|
|
13
13
|
"ota",
|
|
14
|
+
"ota-updates",
|
|
14
15
|
"over-the-air",
|
|
15
|
-
"
|
|
16
|
+
"pulsar",
|
|
17
|
+
"react-native",
|
|
18
|
+
"react-native-ota"
|
|
16
19
|
],
|
|
17
20
|
"author": "Novacraft Engineering",
|
|
18
21
|
"license": "MIT",
|
package/request-fetch-adapter.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
const { Platform } = require("react-native");
|
|
1
2
|
const packageJson = require("./package.json");
|
|
2
3
|
const { getTargetingInfo } = require("./targeting");
|
|
3
4
|
|
|
5
|
+
// React Native's default agent carries no usable platform detail — iOS sends a Darwin/CFNetwork
|
|
6
|
+
// string with no "iPhone OS x_y", and Android goes out through OkHttp as bare "okhttp/4.x". Both
|
|
7
|
+
// left the server guessing (Android was filed as "unknown" entirely, OS version was always empty),
|
|
8
|
+
// so state it explicitly instead.
|
|
9
|
+
function userAgent() {
|
|
10
|
+
const os = Platform.OS === "ios" ? "iOS" : Platform.OS === "android" ? "Android" : Platform.OS;
|
|
11
|
+
const version = Platform.Version === undefined || Platform.Version === null ? "" : String(Platform.Version);
|
|
12
|
+
return `Pulsar/${packageJson.version} (${os}${version ? " " + version : ""})`;
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
module.exports = {
|
|
5
16
|
async request(verb, url, requestBody, callback) {
|
|
6
17
|
if (typeof requestBody === "function") {
|
|
@@ -9,18 +20,32 @@ module.exports = {
|
|
|
9
20
|
}
|
|
10
21
|
|
|
11
22
|
// Scope the update-check to a specific device model / user id when the host app has set them.
|
|
12
|
-
|
|
23
|
+
const { userId, deviceModel } = getTargetingInfo();
|
|
13
24
|
if (verb === 0 && url.indexOf("update_check") !== -1) {
|
|
14
|
-
const { userId, deviceModel } = getTargetingInfo();
|
|
15
25
|
const extra = [];
|
|
16
26
|
if (deviceModel) extra.push("device_model=" + encodeURIComponent(deviceModel));
|
|
17
27
|
if (userId) extra.push("user_id=" + encodeURIComponent(userId));
|
|
18
28
|
if (extra.length) url += (url.indexOf("?") === -1 ? "?" : "&") + extra.join("&");
|
|
19
29
|
}
|
|
20
30
|
|
|
31
|
+
// Status reports carry the same identity. update_check only records a device when an update is
|
|
32
|
+
// actually served, so without this the device/user targeting lists never fill on a quiet app.
|
|
33
|
+
// The acquisition SDK hands us an already-stringified body here, so parse before augmenting.
|
|
34
|
+
if (url.indexOf("report_status") !== -1 && requestBody && (deviceModel || userId)) {
|
|
35
|
+
try {
|
|
36
|
+
const parsed = typeof requestBody === "string" ? JSON.parse(requestBody) : requestBody;
|
|
37
|
+
if (deviceModel && !parsed.device_model) parsed.device_model = deviceModel;
|
|
38
|
+
if (userId && !parsed.user_id) parsed.user_id = userId;
|
|
39
|
+
requestBody = parsed;
|
|
40
|
+
} catch (err) {
|
|
41
|
+
// A body we can't parse is left exactly as-is — reporting must never fail over telemetry.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
21
45
|
const headers = {
|
|
22
46
|
"Accept": "application/json",
|
|
23
47
|
"Content-Type": "application/json",
|
|
48
|
+
"User-Agent": userAgent(),
|
|
24
49
|
"X-CodePush-Plugin-Name": packageJson.name,
|
|
25
50
|
"X-CodePush-Plugin-Version": packageJson.version,
|
|
26
51
|
"X-CodePush-SDK-Version": packageJson.dependencies["code-push"]
|