code-push-itspar 1.0.0
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/LICENSE.txt +22 -0
- package/README.md +174 -0
- package/bin/script/acquisition-sdk.js +178 -0
- package/bin/script/cli.js +23 -0
- package/bin/script/command-executor.js +1392 -0
- package/bin/script/command-parser.js +1225 -0
- package/bin/script/commands/debug.js +125 -0
- package/bin/script/hash-utils.js +203 -0
- package/bin/script/index.js +5 -0
- package/bin/script/management-sdk.js +531 -0
- package/bin/script/patch-scripts/apply-patch.sh +111 -0
- package/bin/script/patch-scripts/create-patch.sh +53 -0
- package/bin/script/react-native-utils.js +249 -0
- package/bin/script/sign.js +69 -0
- package/bin/script/types/cli.js +50 -0
- package/bin/script/types/rest-definitions.js +19 -0
- package/bin/script/types.js +4 -0
- package/bin/script/utils/config.constants.js +13 -0
- package/bin/script/utils/file-utils.js +50 -0
- package/bin/test/acquisition-rest-mock.js +108 -0
- package/bin/test/acquisition-sdk.js +188 -0
- package/bin/test/cli.js +1342 -0
- package/bin/test/hash-utils.js +149 -0
- package/bin/test/management-sdk.js +338 -0
- package/bsdiff/README.md +114 -0
- package/bsdiff/bsdiff43 +0 -0
- package/package.json +107 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dream Sports Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# CodePush CLI
|
|
2
|
+
|
|
3
|
+
The **CodePush CLI** is a Node.js application that allows users to deploy and manage over-the-air updates for React Native applications.
|
|
4
|
+
|
|
5
|
+
## Installation & Usage
|
|
6
|
+
|
|
7
|
+
### Global Installation
|
|
8
|
+
```bash
|
|
9
|
+
npm install -g code-push-itspar
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Or using yarn:
|
|
13
|
+
```bash
|
|
14
|
+
yarn global add code-push-itspar
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
After global installation, you can use the CLI directly:
|
|
18
|
+
```bash
|
|
19
|
+
code-push-itspar <command>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Project Installation
|
|
23
|
+
```bash
|
|
24
|
+
# Using npm
|
|
25
|
+
npm install --save-dev code-push-itspar
|
|
26
|
+
|
|
27
|
+
# Using yarn
|
|
28
|
+
yarn add --dev code-push-itspar
|
|
29
|
+
```
|
|
30
|
+
After project installation, you can use the CLI through npm/yarn:
|
|
31
|
+
```bash
|
|
32
|
+
# Using npm
|
|
33
|
+
npm run code-push-itspar <command>
|
|
34
|
+
|
|
35
|
+
# Using yarn
|
|
36
|
+
yarn code-push-itspar <command>
|
|
37
|
+
|
|
38
|
+
# Using npx
|
|
39
|
+
npx code-push-itspar <command>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Authentication
|
|
43
|
+
|
|
44
|
+
Most commands require authentication. You'll need an access key and server URL to use the CLI.
|
|
45
|
+
|
|
46
|
+
### Login
|
|
47
|
+
```bash
|
|
48
|
+
# Login with access key and server URL
|
|
49
|
+
code-push-itspar login --accessKey <your-access-key> <server-url>
|
|
50
|
+
|
|
51
|
+
# Example
|
|
52
|
+
code-push-itspar login --accessKey abc123xyz https://codepush.jaswantdhayal.com
|
|
53
|
+
|
|
54
|
+
# Check login status
|
|
55
|
+
code-push-itspar whoami
|
|
56
|
+
|
|
57
|
+
# Logout
|
|
58
|
+
code-push-itspar logout
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
To get an access key:
|
|
62
|
+
|
|
63
|
+
1. Visit your CodePush Dashboard
|
|
64
|
+
2. Go to Settings → Generate New Token
|
|
65
|
+
3. Generate a new access key
|
|
66
|
+
|
|
67
|
+
## Release Management
|
|
68
|
+
|
|
69
|
+
The `release` command allows you to deploy updates to your app. There are two types of updates you can release:
|
|
70
|
+
|
|
71
|
+
1. Full Bundle (sending fully updated bundle)
|
|
72
|
+
2. Patch Bundle (sending only the diff)
|
|
73
|
+
|
|
74
|
+
### Command Structure
|
|
75
|
+
```bash
|
|
76
|
+
code-push-itspar release <appName> <updateContents> <targetBinaryVersion>
|
|
77
|
+
[--deploymentName <deploymentName>]
|
|
78
|
+
[--description <description>]
|
|
79
|
+
[--disabled <disabled>]
|
|
80
|
+
[--mandatory]
|
|
81
|
+
[--noDuplicateReleaseError]
|
|
82
|
+
[--rollout <rolloutPercentage>]
|
|
83
|
+
[--isPatch <true|false>] # Default false. Specify true in case sending patch bundle.
|
|
84
|
+
[--compression <'deflate' | 'brotli'>] # 'deflate' (default) or 'brotli' (better compression)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Parameters:
|
|
88
|
+
|
|
89
|
+
Required Parameters:
|
|
90
|
+
- `appName`: Name of your app (e.g., "MyApp-iOS")
|
|
91
|
+
- `updateContents`: Path to your update files (bundle/assets)
|
|
92
|
+
- `targetBinaryVersion`: App store version this update is for. Can be:
|
|
93
|
+
- Exact version: "1.0.0"
|
|
94
|
+
- Range: "^1.0.0" (compatible with 1.x.x)
|
|
95
|
+
- Wildcard: "*" (all versions)
|
|
96
|
+
|
|
97
|
+
Optional Parameters:
|
|
98
|
+
- `--deploymentName` or `-d`: Target deployment ("Staging" or "Production", defaults to "Staging")
|
|
99
|
+
- `--description` or `-des`: Release notes or changelog
|
|
100
|
+
- `--disabled`: Prevents update from being downloaded (useful for staged rollouts)
|
|
101
|
+
- `--mandatory`: Forces users to accept this update
|
|
102
|
+
- `--noDuplicateReleaseError`: Shows warning instead of error if releasing same content
|
|
103
|
+
- `--rollout`: Percentage of users who should receive this update (1-100)
|
|
104
|
+
- `--isPatch`: Whether this is a patch update
|
|
105
|
+
- `false` (default): Full bundle update
|
|
106
|
+
- `true`: Patch update (requires patch bundle)
|
|
107
|
+
- `--compression`: Compression algorithm to use
|
|
108
|
+
- `deflate` (default): Standard compression
|
|
109
|
+
- `brotli`: Better compression, smaller bundle size
|
|
110
|
+
|
|
111
|
+
### Full Bundle Release
|
|
112
|
+
Release a complete new bundle:
|
|
113
|
+
```bash
|
|
114
|
+
# Release to staging with deflate compression (default)
|
|
115
|
+
code-push-itspar release MyApp-iOS ./codepush 1.0.0 \
|
|
116
|
+
--deploymentName Staging \
|
|
117
|
+
--description "New features" \
|
|
118
|
+
--isPatch false
|
|
119
|
+
|
|
120
|
+
# Release with brotli compression (better compression)
|
|
121
|
+
code-push-itspar release MyApp-iOS ./dist/bundle "^1.0.0" \
|
|
122
|
+
--deploymentName Production \
|
|
123
|
+
--mandatory \
|
|
124
|
+
--isPatch false \
|
|
125
|
+
--compression brotli
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
> Note about compression: Brotli typically achieves better compression ratios than deflate (e.g., 23.1MB → 8.14MB with Brotli vs 11.04MB with deflate).
|
|
129
|
+
|
|
130
|
+
### Patch Bundle Release
|
|
131
|
+
For smaller updates, first create a patch and then release it:
|
|
132
|
+
|
|
133
|
+
1. Create patch between old and new bundles:
|
|
134
|
+
```bash
|
|
135
|
+
code-push-itspar create-patch \
|
|
136
|
+
./old-bundle \
|
|
137
|
+
./new-bundle \
|
|
138
|
+
./.codepush/patches
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
2. Release the patch:
|
|
142
|
+
```bash
|
|
143
|
+
# Release patch with brotli compression
|
|
144
|
+
code-push-itspar release MyApp-iOS ./.codeupush/patches "1.0.0" \
|
|
145
|
+
--deploymentName Staging \
|
|
146
|
+
--description "Bug fixes" \
|
|
147
|
+
--isPatch true \
|
|
148
|
+
--compression brotli
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
> Note about patches: Patch updates significantly reduce the update size as they only contain the changes between versions. Always use `--isPatch true` when releasing a patch bundle.
|
|
152
|
+
|
|
153
|
+
_Note: Make sure to upload assets alongwith patch bundle._
|
|
154
|
+
|
|
155
|
+
For more details about the binary diff implementation, see [bsdiff/README.md](./bsdiff/README.md).
|
|
156
|
+
|
|
157
|
+
### Promote Updates
|
|
158
|
+
After testing in staging, promote to production:
|
|
159
|
+
```bash
|
|
160
|
+
# Basic promotion
|
|
161
|
+
code-push-itspar promote MyApp-iOS Staging Production
|
|
162
|
+
|
|
163
|
+
# Promotion with options
|
|
164
|
+
code-push-itspar promote MyApp-iOS Staging Production \
|
|
165
|
+
--rollout 25 \ # Release to 25% of users
|
|
166
|
+
--description "Verified update" # Update description
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Contributing
|
|
170
|
+
|
|
171
|
+
For information about contributing to CodePush CLI, please see our [Contributing Guide](./CONTRIBUTING.md).
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
**Note:** For additional commands and advanced features, see our [Advanced Usage Guide](./CLI_REFERENCE.md).
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.AcquisitionManager = exports.AcquisitionStatus = void 0;
|
|
6
|
+
class AcquisitionStatus {
|
|
7
|
+
static DeploymentSucceeded = "DeploymentSucceeded";
|
|
8
|
+
static DeploymentFailed = "DeploymentFailed";
|
|
9
|
+
}
|
|
10
|
+
exports.AcquisitionStatus = AcquisitionStatus;
|
|
11
|
+
class AcquisitionManager {
|
|
12
|
+
_appVersion;
|
|
13
|
+
_clientUniqueId;
|
|
14
|
+
_deploymentKey;
|
|
15
|
+
_httpRequester;
|
|
16
|
+
_ignoreAppVersion;
|
|
17
|
+
_serverUrl;
|
|
18
|
+
constructor(httpRequester, configuration) {
|
|
19
|
+
this._httpRequester = httpRequester;
|
|
20
|
+
this._serverUrl = configuration.serverUrl;
|
|
21
|
+
if (this._serverUrl.slice(-1) !== "/") {
|
|
22
|
+
this._serverUrl += "/";
|
|
23
|
+
}
|
|
24
|
+
this._appVersion = configuration.appVersion;
|
|
25
|
+
this._clientUniqueId = configuration.clientUniqueId;
|
|
26
|
+
this._deploymentKey = configuration.deploymentKey;
|
|
27
|
+
this._ignoreAppVersion = configuration.ignoreAppVersion;
|
|
28
|
+
}
|
|
29
|
+
queryUpdateWithCurrentPackage(currentPackage, callback) {
|
|
30
|
+
if (!currentPackage || !currentPackage.appVersion) {
|
|
31
|
+
throw new Error("Calling common acquisition SDK with incorrect package"); // Unexpected; indicates error in our implementation
|
|
32
|
+
}
|
|
33
|
+
const updateRequest = {
|
|
34
|
+
deploymentKey: this._deploymentKey,
|
|
35
|
+
appVersion: currentPackage.appVersion,
|
|
36
|
+
packageHash: currentPackage.packageHash,
|
|
37
|
+
isCompanion: this._ignoreAppVersion,
|
|
38
|
+
label: currentPackage.label,
|
|
39
|
+
clientUniqueId: this._clientUniqueId,
|
|
40
|
+
};
|
|
41
|
+
const requestUrl = this._serverUrl + "updateCheck?" + queryStringify(updateRequest);
|
|
42
|
+
this._httpRequester.request(0 /* Http.Verb.GET */, requestUrl, (error, response) => {
|
|
43
|
+
if (error) {
|
|
44
|
+
callback(error, /*remotePackage=*/ null);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (response.statusCode !== 200) {
|
|
48
|
+
callback(new Error(response.statusCode + ": " + response.body), /*remotePackage=*/ null);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
let updateInfo;
|
|
52
|
+
try {
|
|
53
|
+
const responseObject = JSON.parse(response.body);
|
|
54
|
+
updateInfo = responseObject.updateInfo;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
callback(error, /*remotePackage=*/ null);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (!updateInfo) {
|
|
61
|
+
callback(error, /*remotePackage=*/ null);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
else if (updateInfo.updateAppVersion) {
|
|
65
|
+
callback(/*error=*/ null, {
|
|
66
|
+
updateAppVersion: true,
|
|
67
|
+
appVersion: updateInfo.appVersion,
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
else if (!updateInfo.isAvailable) {
|
|
72
|
+
callback(/*error=*/ null, /*remotePackage=*/ null);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const remotePackage = {
|
|
76
|
+
deploymentKey: this._deploymentKey,
|
|
77
|
+
description: updateInfo.description,
|
|
78
|
+
label: updateInfo.label,
|
|
79
|
+
appVersion: updateInfo.appVersion,
|
|
80
|
+
isMandatory: updateInfo.isMandatory,
|
|
81
|
+
packageHash: updateInfo.packageHash,
|
|
82
|
+
packageSize: updateInfo.packageSize,
|
|
83
|
+
downloadUrl: updateInfo.downloadURL,
|
|
84
|
+
};
|
|
85
|
+
callback(/*error=*/ null, remotePackage);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
reportStatusDeploy(deployedPackage, status, previousLabelOrAppVersion, previousDeploymentKey, callback) {
|
|
89
|
+
const url = this._serverUrl + "reportStatus/deploy";
|
|
90
|
+
const body = {
|
|
91
|
+
appVersion: this._appVersion,
|
|
92
|
+
deploymentKey: this._deploymentKey,
|
|
93
|
+
};
|
|
94
|
+
if (this._clientUniqueId) {
|
|
95
|
+
body.clientUniqueId = this._clientUniqueId;
|
|
96
|
+
}
|
|
97
|
+
if (deployedPackage) {
|
|
98
|
+
body.label = deployedPackage.label;
|
|
99
|
+
body.appVersion = deployedPackage.appVersion;
|
|
100
|
+
switch (status) {
|
|
101
|
+
case AcquisitionStatus.DeploymentSucceeded:
|
|
102
|
+
case AcquisitionStatus.DeploymentFailed:
|
|
103
|
+
body.status = status;
|
|
104
|
+
break;
|
|
105
|
+
default:
|
|
106
|
+
if (callback) {
|
|
107
|
+
if (!status) {
|
|
108
|
+
callback(new Error("Missing status argument."), /*not used*/ null);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
callback(new Error('Unrecognized status "' + status + '".'), /*not used*/ null);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (previousLabelOrAppVersion) {
|
|
118
|
+
body.previousLabelOrAppVersion = previousLabelOrAppVersion;
|
|
119
|
+
}
|
|
120
|
+
if (previousDeploymentKey) {
|
|
121
|
+
body.previousDeploymentKey = previousDeploymentKey;
|
|
122
|
+
}
|
|
123
|
+
callback = typeof arguments[arguments.length - 1] === "function" && arguments[arguments.length - 1];
|
|
124
|
+
this._httpRequester.request(2 /* Http.Verb.POST */, url, JSON.stringify(body), (error, response) => {
|
|
125
|
+
if (callback) {
|
|
126
|
+
if (error) {
|
|
127
|
+
callback(error, /*not used*/ null);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (response.statusCode !== 200) {
|
|
131
|
+
callback(new Error(response.statusCode + ": " + response.body), /*not used*/ null);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
callback(/*error*/ null, /*not used*/ null);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
reportStatusDownload(downloadedPackage, callback) {
|
|
139
|
+
const url = this._serverUrl + "reportStatus/download";
|
|
140
|
+
const body = {
|
|
141
|
+
clientUniqueId: this._clientUniqueId,
|
|
142
|
+
deploymentKey: this._deploymentKey,
|
|
143
|
+
label: downloadedPackage.label,
|
|
144
|
+
};
|
|
145
|
+
this._httpRequester.request(2 /* Http.Verb.POST */, url, JSON.stringify(body), (error, response) => {
|
|
146
|
+
if (callback) {
|
|
147
|
+
if (error) {
|
|
148
|
+
callback(error, /*not used*/ null);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (response.statusCode !== 200) {
|
|
152
|
+
callback(new Error(response.statusCode + ": " + response.body), /*not used*/ null);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
callback(/*error*/ null, /*not used*/ null);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
exports.AcquisitionManager = AcquisitionManager;
|
|
161
|
+
function queryStringify(object) {
|
|
162
|
+
let queryString = "";
|
|
163
|
+
let isFirst = true;
|
|
164
|
+
for (const property in object) {
|
|
165
|
+
if (object.hasOwnProperty(property)) {
|
|
166
|
+
const value = object[property];
|
|
167
|
+
if (!isFirst) {
|
|
168
|
+
queryString += "&";
|
|
169
|
+
}
|
|
170
|
+
queryString += encodeURIComponent(property) + "=";
|
|
171
|
+
if (value !== null && typeof value !== "undefined") {
|
|
172
|
+
queryString += encodeURIComponent(value);
|
|
173
|
+
}
|
|
174
|
+
isFirst = false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return queryString;
|
|
178
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// Copyright (c) Microsoft Corporation.
|
|
4
|
+
// Licensed under the MIT License.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const parser = require("./command-parser");
|
|
7
|
+
const execute = require("./command-executor");
|
|
8
|
+
const chalk = require("chalk");
|
|
9
|
+
function run() {
|
|
10
|
+
const command = parser.createCommand();
|
|
11
|
+
if (!command) {
|
|
12
|
+
parser.showHelp(/*showRootDescription*/ false);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
execute
|
|
16
|
+
.execute(command)
|
|
17
|
+
.catch((error) => {
|
|
18
|
+
console.error(chalk.red(`[Error] ${error.message}`));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
})
|
|
21
|
+
.done();
|
|
22
|
+
}
|
|
23
|
+
run();
|