@easyrn/erpush-cli 0.0.1-alpha
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.
Potentially problematic release.
This version of @easyrn/erpush-cli might be problematic. Click here for more details.
- package/cli.js +2 -0
- package/index.js +29 -0
- package/package.json +26 -0
- package/src/api.js +294 -0
- package/src/commands.js +281 -0
- package/src/exitsig.js +96 -0
- package/src/pack.js +325 -0
- package/src/patch.js +288 -0
- package/src/shell.js +251 -0
- package/src/utils.js +939 -0
package/cli.js
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* erpush-cli 为 erpush 提供了可用的命令行
|
|
3
|
+
* 运行 erpush 的命令, 实际调是由当前包的 cli 进行执行
|
|
4
|
+
*
|
|
5
|
+
* 导出 cli 所用的函数, 若自行开发功能, 可使用这些函数, 如
|
|
6
|
+
*
|
|
7
|
+
* import erpush from 'erpush-cli';
|
|
8
|
+
*
|
|
9
|
+
* erpush.makeBundle()
|
|
10
|
+
*
|
|
11
|
+
* 需注意: 该包的大部分函数仅可在 node cli 环境下执行
|
|
12
|
+
*/
|
|
13
|
+
const api = require('./src/api');
|
|
14
|
+
const utils = require('./src/utils');
|
|
15
|
+
const Shell = require('./src/shell');
|
|
16
|
+
const {makeApk, makeIpa, makeBundle} = require('./src/pack');
|
|
17
|
+
const {diff, diffPackage, diffBundle} = require('./src/patch');
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
api,
|
|
21
|
+
utils,
|
|
22
|
+
Shell,
|
|
23
|
+
makeApk,
|
|
24
|
+
makeIpa,
|
|
25
|
+
makeBundle,
|
|
26
|
+
diff,
|
|
27
|
+
diffPackage,
|
|
28
|
+
diffBundle,
|
|
29
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@easyrn/erpush-cli",
|
|
3
|
+
"version": "0.0.1-alpha",
|
|
4
|
+
"description": "easyrn push cli",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"author": "malacca",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"react-native",
|
|
10
|
+
"erpush"
|
|
11
|
+
],
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"yazl": "^2.5.1",
|
|
14
|
+
"yauzl": "^2.10.0",
|
|
15
|
+
"wcwidth": "^1.0.1",
|
|
16
|
+
"minimist": "^1.2.7",
|
|
17
|
+
"form-data": "^3.0.0",
|
|
18
|
+
"node-fetch": "^2.6.7",
|
|
19
|
+
"tough-cookie": "^4.0.0"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/easyrn/archives"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/easyrn/archives/tree/master/erpush-cli"
|
|
26
|
+
}
|
package/src/api.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const {fileExist, getConfig, getAppId, requestAPI} = require('./utils');
|
|
3
|
+
|
|
4
|
+
class CommandAPI {
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 初始化时指明运行目录, 会在该目录下自动寻找 RN 项目的相关配置
|
|
8
|
+
* @param string cwd
|
|
9
|
+
*/
|
|
10
|
+
constructor(cwd){
|
|
11
|
+
this.cwd = cwd;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 所有 API 接口皆通过此方法发出请求,返回结果格式必须为: {code:int, message:string}
|
|
16
|
+
* code==0: 服务端处理成功, 有些接口还需要其他数据
|
|
17
|
+
* code!=0: 服务端处理异常, message 为异常信息
|
|
18
|
+
*/
|
|
19
|
+
async request(uri, payload, asForm){
|
|
20
|
+
const res = await requestAPI(this.cwd, uri, payload, asForm);
|
|
21
|
+
return await res.json();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* API : 登陆账号, 服务端应在 response header 中返回 set-cookie
|
|
26
|
+
* 后续其他请求都会附带该 cookie, 服务端可以此来鉴权
|
|
27
|
+
* POST: /login {user:string, pwd:string}
|
|
28
|
+
* RES : {code:int, message:string}
|
|
29
|
+
*/
|
|
30
|
+
async login(user, pwd){
|
|
31
|
+
const res = await this.request('/login', {user, pwd});
|
|
32
|
+
if (res.code === 0 && !('message' in res)){
|
|
33
|
+
res.message = 'Logged in as ' + user;
|
|
34
|
+
}
|
|
35
|
+
return res;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* API : 查询当前登陆账号
|
|
40
|
+
* POST: /whoami {}
|
|
41
|
+
* RES : {code:int, message:string}
|
|
42
|
+
*/
|
|
43
|
+
async whoami(){
|
|
44
|
+
return this.request('/whoami', {});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* API : 列出所有 App 信息, 参数 platform 可能为空, 此时应返回所有平台的 App
|
|
49
|
+
* POST: /app/list {platform:string|''}
|
|
50
|
+
* RES : {code:int, message:string, data:Array<DataItem>}
|
|
51
|
+
* DataItem: {
|
|
52
|
+
* id:number|string,
|
|
53
|
+
* name:string,
|
|
54
|
+
* version:string,
|
|
55
|
+
* android:string, // apk url, 若不是 android app, 为空或null即可
|
|
56
|
+
* android_md5:string, // 若是 android app, 返回 apk 文件的 md5 值
|
|
57
|
+
* ios:string, // ipa url, 若不是 ios app, 为空或null即可
|
|
58
|
+
* ios_md5:string, // 若是 ios app, 返回 iap 文件的 md5 值
|
|
59
|
+
* create_at:string,
|
|
60
|
+
* }
|
|
61
|
+
*/
|
|
62
|
+
async listApp(platform){
|
|
63
|
+
platform = platform||null;
|
|
64
|
+
//const list = await this.request('/app/list', {platform})
|
|
65
|
+
const list = {
|
|
66
|
+
code:0,
|
|
67
|
+
message:'a',
|
|
68
|
+
data:[
|
|
69
|
+
{
|
|
70
|
+
id:'333',
|
|
71
|
+
name:'测试版',
|
|
72
|
+
version:'1.0',
|
|
73
|
+
android:'a',
|
|
74
|
+
android_md5:'ddddd',
|
|
75
|
+
ios:'cccc',
|
|
76
|
+
ios_md5:'dddd',
|
|
77
|
+
create_at:'2012-12-09 08:30',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id:'333',
|
|
81
|
+
name:'测试版',
|
|
82
|
+
version:'1.0',
|
|
83
|
+
android:'a',
|
|
84
|
+
android_md5:'ddddd',
|
|
85
|
+
ios:'cccc',
|
|
86
|
+
ios_md5:'dddd',
|
|
87
|
+
create_at:'2012-12-09 08:30',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id:'333',
|
|
91
|
+
name:'测试版',
|
|
92
|
+
version:'1.0',
|
|
93
|
+
android:'a',
|
|
94
|
+
android_md5:'ddddd',
|
|
95
|
+
ios:'cccc',
|
|
96
|
+
ios_md5:'dddd',
|
|
97
|
+
create_at:'2012-12-09 08:30',
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
if (typeof list.code !== 'number' || list.code !== 0 || !Array.isArray(list.data) || !list.data.length) {
|
|
102
|
+
return list;
|
|
103
|
+
}
|
|
104
|
+
const {android, ios} = getConfig(this.cwd);
|
|
105
|
+
list.data = list.data.map(item => {
|
|
106
|
+
item.ios_current = Boolean(ios && ios == item.id);
|
|
107
|
+
item.android_current = Boolean(android && android == item.id);
|
|
108
|
+
return item;
|
|
109
|
+
});
|
|
110
|
+
return list;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* API : 新增 App
|
|
115
|
+
* POST: /app/add {name, version}
|
|
116
|
+
* RES : {code:int, message:string}
|
|
117
|
+
*/
|
|
118
|
+
async addApp(name, version){
|
|
119
|
+
return await this.request('/app/add', {name, version});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* API : 上传安装包到指定的 app
|
|
124
|
+
* POST: /app/upload FormData:{id, platform:string, package:File}
|
|
125
|
+
* RES : {code:int, message:string}
|
|
126
|
+
*/
|
|
127
|
+
async uploadApp(platform, packagePath, appId){
|
|
128
|
+
// 未指定 appId, 尝试自动获取当前项目绑定的 id
|
|
129
|
+
const {code, message} = getAppId(this.cwd, platform, appId);
|
|
130
|
+
if (code !== 0) {
|
|
131
|
+
return {code, message};
|
|
132
|
+
}
|
|
133
|
+
if (!fileExist(packagePath)) {
|
|
134
|
+
return {code:-2, message:'package not exist'}
|
|
135
|
+
}
|
|
136
|
+
return await this.request(
|
|
137
|
+
'/app/upload',
|
|
138
|
+
{id:message, platform, package:fs.createReadStream(packagePath)},
|
|
139
|
+
true
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 列出指定 app 的所有热更版本 (
|
|
145
|
+
* POST: /patch/list {id, platform}
|
|
146
|
+
* RES : {code:int, message:string, data:Array<PatchItem>}
|
|
147
|
+
* PatchItem: {
|
|
148
|
+
* id:string,
|
|
149
|
+
* version:string,
|
|
150
|
+
* desc:string,
|
|
151
|
+
* active:boolean, // 是否为已启用的热更版本
|
|
152
|
+
* bundle:string, // 全量 bundle 包的 url
|
|
153
|
+
* bundle_md5:string, // bundle 包文件的 md5 值
|
|
154
|
+
* create_at:string,
|
|
155
|
+
* }
|
|
156
|
+
* 备注: 返回 data 中的 bundle 字段返回该热更版本的完整包下载地址
|
|
157
|
+
* patch_md5 为包文件的 md5 hash 值
|
|
158
|
+
*/
|
|
159
|
+
async listPatch(platform, appId){
|
|
160
|
+
// 未指定 appId, 尝试自动获取当前项目绑定的 id
|
|
161
|
+
const {code, message} = getAppId(this.cwd, platform, appId);
|
|
162
|
+
if (code !== 0) {
|
|
163
|
+
return {code, message};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
code:0,
|
|
167
|
+
message:'a',
|
|
168
|
+
data:[
|
|
169
|
+
{
|
|
170
|
+
id:'333',
|
|
171
|
+
version:'1.0',
|
|
172
|
+
desc:'测试版',
|
|
173
|
+
bundle:'a',
|
|
174
|
+
bundle_md5:'ddddd',
|
|
175
|
+
create_at:'2012-12-09 08:30',
|
|
176
|
+
active:false
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id:'333',
|
|
180
|
+
version:'1.0',
|
|
181
|
+
desc:'测试版',
|
|
182
|
+
bundle:'a',
|
|
183
|
+
bundle_md5:'ddddd',
|
|
184
|
+
create_at:'2012-12-09 08:30',
|
|
185
|
+
active:true
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id:'333',
|
|
189
|
+
version:'1.0',
|
|
190
|
+
desc:'测试版',
|
|
191
|
+
bundle:'a',
|
|
192
|
+
bundle_md5:'ddddd',
|
|
193
|
+
create_at:'2012-12-09 08:30',
|
|
194
|
+
active:false
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id:'333',
|
|
198
|
+
version:'1.0',
|
|
199
|
+
desc:'测试版',
|
|
200
|
+
bundle:'a',
|
|
201
|
+
bundle_md5:'ddddd',
|
|
202
|
+
create_at:'2012-12-09 08:30',
|
|
203
|
+
active:false
|
|
204
|
+
},
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
return await this.request('/patch/list', {id:message, platform});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* API : 上传热更包
|
|
212
|
+
* POST: /patch/upload FormData:{id, platform:string, package:File}
|
|
213
|
+
* RES : {code:int, message:string}
|
|
214
|
+
*/
|
|
215
|
+
async uploadApp(platform, packagePath, appId){
|
|
216
|
+
// 未指定 appId, 尝试自动获取当前项目绑定的 id
|
|
217
|
+
const {code, message} = getAppId(this.cwd, platform, appId);
|
|
218
|
+
if (code !== 0) {
|
|
219
|
+
return {code, message};
|
|
220
|
+
}
|
|
221
|
+
if (!fileExist(packagePath)) {
|
|
222
|
+
return {code:-2, message:'package not exist'}
|
|
223
|
+
}
|
|
224
|
+
return await this.request(
|
|
225
|
+
'/app/upload',
|
|
226
|
+
{id:message, platform, package:fs.createReadStream(packagePath)},
|
|
227
|
+
true
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
// /**
|
|
239
|
+
// * 上传新的更新补丁包
|
|
240
|
+
// * POST: /patch/upload formData:{app:string, platform:string, version:string, bundle:File, patch:File, origin:string||null, diff:File||null}
|
|
241
|
+
// * RES : {code:int, message:string}
|
|
242
|
+
// * 备注: post 发送三个文件
|
|
243
|
+
// * 1. bundle 为完整包
|
|
244
|
+
// * 2. patch 为相对于安装包的补丁
|
|
245
|
+
// * 3. diff 为相对于 origin bundle 的补丁
|
|
246
|
+
// * 4. origin 是 diff 相对应的 bundel id
|
|
247
|
+
// */
|
|
248
|
+
// async uploadPatch(appId, platform, version, bundle, patch, origin, diff){
|
|
249
|
+
// if (!fileExist(bundle)) {
|
|
250
|
+
// return {code:-2, message:'bundle file not exist'}
|
|
251
|
+
// }
|
|
252
|
+
// if (!fileExist(patch)) {
|
|
253
|
+
// return {code:-2, message:'patch file not exist'}
|
|
254
|
+
// }
|
|
255
|
+
// if (diff && !origin) {
|
|
256
|
+
// return {code:-2, message:'origin id not defined'}
|
|
257
|
+
// } else if (origin) {
|
|
258
|
+
// if (!diff) {
|
|
259
|
+
// return {code:-2, message:'diff file not defined'}
|
|
260
|
+
// } else if (!fileExist(diff)) {
|
|
261
|
+
// return {code:-2, message:'diff file not exist'}
|
|
262
|
+
// }
|
|
263
|
+
// }
|
|
264
|
+
// const payload = {
|
|
265
|
+
// app:appId,
|
|
266
|
+
// platform,
|
|
267
|
+
// version,
|
|
268
|
+
// bundle: fs.createReadStream(bundle),
|
|
269
|
+
// patch: fs.createReadStream(patch)
|
|
270
|
+
// };
|
|
271
|
+
// if (origin) {
|
|
272
|
+
// payload.origin = origin;
|
|
273
|
+
// payload.diff = fs.createReadStream(diff);
|
|
274
|
+
// }
|
|
275
|
+
// return await sendRequest(cwd, '/patch/upload', payload, true);
|
|
276
|
+
// }
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
// /**
|
|
280
|
+
// * 绑定 bundle, 即设定当前为指定的 热更版本
|
|
281
|
+
// * POST: /patch/bind {app, platform, id}
|
|
282
|
+
// * RES : {code:int, message:string}
|
|
283
|
+
// */
|
|
284
|
+
// async bindPatch(platform, id, appId){
|
|
285
|
+
// const {code, message} = getCurrentAppId(cwd, platform, appId);
|
|
286
|
+
// if (code !== 0) {
|
|
287
|
+
// return {code, message};
|
|
288
|
+
// }
|
|
289
|
+
// appId = message;
|
|
290
|
+
// return await sendRequest(cwd, '/patch/bind', {app:appId, platform, id});
|
|
291
|
+
// }
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
module.exports = CommandAPI;
|
package/src/commands.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const Shell = require('./shell');
|
|
3
|
+
const CommandAPI = require('./api');
|
|
4
|
+
const {supportPlatforms, color, makeSpinner, parseProcess, setConfig} = require('./utils');
|
|
5
|
+
|
|
6
|
+
// 当前命令的 cwd api cmd{name args options} interact
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
const api = new CommandAPI(cwd);
|
|
9
|
+
const cmd = parseProcess(process);
|
|
10
|
+
const interact = new Shell(process);
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Commands {
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
// 初始化, 配置 baseUrl
|
|
19
|
+
$init = 'base_url';
|
|
20
|
+
async init(){
|
|
21
|
+
let baseUrl = cmd.args.length ? cmd.args[0] : null;
|
|
22
|
+
baseUrl = baseUrl||await interact.ask("base url:");
|
|
23
|
+
return interact.showResult({code:0, message:'saved to: ' + path.basename(setConfig(cwd, {baseUrl}))})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
// Manage Account ===================================================================================
|
|
43
|
+
|
|
44
|
+
// 初始化, 配置 baseUrl
|
|
45
|
+
init.options = 'base_url';
|
|
46
|
+
async function init() {
|
|
47
|
+
let baseUrl = cmd.args.length ? cmd.args[0] : null;
|
|
48
|
+
baseUrl = baseUrl||await interact.ask("base url:");
|
|
49
|
+
return interact.showResult({code:0, message:'saved to: ' + path.basename(setConfig(cwd, {baseUrl}))})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 登陆
|
|
53
|
+
login.options = '--user user --pwd pwd';
|
|
54
|
+
async function login() {
|
|
55
|
+
let {user, pwd} = cmd.options;
|
|
56
|
+
user = user || await interact.ask("username:");
|
|
57
|
+
pwd = pwd || await interact.ask("password:");
|
|
58
|
+
return interact.showResult(await api.login(user, pwd))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 查看当前登陆的账号
|
|
62
|
+
async function whoami() {
|
|
63
|
+
return interact.showResult(await api.whoami())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Manage App ===================================================================================
|
|
67
|
+
|
|
68
|
+
// 列出所有 app
|
|
69
|
+
app_list.options = '[--platform platform]';
|
|
70
|
+
async function app_list() {
|
|
71
|
+
const list = await api.listApp(await interact.getPlatform(cmd.options.platform));
|
|
72
|
+
if (interact.showError(list)) {
|
|
73
|
+
return list;
|
|
74
|
+
}
|
|
75
|
+
const {data=[]} = list;
|
|
76
|
+
const table = data.map((item, index) => {
|
|
77
|
+
const row = {id:index, name:item.name, version:item.version, create_at:item.create_at};
|
|
78
|
+
row.ios = (item.ios && item.ios_md5 ? 'Y' : 'N');
|
|
79
|
+
if (item.ios_current) {
|
|
80
|
+
row.ios = color(row.ios + '*', 36, true)
|
|
81
|
+
}
|
|
82
|
+
row.android = (item.android && item.android_md5 ? 'Y' : 'N');
|
|
83
|
+
if (item.android_current) {
|
|
84
|
+
row.android = color(row.android + '*', 36, true)
|
|
85
|
+
}
|
|
86
|
+
return row;
|
|
87
|
+
});
|
|
88
|
+
interact.showTable(table);
|
|
89
|
+
return list;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 新增 app
|
|
93
|
+
app_add.options = '--name name --version version';
|
|
94
|
+
async function app_add() {
|
|
95
|
+
let {name, version} = cmd.options;
|
|
96
|
+
name = name || await interact.ask("app name:");
|
|
97
|
+
version = version || await interact.ask("app version:");
|
|
98
|
+
return interact.showResult(await api.addApp(name, version))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 将当前项目与服务端指定的 app 绑定
|
|
102
|
+
app_bind.options = '--platform platform';
|
|
103
|
+
async function app_bind() {
|
|
104
|
+
const list = await app_list();
|
|
105
|
+
const {data=[]} = list;
|
|
106
|
+
const platform = await interact.getPlatform(cmd.options.platform, true);
|
|
107
|
+
const id = await interact.getRealId("app id:", data);
|
|
108
|
+
return interact.showResult(supportPlatforms.includes(platform)
|
|
109
|
+
? {code:0, message:'saved to: ' + path.basename(setConfig(cwd, {[platform]:id}))}
|
|
110
|
+
: {code:-1, message:'platform not support'}
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 上传 app 包
|
|
115
|
+
app_upload.options = '--platform platform --package package_path';
|
|
116
|
+
async function app_upload() {
|
|
117
|
+
let {platform, package:pack} = cmd.options;
|
|
118
|
+
platform = await interact.getPlatform(platform, true);
|
|
119
|
+
pack = pack || await interact.askPath("package path:", cwd);
|
|
120
|
+
const spinner = makeSpinner({
|
|
121
|
+
text:'uploading...',
|
|
122
|
+
stream: process.stdout
|
|
123
|
+
}).start();
|
|
124
|
+
const rs = await api.uploadApp(platform, path.join(cwd, pack));
|
|
125
|
+
spinner.stop();
|
|
126
|
+
return interact.showResult(rs)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Manage Patch ===================================================================================
|
|
130
|
+
|
|
131
|
+
// 列出所有 patch
|
|
132
|
+
patch_list.options = '--platform platform';
|
|
133
|
+
async function patch_list() {
|
|
134
|
+
const list = await api.listPatch(await interact.getPlatform(cmd.options.platform, true));
|
|
135
|
+
if (interact.showError(list)) {
|
|
136
|
+
return list;
|
|
137
|
+
}
|
|
138
|
+
const {data=[]} = list;
|
|
139
|
+
const table = data.map((item, index) => {
|
|
140
|
+
const row = {id:index, version:item.version, create_at:item.create_at};
|
|
141
|
+
row.active = (item.active ? color('Y', 36, true) : 'N');
|
|
142
|
+
row.desc = item.desc ? item.desc.substr(0, 20) + '...' : '';
|
|
143
|
+
return row;
|
|
144
|
+
});
|
|
145
|
+
interact.showTable(table);
|
|
146
|
+
return list;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 新增 patch 包
|
|
150
|
+
patch_upload.options = '--platform platform [--bundle bundle_path]';
|
|
151
|
+
async function patch_upload() {
|
|
152
|
+
let {platform, version, bundle} = cmd.options;
|
|
153
|
+
platform = await interact.getPlatform(platform, true);
|
|
154
|
+
if (!bundle) {
|
|
155
|
+
interact.output("\n You did not define bundle path\n")
|
|
156
|
+
const go = (await interact.ask('create new bundle? [Y|n|e]:')).toLowerCase();
|
|
157
|
+
if (go === 'e') {
|
|
158
|
+
process.exit();
|
|
159
|
+
}
|
|
160
|
+
if (go === 'n') {
|
|
161
|
+
bundle = await interact.askPath('set bundle path:');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return interact.showResult(await api.makeAndUploadPatch(cwd, platform, bundle, version, process.stdout, process.stderr))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 绑定 patch
|
|
168
|
+
patch_bind.options = '--platform platform';
|
|
169
|
+
async function patch_bind() {
|
|
170
|
+
let {platform} = cmd.options;
|
|
171
|
+
platform = await getPlatform(platform, true);
|
|
172
|
+
const list = await patch_list_table(platform);
|
|
173
|
+
const {data=[]} = list;
|
|
174
|
+
const id = await getRealId(data, "patch id:");
|
|
175
|
+
return showApiRs(await api.bindPatch(cwd, platform, id))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Build Tool ===================================================================================
|
|
179
|
+
|
|
180
|
+
// 生成 android release apk 包
|
|
181
|
+
apk.options = '[--target target --output save_path]';
|
|
182
|
+
async function apk() {
|
|
183
|
+
return require('./pack').makeApk(cwd, packOptions(), process.stdout, process.stderr);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 生成 ios release ipa 包
|
|
187
|
+
ipa.options = '[--target target --output save_path]';
|
|
188
|
+
async function ipa() {
|
|
189
|
+
return require('./pack').makeIpa(cwd, packOptions(true), process.stdout, process.stderr);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 生成 bundle 包
|
|
193
|
+
bundle.options = '--platform platform [--output save_dir --save-name save_name]';
|
|
194
|
+
async function bundle() {
|
|
195
|
+
const options = cmd.options;
|
|
196
|
+
options.platform = await interact.getPlatform(options.platform, true);
|
|
197
|
+
return await require('./pack').makeBundle(cwd, options, process.stdout, process.stderr);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 生成两个文件的 diff patch
|
|
201
|
+
diff.options = 'origin_file new_file [--output save_name]';
|
|
202
|
+
async function diff() {
|
|
203
|
+
return require('./patch').diff(cwd, await diffOptions('file'), process.stdout, process.stderr)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 生成 bundle 相对于 apk 的补丁包
|
|
207
|
+
diffapk.options = 'origin_apk new_bundle [--output save_name]';
|
|
208
|
+
async function diffapk() {
|
|
209
|
+
return require('./patch').diffPackage(cwd, await diffOptions('apk'), process.stdout, process.stderr, false)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 生成 bundle 相对于 ipa 的补丁包
|
|
213
|
+
diffipa.options = 'origin_ipa new_bundle [--output save_name]';
|
|
214
|
+
async function diffipa() {
|
|
215
|
+
return require('./patch').diffPackage(cwd, await diffOptions('ipa'), process.stdout, process.stderr, true)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 生成 bundle 相对于 bundle 的补丁包
|
|
219
|
+
diffbundle.options = 'origin_bundle new_bundle [--output save_name]';
|
|
220
|
+
async function diffbundle() {
|
|
221
|
+
return require('./patch').diffBundle(cwd, await diffOptions('bundle'), process.stdout, process.stderr)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 获取 apk/ipa 命令 options
|
|
225
|
+
function packOptions(ipa) {
|
|
226
|
+
const {target='release', output, ...options} = cmd.options||{};
|
|
227
|
+
options.target = target;
|
|
228
|
+
if (!output) {
|
|
229
|
+
options.output = 'build/output/app-' + target + '.' + (ipa ? 'ipa' : 'apk');
|
|
230
|
+
}
|
|
231
|
+
return options;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 获取 diff 命令 options
|
|
235
|
+
async function diffOptions(type) {
|
|
236
|
+
let [origin, next] = cmd.args;
|
|
237
|
+
const output = cmd.options.output;
|
|
238
|
+
origin = origin || await interact.askPath(`origin ${type} path:`, cwd);
|
|
239
|
+
next = next || await interact.askPath(type === 'file' ? 'new file path:' : 'new bundle path:', cwd);
|
|
240
|
+
return {origin, next, output, cmd:true};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
// exports ===================================================================================
|
|
255
|
+
|
|
256
|
+
module.exports = interact.getCommandsExecuter([
|
|
257
|
+
'Manage Account',
|
|
258
|
+
init,
|
|
259
|
+
login,
|
|
260
|
+
whoami,
|
|
261
|
+
|
|
262
|
+
'Manage App',
|
|
263
|
+
[app_list],
|
|
264
|
+
app_add,
|
|
265
|
+
app_bind,
|
|
266
|
+
app_upload,
|
|
267
|
+
|
|
268
|
+
'Manage Patch',
|
|
269
|
+
[patch_list],
|
|
270
|
+
patch_upload,
|
|
271
|
+
patch_bind,
|
|
272
|
+
|
|
273
|
+
'Build Tool',
|
|
274
|
+
apk,
|
|
275
|
+
ipa,
|
|
276
|
+
bundle,
|
|
277
|
+
diff,
|
|
278
|
+
diffapk,
|
|
279
|
+
diffipa,
|
|
280
|
+
diffbundle,
|
|
281
|
+
], {...cmd, showLogo:true});
|