1
0
mirror of https://gitcode.com/github-mirrors/react-native-update-cli.git synced 2025-09-17 18:06:10 +08:00
Code Issues Packages Projects Releases Wiki Activity GitHub Gitee

Compare commits

..

47 Commits

Author SHA1 Message Date
sunnylqm
44834ee477 v1.20.2 2024-03-06 10:19:54 +08:00
sunnylqm
2a02deb015 v1.20.1 2024-03-06 09:55:48 +08:00
sunnylqm
8b7cb809f6 v1.20.0 2024-03-05 23:29:31 +08:00
sunnylqm
070991d08b fix: hermes path 2024-03-05 23:29:05 +08:00
sunnylqm
0d03e18c58 fix: version name 2024-03-05 23:03:10 +08:00
sunnylqm
a0dfcb5c4b v1.19.0 2024-02-18 20:04:05 +08:00
sunnylqm
587da8aaf9 Fix android crunchPngs option and add support for expo-router 2024-02-18 20:03:43 +08:00
sunnylqm
724088a810 v1.18.0 2024-02-03 21:28:38 +08:00
sunnylqm
7c20b30c85 feat: support expo 2024-02-03 21:27:47 +08:00
sunnylqm
6a053c6428 v1.17.0 2024-01-30 19:15:15 +08:00
sunnylqm
f202fc590d fix: rndir path 2024-01-30 19:15:00 +08:00
sunnylqm
0f8cf3c399 v1.16.0 2024-01-30 17:50:32 +08:00
sunnylqm
6a0d82c7a5 fix: rndir path 2024-01-30 17:50:09 +08:00
sunnylqm
eed19992d8 v1.15.0 2024-01-30 12:43:48 +08:00
sunnylqm
1b5078831c fix: require path 2024-01-30 12:43:31 +08:00
sunnylqm
bc9aff343a v1.14.0 2024-01-29 19:11:06 +08:00
sunnylqm
7aaa32a5a2 v1.14.0-beta.0 2024-01-29 19:02:01 +08:00
sunnylqm
ab7920fe38 feat: support symlink 2024-01-29 19:01:11 +08:00
sunnylqm
d912ace4a7 v1.13.0 2023-09-19 18:40:12 +08:00
sunnylqm
8af4d314ce fix appid equal 2023-09-19 18:36:26 +08:00
sunnylqm
f2d5269512 feat: support rn0.71 android hermes 2023-07-20 18:27:14 +08:00
sunnylqm
fe24c4ca36 v1.12.0 2023-07-20 18:25:47 +08:00
sunnylqm
cf61c297a6 update endpoint 2023-01-29 10:35:44 +08:00
sunnylqm
f9adc700ed v1.11.0 2023-01-11 23:36:13 +08:00
sunnylqm
dcff16cbb5 support source-map 2023-01-11 23:35:39 +08:00
sunnylqm
2bb8e83010 v1.10.0 2022-07-01 23:08:16 +08:00
sunnylqm
0cfc6e4f0d support rn 69 hermes 2022-07-01 23:02:20 +08:00
sunnylqm
da7bdbfdd2 v1.9.0 2022-06-29 09:42:05 +08:00
sunnylqm
07ee28ba3b add updateVersionInfo 2022-06-29 09:41:44 +08:00
sunnylqm
dae3e4024f v1.8.1 2021-12-24 17:55:15 +08:00
sunnylqm
d673b5736a Fix selectApp id 2021-12-24 17:53:14 +08:00
sunnylqm
732845faad Cleanup headers 2021-10-28 18:01:12 +08:00
sunnylqm
bcfdd67ea8 v1.8.0 2021-10-18 18:24:15 +08:00
sunnylqm
27ea54c1ec Update package output 2021-10-18 18:22:56 +08:00
sunnylqm
ba0fa836d1 v1.7.2 2021-10-18 18:02:43 +08:00
sunnylqm
bde76094fc v1.7.1 2021-10-18 18:00:21 +08:00
sunnylqm
f1d6c3744e v1.7.0 2021-10-18 17:54:07 +08:00
sunnylqm
768484d7b5 Add packageVersion 2021-10-18 17:53:29 +08:00
sunnylqm
d6632ffcc6 v1.7.0-beta.0 2021-10-18 17:28:01 +08:00
sunnylqm
24f5b316a9 v1.6.0 2021-10-10 12:39:29 +08:00
sunnylqm
03a4108a08 汉化 2021-10-10 12:39:00 +08:00
sunnylqm
32d7ed9b00 v1.5.0 2021-09-01 14:55:09 +08:00
sunnylqm
6f3d45c3f2 Add acc option 2021-09-01 14:54:17 +08:00
sunnylqm
25cb724921 v1.4.2 2021-06-23 10:05:55 +08:00
sunnylqm
a7b79a30e8 Print server error 2021-06-23 10:05:29 +08:00
sunnylqm
11799dd0c1 v1.4.1 2021-04-11 23:29:56 +08:00
sunnylqm
2ab0cad7e5 Detect hermes first 2021-04-11 23:29:44 +08:00
13 changed files with 3638 additions and 3186 deletions

29
LICENSE
View File

@@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2020, reactnativecn
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -80,6 +80,33 @@
}, },
"packageId": { "packageId": {
"hasValue": true "hasValue": true
},
"packageVersion": {
"hasValue": true
}
}
},
"updateVersionInfo": {
"options": {
"platform": {
"hasValue": true
},
"versionId": {
"hasValue": true
},
"name": {
"default": false,
"hasValue": true
},
"description": {
"default": false,
"hasValue": true
},
"metaInfo": {
"default": false,
"hasValue": true
} }
} }
}, },
@@ -113,7 +140,9 @@
"default": ".pushy/output/${platform}.${time}.ppk", "default": ".pushy/output/${platform}.${time}.ppk",
"hasValue": true "hasValue": true
}, },
"verbose": {} "sourcemap": {
"default": false
}
} }
}, },
"release": { "release": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-native-update-cli", "name": "react-native-update-cli",
"version": "1.4.0", "version": "1.20.2",
"description": "Command tools for javaScript updater with `pushy` service for react native apps.", "description": "Command tools for javaScript updater with `pushy` service for react native apps.",
"main": "index.js", "main": "index.js",
"bin": { "bin": {
@@ -12,7 +12,6 @@
"cli.json" "cli.json"
], ],
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"prepublish": "babel src --out-dir lib" "prepublish": "babel src --out-dir lib"
}, },
"repository": { "repository": {
@@ -32,18 +31,19 @@
}, },
"homepage": "https://github.com/reactnativecn/react-native-pushy/tree/master/react-native-pushy-cli", "homepage": "https://github.com/reactnativecn/react-native-pushy/tree/master/react-native-pushy-cli",
"dependencies": { "dependencies": {
"app-info-parser": "^1.0.0", "app-info-parser": "github:sunnylqm/app-info-parser#fix/support-ascii-versionname",
"cli-arguments": "^0.2.1", "cli-arguments": "^0.2.1",
"filesize-parser": "^1.5.0", "filesize-parser": "^1.5.0",
"fs-extra": "8", "fs-extra": "8",
"gradle-to-js": "^2.0.0", "gradle-to-js": "^2.0.1",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"progress": "^2.0.3", "progress": "^2.0.3",
"properties": "^1.2.1",
"read": "^1.0.7", "read": "^1.0.7",
"request": "^2.88.2", "request": "^2.88.2",
"tcp-ping": "^0.1.1", "tcp-ping": "^0.1.1",
"tty-table": "4.1", "tty-table": "4.2",
"update-notifier": "^4.1.1", "update-notifier": "^5.1.0",
"yauzl": "^2.10.0", "yauzl": "^2.10.0",
"yazl": "2.5.1" "yazl": "2.5.1"
}, },
@@ -55,6 +55,6 @@
"babel-plugin-transform-object-rest-spread": "^6.26.0" "babel-plugin-transform-object-rest-spread": "^6.26.0"
}, },
"engines": { "engines": {
"node": ">= 8" "node": ">= 10"
} }
} }

3253
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,13 @@
/** import fetch from 'node-fetch';
* Created by tdzl2003 on 2/13/16. const defaultEndpoint = 'https://update.reactnative.cn/api';
*/
const fetch = require('node-fetch');
const defaultEndpoint = 'http://u.reactnative.cn/api';
let host = process.env.PUSHY_REGISTRY || defaultEndpoint; let host = process.env.PUSHY_REGISTRY || defaultEndpoint;
const fs = require('fs'); import fs from 'fs';
import request from 'request'; import request from 'request';
import ProgressBar from 'progress'; import ProgressBar from 'progress';
const packageJson = require('../package.json'); import packageJson from '../package.json';
const tcpp = require('tcp-ping'); import tcpp from 'tcp-ping';
const util = require('util'); import util from 'util';
const path = require('path'); import path from 'path';
import filesizeParser from 'filesize-parser'; import filesizeParser from 'filesize-parser';
import { pricingPageUrl } from './utils'; import { pricingPageUrl } from './utils';
@@ -22,10 +18,18 @@ let savedSession = undefined;
const userAgent = `react-native-update-cli/${packageJson.version}`; const userAgent = `react-native-update-cli/${packageJson.version}`;
exports.loadSession = async function () { export const getSession = function () {
return session;
};
export const replaceSession = function (newSession) {
session = newSession;
};
export const loadSession = async function () {
if (fs.existsSync('.update')) { if (fs.existsSync('.update')) {
try { try {
exports.replaceSession(JSON.parse(fs.readFileSync('.update', 'utf8'))); replaceSession(JSON.parse(fs.readFileSync('.update', 'utf8')));
savedSession = session; savedSession = session;
} catch (e) { } catch (e) {
console.error( console.error(
@@ -36,15 +40,7 @@ exports.loadSession = async function () {
} }
}; };
exports.getSession = function () { export const saveSession = function () {
return session;
};
exports.replaceSession = function (newSession) {
session = newSession;
};
exports.saveSession = function () {
// Only save on change. // Only save on change.
if (session !== savedSession) { if (session !== savedSession) {
const current = session; const current = session;
@@ -54,7 +50,7 @@ exports.saveSession = function () {
} }
}; };
exports.closeSession = function () { export const closeSession = function () {
if (fs.existsSync('.update')) { if (fs.existsSync('.update')) {
fs.unlinkSync('.update'); fs.unlinkSync('.update');
savedSession = undefined; savedSession = undefined;
@@ -65,7 +61,14 @@ exports.closeSession = function () {
async function query(url, options) { async function query(url, options) {
const resp = await fetch(url, options); const resp = await fetch(url, options);
const json = await resp.json(); const text = await resp.text();
let json;
try {
json = JSON.parse(text);
} catch (e) {
throw new Error(`Server error: ${text}`);
}
if (resp.status !== 200) { if (resp.status !== 200) {
throw Object.assign(new Error(json.message || json.error), { throw Object.assign(new Error(json.message || json.error), {
status: resp.status, status: resp.status,
@@ -100,26 +103,30 @@ function queryWithBody(method) {
}; };
} }
exports.get = queryWithoutBody('GET'); export const get = queryWithoutBody('GET');
exports.post = queryWithBody('POST'); export const post = queryWithBody('POST');
exports.put = queryWithBody('PUT'); export const put = queryWithBody('PUT');
exports.doDelete = queryWithBody('DELETE'); export const doDelete = queryWithBody('DELETE');
async function uploadFile(fn, key) { export async function uploadFile(fn, key) {
const { url, backupUrl, formData, maxSize } = await exports.post('/upload', { const { url, backupUrl, formData, maxSize } = await post('/upload', {
ext: path.extname(fn), ext: path.extname(fn),
}); });
let realUrl = url; let realUrl = url;
if (backupUrl) { if (backupUrl) {
const pingResult = await tcpPing({ if (global.USE_ACC_OSS) {
address: url.replace('https://', ''),
attempts: 4,
timeout: 1000,
});
// console.log({pingResult});
if (isNaN(pingResult.avg) || pingResult.avg > 150) {
realUrl = backupUrl; realUrl = backupUrl;
} else {
const pingResult = await tcpPing({
address: url.replace('https://', ''),
attempts: 4,
timeout: 1000,
});
// console.log({pingResult});
if (isNaN(pingResult.avg) || pingResult.avg > 150) {
realUrl = backupUrl;
}
} }
// console.log({realUrl}); // console.log({realUrl});
} }
@@ -129,11 +136,11 @@ async function uploadFile(fn, key) {
throw new Error( throw new Error(
`此文件大小${(fileSize / 1048576).toFixed( `此文件大小${(fileSize / 1048576).toFixed(
1, 1,
)}m, 超出当前额度${maxSize}。您可以考虑升级付费业务以提升此额度。详情请访问${pricingPageUrl}`, )}m, 超出当前额度${maxSize}。您可以考虑升级付费业务以提升此额度。详情请访问: ${pricingPageUrl}`,
); );
} }
const bar = new ProgressBar(' Uploading [:bar] :percent :etas', { const bar = new ProgressBar(' 上传中 [:bar] :percent :etas', {
complete: '=', complete: '=',
incomplete: ' ', incomplete: ' ',
total: fileSize, total: fileSize,
@@ -152,10 +159,6 @@ async function uploadFile(fn, key) {
realUrl, realUrl,
{ {
formData, formData,
headers: {
'User-Agent': userAgent,
'X-AccessToken': session ? session.token : '',
},
}, },
(err, resp, body) => { (err, resp, body) => {
if (err) { if (err) {
@@ -166,17 +169,9 @@ async function uploadFile(fn, key) {
Object.assign(new Error(body), { status: resp.statusCode }), Object.assign(new Error(body), { status: resp.statusCode }),
); );
} }
resolve( resolve({ hash: formData.key });
body
? // qiniu
JSON.parse(body)
: // aliyun oss
{ hash: formData.key },
);
}, },
); );
}); });
return info; return info;
} }
exports.uploadFile = uploadFile;

View File

@@ -1,12 +1,8 @@
/**
* Created by tdzl2003 on 2/13/16.
*/
import { question } from './utils'; import { question } from './utils';
import fs from 'fs'; import fs from 'fs';
const Table = require('tty-table'); import Table from 'tty-table';
const { post, get, doDelete } = require('./api'); import { post, get, doDelete } from './api';
const validPlatforms = { const validPlatforms = {
ios: 1, ios: 1,
@@ -15,7 +11,7 @@ const validPlatforms = {
export function checkPlatform(platform) { export function checkPlatform(platform) {
if (!validPlatforms[platform]) { if (!validPlatforms[platform]) {
throw new Error(`Invalid platform '${platform}'`); throw new Error(`无法识别的平台 '${platform}'`);
} }
return platform; return platform;
} }
@@ -42,9 +38,9 @@ export async function listApp(platform) {
const list = platform ? data.filter((v) => v.platform === platform) : data; const list = platform ? data.filter((v) => v.platform === platform) : data;
const header = [ const header = [
{ value: 'App Id' }, { value: '应用 id' },
{ value: 'App Name' }, { value: '应用名称' },
{ value: 'Platform' }, { value: '平台' },
]; ];
const rows = []; const rows = [];
for (const app of list) { for (const app of list) {
@@ -54,9 +50,9 @@ export async function listApp(platform) {
console.log(Table(header, rows).render()); console.log(Table(header, rows).render());
if (platform) { if (platform) {
console.log(`\nTotal ${list.length} ${platform} apps`); console.log(`\ ${list.length} ${platform} 个应用`);
} else { } else {
console.log(`\nTotal ${list.length} apps`); console.log(`\ ${list.length} 个应用`);
} }
return list; return list;
} }
@@ -65,7 +61,7 @@ export async function chooseApp(platform) {
const list = await listApp(platform); const list = await listApp(platform);
while (true) { while (true) {
const id = await question('Enter appId:'); const id = await question('输入应用 id:');
const app = list.find((v) => v.id === (id | 0)); const app = list.find((v) => v.id === (id | 0));
if (app) { if (app) {
return app; return app;
@@ -75,13 +71,13 @@ export async function chooseApp(platform) {
export const commands = { export const commands = {
createApp: async function ({ options }) { createApp: async function ({ options }) {
const name = options.name || (await question('App Name:')); const name = options.name || (await question('应用名称:'));
const { downloadUrl } = options; const { downloadUrl } = options;
const platform = checkPlatform( const platform = checkPlatform(
options.platform || (await question('Platform(ios/android):')), options.platform || (await question('平台(ios/android):')),
); );
const { id } = await post('/app/create', { name, platform }); const { id } = await post('/app/create', { name, platform });
console.log(`Created app ${id}`); console.log(`已成功创建应用id: ${id}`);
await this.selectApp({ await this.selectApp({
args: [id], args: [id],
options: { platform, downloadUrl }, options: { platform, downloadUrl },
@@ -91,10 +87,10 @@ export const commands = {
const { platform } = options; const { platform } = options;
const id = args[0] || chooseApp(platform); const id = args[0] || chooseApp(platform);
if (!id) { if (!id) {
console.log('Canceled'); console.log('已取消');
} }
await doDelete(`/app/${id}`); await doDelete(`/app/${id}`);
console.log('Ok.'); console.log('操作成功');
}, },
apps: async function ({ options }) { apps: async function ({ options }) {
const { platform } = options; const { platform } = options;
@@ -102,9 +98,9 @@ export const commands = {
}, },
selectApp: async function ({ args, options }) { selectApp: async function ({ args, options }) {
const platform = checkPlatform( const platform = checkPlatform(
options.platform || (await question('Platform(ios/android):')), options.platform || (await question('平台(ios/android):')),
); );
const id = args[0] || (await chooseApp(platform)).id; const id = args[0] ? parseInt(args[0]) : (await chooseApp(platform)).id;
let updateInfo = {}; let updateInfo = {};
if (fs.existsSync('update.json')) { if (fs.existsSync('update.json')) {

View File

@@ -1,19 +1,16 @@
/** import path from 'path';
* Created by tdzl2003 on 2/22/16.
*/
const path = require('path');
import { getRNVersion, translateOptions } from './utils'; import { getRNVersion, translateOptions } from './utils';
import * as fs from 'fs-extra'; import * as fs from 'fs-extra';
import { ZipFile } from 'yazl'; import { ZipFile } from 'yazl';
import { open as openZipFile } from 'yauzl'; import { open as openZipFile } from 'yauzl';
import { question, printVersionCommand } from './utils'; import { question, printVersionCommand } from './utils';
import { checkPlatform } from './app'; import { checkPlatform } from './app';
const { spawn, spawnSync } = require('child_process'); import { spawn, spawnSync } from 'child_process';
const g2js = require('gradle-to-js/lib/parser'); const g2js = require('gradle-to-js/lib/parser');
const os = require('os'); import os from 'os';
const properties = require('properties');
var bsdiff, hdiff, diff; let bsdiff, hdiff, diff;
try { try {
bsdiff = require('node-bsdiff').diff; bsdiff = require('node-bsdiff').diff;
} catch (e) {} } catch (e) {}
@@ -34,9 +31,11 @@ async function runReactNativeBundleCommand(
let gradleConfig = {}; let gradleConfig = {};
if (platform === 'android') { if (platform === 'android') {
gradleConfig = await checkGradleConfig(); gradleConfig = await checkGradleConfig();
// if (gradleConfig.crunchPngs !== false) { if (gradleConfig.crunchPngs !== false) {
// throw new Error('请先禁用android的crunchPngs优化具体请参考 https://pushy.reactnative.cn/docs/getting-started.html#%E7%A6%81%E7%94%A8android%E7%9A%84crunch%E4%BC%98%E5%8C%96') console.warn(
// } 'android的crunchPngs选项似乎尚未禁用如已禁用则请忽略此提示这可能导致热更包体积异常增大具体请参考 https://pushy.reactnative.cn/docs/getting-started.html#%E7%A6%81%E7%94%A8-android-%E7%9A%84-crunch-%E4%BC%98%E5%8C%96 \n',
);
}
} }
let reactNativeBundleArgs = []; let reactNativeBundleArgs = [];
@@ -52,9 +51,27 @@ async function runReactNativeBundleCommand(
fs.emptyDirSync(outputFolder); fs.emptyDirSync(outputFolder);
let cliPath = require.resolve('react-native/local-cli/cli.js', {
paths: [process.cwd()],
});
try {
require.resolve('expo-router', {
paths: [process.cwd()],
});
console.log(`expo-router detected, will use @expo/cli to bundle.\n`);
// if using expo-router, use expo-cli
cliPath = require.resolve('@expo/cli', {
paths: [process.cwd()],
});
} catch (e) {}
const bundleCommand = cliPath.includes('@expo/cli')
? 'export:embed'
: 'bundle';
Array.prototype.push.apply(reactNativeBundleArgs, [ Array.prototype.push.apply(reactNativeBundleArgs, [
path.join('node_modules', 'react-native', 'local-cli', 'cli.js'), cliPath,
'bundle', bundleCommand,
'--assets-dest', '--assets-dest',
outputFolder, outputFolder,
'--bundle-output', '--bundle-output',
@@ -98,8 +115,39 @@ async function runReactNativeBundleCommand(
), ),
); );
} else { } else {
if (gradleConfig.enableHermes) { let hermesEnabled = false;
await compileHermesByteCode(bundleName, outputFolder);
if (platform === 'android') {
const gradlePropeties = await new Promise((resolve) => {
properties.parse(
'./android/gradle.properties',
{ path: true },
function (error, props) {
if (error) {
console.error(error);
resolve(null);
}
resolve(props);
},
);
});
hermesEnabled = gradlePropeties.hermesEnabled;
if (typeof hermesEnabled !== 'boolean')
hermesEnabled = gradleConfig.enableHermes;
} else if (
platform === 'ios' &&
fs.existsSync('ios/Pods/hermes-engine')
) {
hermesEnabled = true;
}
if (hermesEnabled) {
await compileHermesByteCode(
bundleName,
outputFolder,
sourcemapOutput,
);
} }
resolve(null); resolve(null);
} }
@@ -119,7 +167,6 @@ async function checkGradleConfig() {
try { try {
const gradleConfig = await g2js.parseFile('android/app/build.gradle'); const gradleConfig = await g2js.parseFile('android/app/build.gradle');
const projectConfig = gradleConfig['project.ext.react']; const projectConfig = gradleConfig['project.ext.react'];
crunchPngs = gradleConfig.android.buildTypes.release.crunchPngs;
for (const packagerConfig of projectConfig) { for (const packagerConfig of projectConfig) {
if ( if (
packagerConfig.includes('enableHermes') && packagerConfig.includes('enableHermes') &&
@@ -129,6 +176,7 @@ async function checkGradleConfig() {
break; break;
} }
} }
crunchPngs = gradleConfig.android.buildTypes.release.crunchPngs;
} catch (e) {} } catch (e) {}
return { return {
enableHermes, enableHermes,
@@ -136,35 +184,50 @@ async function checkGradleConfig() {
}; };
} }
async function compileHermesByteCode(bundleName, outputFolder) { async function compileHermesByteCode(
bundleName,
outputFolder,
sourcemapOutput,
) {
console.log(`Hermes enabled, now compiling to hermes bytecode:\n`); console.log(`Hermes enabled, now compiling to hermes bytecode:\n`);
const hermesPackage = fs.existsSync('node_modules/hermes-engine') // >= rn 0.69
? 'node_modules/hermes-engine' // 0.2+ const rnDir = path.dirname(
: 'node_modules/hermesvm'; // < 0.2 require.resolve('react-native', {
const hermesPath = `${hermesPackage}/${getHermesOSBin()}`; paths: [process.cwd()],
}),
const hermesCommand = fs.existsSync(`${hermesPath}/hermesc`)
? `${hermesPath}/hermesc` // 0.5+
: `${hermesPath}/hermes`; // < 0.5
spawnSync(
path.join.apply(null, hermesCommand.split('/')),
[
'-emit-binary',
'-out',
path.join(outputFolder, bundleName),
path.join(outputFolder, bundleName),
'-O',
],
{ stdio: 'ignore' },
); );
let hermesPath = path.join(rnDir, `/sdks/hermesc/${getHermesOSBin()}`);
// < rn 0.69
if (!fs.existsSync(hermesPath)) {
hermesPath = `node_modules/hermes-engine/${getHermesOSBin()}`;
}
const hermesCommand = `${hermesPath}/hermesc`;
const args = [
'-emit-binary',
'-out',
path.join(outputFolder, bundleName),
path.join(outputFolder, bundleName),
'-O',
];
if (sourcemapOutput) {
args.push('-output-source-map');
}
console.log(
'Running hermesc: ' + hermesCommand + ' ' + args.join(' ') + '\n',
);
spawnSync(hermesCommand, args, {
stdio: 'ignore',
});
} }
async function pack(dir, output) { async function pack(dir, output) {
console.log('Packing'); console.log('Packing');
fs.ensureDirSync(path.dirname(output)); fs.ensureDirSync(path.dirname(output));
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
var zipfile = new ZipFile(); const zipfile = new ZipFile();
function addDirectory(root, rel) { function addDirectory(root, rel) {
if (rel) { if (rel) {
@@ -197,7 +260,7 @@ async function pack(dir, output) {
}); });
zipfile.end(); zipfile.end();
}); });
console.log('Bundled saved to: ' + output); console.log('ppk热更包已生成并保存到: ' + output);
} }
function readEntire(entry, zipFile) { function readEntire(entry, zipFile) {
@@ -254,7 +317,7 @@ async function diffFromPPK(origin, next, output) {
const copies = {}; const copies = {};
var zipfile = new ZipFile(); const zipfile = new ZipFile();
const writePromise = new Promise((resolve, reject) => { const writePromise = new Promise((resolve, reject) => {
zipfile.outputStream.on('error', (err) => { zipfile.outputStream.on('error', (err) => {
@@ -339,7 +402,7 @@ async function diffFromPPK(origin, next, output) {
const deletes = {}; const deletes = {};
for (var k in originEntries) { for (let k in originEntries) {
if (!newEntries[k]) { if (!newEntries[k]) {
console.log('Delete ' + k); console.log('Delete ' + k);
deletes[k] = 1; deletes[k] = 1;
@@ -396,7 +459,7 @@ async function diffFromPackage(
const copies = {}; const copies = {};
var zipfile = new ZipFile(); const zipfile = new ZipFile();
const writePromise = new Promise((resolve, reject) => { const writePromise = new Promise((resolve, reject) => {
zipfile.outputStream.on('error', (err) => { zipfile.outputStream.on('error', (err) => {
@@ -515,22 +578,16 @@ function diffArgsCheck(args, options, diffFn) {
export const commands = { export const commands = {
bundle: async function ({ options }) { bundle: async function ({ options }) {
const platform = checkPlatform( const platform = checkPlatform(
options.platform || (await question('Platform(ios/android):')), options.platform || (await question('平台(ios/android):')),
); );
let { let { bundleName, entryFile, intermediaDir, output, dev, sourcemap } =
bundleName, translateOptions({
entryFile, ...options,
intermediaDir, platform,
output, });
dev,
verbose,
} = translateOptions({
...options,
platform,
});
// const sourcemapOutput = path.join(intermediaDir, bundleName + ".map"); const sourcemapOutput = path.join(intermediaDir, bundleName + '.map');
const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); const realOutput = output.replace(/\$\{time\}/g, '' + Date.now());
@@ -549,11 +606,12 @@ export const commands = {
entryFile, entryFile,
intermediaDir, intermediaDir,
platform, platform,
sourcemap ? sourcemapOutput : '',
); );
await pack(path.resolve(intermediaDir), realOutput); await pack(path.resolve(intermediaDir), realOutput);
const v = await question('Would you like to publish it?(Y/N)'); const v = await question('是否现在上传此热更包?(Y/N)');
if (v.toLowerCase() === 'y') { if (v.toLowerCase() === 'y') {
await this.publish({ await this.publish({
args: [realOutput], args: [realOutput],

View File

@@ -1,21 +1,20 @@
#!/usr/bin/env node #!/usr/bin/env node
/**
* Created by tdzl2003 on 2/13/16.
*/
const {loadSession} = require('./api'); import { loadSession } from './api';
const updateNotifier = require('update-notifier'); import updateNotifier from 'update-notifier';
import { printVersionCommand } from './utils/index.js'; import { printVersionCommand } from './utils/index.js';
const pkg = require('../package.json'); import pkg from '../package.json';
updateNotifier({pkg}).notify({isGlobal: true}); updateNotifier({ pkg }).notify({ isGlobal: true });
function printUsage({args}) { function printUsage({ args }) {
// const commandName = args[0]; // const commandName = args[0];
// TODO: print usage of commandName, or print global usage. // TODO: print usage of commandName, or print global usage.
console.log('Usage is under development now.') console.log('Usage is under development now.');
console.log('Visit `https://github.com/reactnativecn/react-native-pushy` for early document.'); console.log(
'Visit `https://github.com/reactnativecn/react-native-pushy` for early document.',
);
process.exit(1); process.exit(1);
} }
@@ -33,20 +32,21 @@ function run() {
printVersionCommand(); printVersionCommand();
process.exit(); process.exit();
} }
const argv = require('cli-arguments').parse(require('../cli.json')); const argv = require('cli-arguments').parse(require('../cli.json'));
global.NO_INTERACTIVE = argv.options['no-interactive']; global.NO_INTERACTIVE = argv.options['no-interactive'];
global.USE_ACC_OSS = argv.options['acc'];
loadSession() loadSession()
.then(()=>commands[argv.command](argv)) .then(() => commands[argv.command](argv))
.catch(err=>{ .catch((err) => {
if (err.status === 401) { if (err.status === 401) {
console.log('Not loggined.\nRun `pushy login` at your project directory to login.'); console.log('尚未登录。\n请在项目目录中运行`pushy login`命令来登录');
return; return;
} }
console.error(err.stack); console.error(err.stack);
process.exit(-1); process.exit(-1);
}); });
}; }
run(); run();

View File

@@ -1,36 +1,37 @@
/** import { get, post, uploadFile } from './api';
* Created by tdzl2003 on 4/2/16.
*/
const { get, post, uploadFile } = require('./api');
import { question, saveToLocal } from './utils'; import { question, saveToLocal } from './utils';
import { checkPlatform, getSelectedApp } from './app'; import { checkPlatform, getSelectedApp } from './app';
import { getApkInfo, getIpaInfo } from './utils'; import { getApkInfo, getIpaInfo } from './utils';
const Table = require('tty-table'); import Table from 'tty-table';
export async function listPackage(appId) { export async function listPackage(appId) {
const { data } = await get(`/app/${appId}/package/list?limit=1000`); const { data } = await get(`/app/${appId}/package/list?limit=1000`);
const header = [{ value: 'Package Id' }, { value: 'Version' }]; const header = [{ value: '原生包 Id' }, { value: '原生版本' }];
const rows = []; const rows = [];
for (const pkg of data) { for (const pkg of data) {
const { version } = pkg; const { version } = pkg;
let versionInfo = ''; let versionInfo = '';
if (version) { if (version) {
versionInfo = ` - ${version.id} ${version.hash.slice(0, 8)} ${ versionInfo = `, 已绑定:${version.name} (${version.id})`;
version.name
}`;
} else { } else {
versionInfo = ' (newest)'; // versionInfo = ' (newest)';
} }
let output = pkg.name;
rows.push([pkg.id, `${pkg.name}(${pkg.status})${versionInfo}`]); if (pkg.status === 'paused') {
output += '(已暂停)';
}
if (pkg.status === 'expired') {
output += '(已过期)';
}
output += versionInfo;
rows.push([pkg.id, output]);
} }
console.log(Table(header, rows).render()); console.log(Table(header, rows).render());
console.log(`\nTotal ${data.length} package(s).`); console.log(`\n ${data.length} 个包`);
return data; return data;
} }
@@ -38,7 +39,7 @@ export async function choosePackage(appId) {
const list = await listPackage(appId); const list = await listPackage(appId);
while (true) { while (true) {
const id = await question('Enter Package Id:'); const id = await question('输入原生包 id:');
const app = list.find((v) => v.id === (id | 0)); const app = list.find((v) => v.id === (id | 0));
if (app) { if (app) {
return app; return app;
@@ -50,7 +51,7 @@ export const commands = {
uploadIpa: async function ({ args }) { uploadIpa: async function ({ args }) {
const fn = args[0]; const fn = args[0];
if (!fn || !fn.endsWith('.ipa')) { if (!fn || !fn.endsWith('.ipa')) {
throw new Error('Usage: pushy uploadIpa <ipaFile>'); throw new Error('使用方法: pushy uploadIpa ipa后缀文件');
} }
const { const {
versionName, versionName,
@@ -60,15 +61,15 @@ export const commands = {
} = await getIpaInfo(fn); } = await getIpaInfo(fn);
const { appId, appKey } = await getSelectedApp('ios'); const { appId, appKey } = await getSelectedApp('ios');
if (appIdInPkg && appIdInPkg !== appId) { if (appIdInPkg && appIdInPkg != appId) {
throw new Error( throw new Error(
`appId不匹配当前ipa${appIdInPkg}, 当前update.json${appId}`, `appId不匹配当前ipa: ${appIdInPkg}, 当前update.json: ${appId}`,
); );
} }
if (appKeyInPkg && appKeyInPkg !== appKey) { if (appKeyInPkg && appKeyInPkg !== appKey) {
throw new Error( throw new Error(
`appKey不匹配当前ipa${appKeyInPkg}, 当前update.json${appKey}`, `appKey不匹配当前ipa: ${appKeyInPkg}, 当前update.json: ${appKey}`,
); );
} }
@@ -80,12 +81,14 @@ export const commands = {
buildTime, buildTime,
}); });
saveToLocal(fn, `${appId}/package/${id}.ipa`); saveToLocal(fn, `${appId}/package/${id}.ipa`);
console.log(`Ipa uploaded: ${id}`); console.log(
`已成功上传ipa原生包id: ${id}, version: ${versionName}, buildTime: ${buildTime}`,
);
}, },
uploadApk: async function ({ args }) { uploadApk: async function ({ args }) {
const fn = args[0]; const fn = args[0];
if (!fn || !fn.endsWith('.apk')) { if (!fn || !fn.endsWith('.apk')) {
throw new Error('Usage: pushy uploadApk <apkFile>'); throw new Error('使用方法: pushy uploadApk apk后缀文件');
} }
const { const {
versionName, versionName,
@@ -95,15 +98,15 @@ export const commands = {
} = await getApkInfo(fn); } = await getApkInfo(fn);
const { appId, appKey } = await getSelectedApp('android'); const { appId, appKey } = await getSelectedApp('android');
if (appIdInPkg && appIdInPkg !== appId) { if (appIdInPkg && appIdInPkg != appId) {
throw new Error( throw new Error(
`appId不匹配当前apk${appIdInPkg}, 当前update.json${appId}`, `appId不匹配当前apk: ${appIdInPkg}, 当前update.json: ${appId}`,
); );
} }
if (appKeyInPkg && appKeyInPkg !== appKey) { if (appKeyInPkg && appKeyInPkg !== appKey) {
throw new Error( throw new Error(
`appKey不匹配当前apk${appKeyInPkg}, 当前update.json${appKey}`, `appKey不匹配当前apk: ${appKeyInPkg}, 当前update.json: ${appKey}`,
); );
} }
@@ -115,25 +118,27 @@ export const commands = {
buildTime, buildTime,
}); });
saveToLocal(fn, `${appId}/package/${id}.apk`); saveToLocal(fn, `${appId}/package/${id}.apk`);
console.log(`Apk uploaded: ${id}`); console.log(
`已成功上传apk原生包id: ${id}, version: ${versionName}, buildTime: ${buildTime}`,
);
}, },
parseIpa: async function ({ args }) { parseIpa: async function ({ args }) {
const fn = args[0]; const fn = args[0];
if (!fn || !fn.endsWith('.ipa')) { if (!fn || !fn.endsWith('.ipa')) {
throw new Error('Usage: pushy parseIpa <ipaFile>'); throw new Error('使用方法: pushy parseIpa ipa后缀文件');
} }
console.log(await getIpaInfo(fn)); console.log(await getIpaInfo(fn));
}, },
parseApk: async function ({ args }) { parseApk: async function ({ args }) {
const fn = args[0]; const fn = args[0];
if (!fn || !fn.endsWith('.apk')) { if (!fn || !fn.endsWith('.apk')) {
throw new Error('Usage: pushy parseApk <apkFile>'); throw new Error('使用方法: pushy parseApk apk后缀文件');
} }
console.log(await getApkInfo(fn)); console.log(await getApkInfo(fn));
}, },
packages: async function ({ options }) { packages: async function ({ options }) {
const platform = checkPlatform( const platform = checkPlatform(
options.platform || (await question('Platform(ios/android):')), options.platform || (await question('平台(ios/android):')),
); );
const { appId } = await getSelectedApp(platform); const { appId } = await getSelectedApp(platform);
await listPackage(appId); await listPackage(appId);

View File

@@ -1,38 +1,28 @@
/** import { question } from './utils';
* Created by tdzl2003 on 2/13/16. import { post, get, replaceSession, saveSession, closeSession } from './api';
*/ import crypto from 'crypto';
import {question} from './utils';
const {
post,
get,
replaceSession,
saveSession,
closeSession,
} = require('./api');
const crypto = require('crypto');
function md5(str) { function md5(str) {
return crypto.createHash('md5').update(str).digest('hex'); return crypto.createHash('md5').update(str).digest('hex');
} }
exports.commands = { export const commands = {
login: async function ({args}){ login: async function ({ args }) {
const email = args[0] || await question('email:'); const email = args[0] || (await question('email:'));
const pwd = args[1] || await question('password:', true); const pwd = args[1] || (await question('password:', true));
const {token, info} = await post('/user/login', { const { token, info } = await post('/user/login', {
email, email,
pwd: md5(pwd), pwd: md5(pwd),
}); });
replaceSession({token}); replaceSession({ token });
await saveSession(); await saveSession();
console.log(`Welcome, ${info.name}.`); console.log(`欢迎使用 pushy 热更新服务, ${info.name}.`);
}, },
logout: async function (){ logout: async function () {
await closeSession(); await closeSession();
console.log('Logged out.'); console.log('已退出登录');
}, },
me: async function (){ me: async function () {
const me = await get('/user/me'); const me = await get('/user/me');
for (const k in me) { for (const k in me) {
if (k !== 'ok') { if (k !== 'ok') {
@@ -40,4 +30,4 @@ exports.commands = {
} }
} }
}, },
} };

View File

@@ -1,14 +1,10 @@
/**
* Created by tdzl2003 on 2/13/16.
*/
import fs from 'fs-extra'; import fs from 'fs-extra';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
const pkg = require('../../package.json'); import pkg from '../../package.json';
const AppInfoParser = require('app-info-parser'); import AppInfoParser from 'app-info-parser';
var read = require('read'); import read from 'read';
export function question(query, password) { export function question(query, password) {
if (NO_INTERACTIVE) { if (NO_INTERACTIVE) {
@@ -43,7 +39,11 @@ export function translateOptions(options) {
export function getRNVersion() { export function getRNVersion() {
const version = JSON.parse( const version = JSON.parse(
fs.readFileSync(path.resolve('node_modules/react-native/package.json')), fs.readFileSync(
require.resolve('react-native/package.json', {
paths: [process.cwd()],
}),
),
).version; ).version;
// We only care about major and minor version. // We only care about major and minor version.
@@ -106,9 +106,8 @@ export async function getIpaInfo(fn) {
if (updateJsonFile) { if (updateJsonFile) {
appCredential = JSON.parse(updateJsonFile.toString()).ios; appCredential = JSON.parse(updateJsonFile.toString()).ios;
} }
const { const { CFBundleShortVersionString: versionName } =
CFBundleShortVersionString: versionName, await appInfoParser.parse();
} = await appInfoParser.parse();
let buildTimeTxtBuffer = await appInfoParser.parser.getEntry( let buildTimeTxtBuffer = await appInfoParser.parser.getEntry(
/payload\/.+?\.app\/pushy_build_time.txt/, /payload\/.+?\.app\/pushy_build_time.txt/,
); );
@@ -139,11 +138,11 @@ export function saveToLocal(originPath, destName) {
export function printVersionCommand() { export function printVersionCommand() {
console.log('react-native-update-cli: ' + pkg.version); console.log('react-native-update-cli: ' + pkg.version);
try { try {
const PACKAGE_JSON_PATH = path.resolve( const PACKAGE_JSON_PATH = require.resolve(
process.cwd(), 'react-native-update/package.json',
'node_modules', {
'react-native-update', paths: [process.cwd()],
'package.json', },
); );
console.log('react-native-update: ' + require(PACKAGE_JSON_PATH).version); console.log('react-native-update: ' + require(PACKAGE_JSON_PATH).version);
} catch (e) { } catch (e) {

View File

@@ -1,13 +1,4 @@
/** import { get, post, put, uploadFile } from './api';
* Created by tdzl2003 on 4/2/16.
*/
const {
get,
post,
put,
uploadFile,
} = require('./api');
import { question, saveToLocal } from './utils'; import { question, saveToLocal } from './utils';
import { checkPlatform, getSelectedApp } from './app'; import { checkPlatform, getSelectedApp } from './app';
@@ -17,17 +8,24 @@ async function showVersion(appId, offset) {
const { data, count } = await get(`/app/${appId}/version/list`); const { data, count } = await get(`/app/${appId}/version/list`);
console.log(`Offset ${offset}`); console.log(`Offset ${offset}`);
for (const version of data) { for (const version of data) {
let packageInfo = version.packages.slice(0, 3).map(v=>v.name).join(', '); let packageInfo = version.packages
.slice(0, 3)
.map((v) => v.name)
.join(', ');
const count = version.packages.length; const count = version.packages.length;
if (count > 3) { if (count > 3) {
packageInfo += `...and ${count-3} more`; packageInfo += `...and ${count - 3} more`;
} }
if (count === 0) { if (count === 0) {
packageInfo = `(no package)`; packageInfo = `(no package)`;
} else { } else {
packageInfo = `[${packageInfo}]`; packageInfo = `[${packageInfo}]`;
} }
console.log(`${version.id}) ${version.hash.slice(0, 8)} ${version.name} ${packageInfo}`); console.log(
`${version.id}) ${version.hash.slice(0, 8)} ${
version.name
} ${packageInfo}`,
);
} }
return data; return data;
} }
@@ -38,10 +36,17 @@ async function listVersions(appId) {
await showVersion(appId, offset); await showVersion(appId, offset);
const cmd = await question('page Up/page Down/Begin/Quit(U/D/B/Q)'); const cmd = await question('page Up/page Down/Begin/Quit(U/D/B/Q)');
switch (cmd.toLowerCase()) { switch (cmd.toLowerCase()) {
case 'u': offset = Math.max(0, offset - 10); break; case 'u':
case 'd': offset += 10; break; offset = Math.max(0, offset - 10);
case 'b': offset = 0; break; break;
case 'q': return; case 'd':
offset += 10;
break;
case 'b':
offset = 0;
break;
case 'q':
return;
} }
} }
} }
@@ -50,14 +55,21 @@ async function chooseVersion(appId) {
let offset = 0; let offset = 0;
while (true) { while (true) {
const data = await showVersion(appId, offset); const data = await showVersion(appId, offset);
const cmd = await question('Enter versionId or page Up/page Down/Begin(U/D/B)'); const cmd = await question(
'Enter versionId or page Up/page Down/Begin(U/D/B)',
);
switch (cmd.toLowerCase()) { switch (cmd.toLowerCase()) {
case 'U': offset = Math.max(0, offset - 10); break; case 'U':
case 'D': offset += 10; break; offset = Math.max(0, offset - 10);
case 'B': offset = 0; break; break;
default: case 'D':
{ offset += 10;
const v = data.find(v=>v.id === (cmd | 0)); break;
case 'B':
offset = 0;
break;
default: {
const v = data.find((v) => v.id === (cmd | 0));
if (v) { if (v) {
return v; return v;
} }
@@ -67,47 +79,88 @@ async function chooseVersion(appId) {
} }
export const commands = { export const commands = {
publish: async function({args, options}) { publish: async function ({ args, options }) {
const fn = args[0]; const fn = args[0];
const {name, description, metaInfo } = options; const { name, description, metaInfo } = options;
if (!fn || !fn.endsWith('.ppk')) { if (!fn || !fn.endsWith('.ppk')) {
throw new Error('Usage: pushy publish <ppkFile> --platform ios|android'); throw new Error(
'使用方法: pushy publish ppk后缀文件 --platform ios|android',
);
} }
const platform = checkPlatform(options.platform || await question('Platform(ios/android):')); const platform = checkPlatform(
options.platform || (await question('平台(ios/android):')),
);
const { appId } = await getSelectedApp(platform); const { appId } = await getSelectedApp(platform);
const { hash } = await uploadFile(fn); const { hash } = await uploadFile(fn);
const { id } = await post(`/app/${appId}/version/create`, { const { id } = await post(`/app/${appId}/version/create`, {
name: name || await question('Enter version name:') || '(未命名)', name: name || (await question('输入版本名称: ')) || '(未命名)',
hash, hash,
description: description || await question('Enter description:'), description: description || (await question('输入版本描述:')),
metaInfo: metaInfo || await question('Enter meta info:'), metaInfo: metaInfo || (await question('输入自定义的 meta info:')),
}); });
// TODO local diff // TODO local diff
saveToLocal(fn, `${appId}/ppk/${id}.ppk`); saveToLocal(fn, `${appId}/ppk/${id}.ppk`);
console.log(`Version published: ${id}`); console.log(`已成功上传新热更包id: ${id}`);
const v = await question('Would you like to bind packages to this version?(Y/N)'); const v = await question('是否现在将此热更应用到原生包上?(Y/N)');
if (v.toLowerCase() === 'y') { if (v.toLowerCase() === 'y') {
await this.update({args:[], options:{versionId: id, platform}}); await this.update({ args: [], options: { versionId: id, platform } });
} }
}, },
versions: async function({options}) { versions: async function ({ options }) {
const platform = checkPlatform(options.platform || await question('Platform(ios/android):')); const platform = checkPlatform(
options.platform || (await question('平台(ios/android):')),
);
const { appId } = await getSelectedApp(platform); const { appId } = await getSelectedApp(platform);
await listVersions(appId); await listVersions(appId);
}, },
update: async function({args, options}) { update: async function ({ args, options }) {
const platform = checkPlatform(options.platform || await question('Platform(ios/android):')); const platform = checkPlatform(
options.platform || (await question('平台(ios/android):')),
);
const { appId } = await getSelectedApp(platform); const { appId } = await getSelectedApp(platform);
const versionId = options.versionId || (await chooseVersion(appId)).id; const versionId = options.versionId || (await chooseVersion(appId)).id;
const pkgId = options.packageId || (await choosePackage(appId)).id;
let pkgId;
let pkgVersion = options.packageVersion;
if (pkgVersion) {
pkgVersion = pkgVersion.trim();
const { data } = await get(`/app/${appId}/package/list?limit=1000`);
const pkg = data.find((d) => d.name === pkgVersion);
if (pkg) {
pkgId = pkg.id;
} else {
throw new Error(`未查询到匹配原生版本:${pkgVersion}`);
}
}
if (!pkgId) {
pkgId = options.packageId || (await choosePackage(appId)).id;
}
if (!pkgId) {
throw new Error('请提供 packageId 或 packageVersion 参数');
}
await put(`/app/${appId}/package/${pkgId}`, { await put(`/app/${appId}/package/${pkgId}`, {
versionId, versionId,
}); });
console.log('Ok.'); console.log('操作成功');
} },
updateVersionInfo: async function ({ args, options }) {
const platform = checkPlatform(
options.platform || (await question('平台(ios/android):')),
);
const { appId } = await getSelectedApp(platform);
const versionId = options.versionId || (await chooseVersion(appId)).id;
const updateParams = {};
options.name && (updateParams.name = options.name);
options.description && (updateParams.description = options.description);
options.metaInfo && (updateParams.metaInfo = options.metaInfo);
await put(`/app/${appId}/version/${versionId}`, updateParams);
console.log('操作成功');
},
}; };

2897
yarn.lock

File diff suppressed because it is too large Load Diff