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

..

74 Commits

Author SHA1 Message Date
sunnylqm
574a52b126 v1.3.1 2021-01-21 17:46:16 +08:00
sunnylqm
c00a7a72fe Disable crunchPngs check 2021-01-21 17:45:15 +08:00
sunnylqm
0e8e3aa420 v1.3.0 2021-01-20 23:30:26 +08:00
sunnylqm
7426e93647 Update gitignore 2021-01-20 23:30:04 +08:00
sunnylqm
b6fb2e7d2a Check does appId/appKey match 2021-01-20 23:29:53 +08:00
sunnylqm
7f1dcbb571 Use table for listApp 2021-01-20 23:29:31 +08:00
sunnylqm
59fad93832 Change local build foler name 2021-01-11 14:06:14 +08:00
sunnylqm
3db4f803e2 v1.2.3 2021-01-05 21:54:04 +08:00
sunnylqm
56d44b8c85 Update defaultEndpoint 2021-01-05 21:53:46 +08:00
sunnylqm
ecd951265f v1.2.2 2020-12-15 13:30:19 +08:00
sunnylqm
62d455ac07 Update app-info-parser 2020-12-15 13:30:04 +08:00
sunnylqm
acfc2dd65b v1.2.1 2020-11-04 14:48:03 +08:00
sunnylqm
7f062f681a v1.2.0 2020-10-21 17:33:01 +08:00
sunnylqm
7f5b9fd7fd Check gradleConfig 2020-10-21 15:50:44 +08:00
sunnylqm
13b21c49c1 Merge branch 'master' of github.com:sunnylqm/react-native-update-cli
# Conflicts:
#	package.json
2020-10-21 15:24:46 +08:00
sunnylqm
85db61704c v1.1.18 2020-10-18 12:16:35 +08:00
sunnylqm
57ddfc5d7d Add token when upload 2020-10-18 12:16:15 +08:00
sunnylqm
6cb38eeec1 v1.1.17 2020-10-13 18:12:35 +08:00
sunnylqm
8a7c21fb50 Do not throw update.json not found error 2020-10-13 18:12:15 +08:00
sunnylqm
7223fa1389 v1.1.16 2020-09-15 15:16:31 +08:00
sunnylqm
2d5c01e990 Improve parse 2020-09-15 15:13:52 +08:00
sunnylqm
16b1cc3ed4 v1.1.15 2020-08-25 11:41:18 +08:00
sunnylqm
5e69793925 Update api endpoint 2020-08-25 11:41:01 +08:00
sunnylqm
b3c8e1f8e3 v1.1.14 2020-08-12 15:28:20 +08:00
sunnylqm
27ed4bd8d1 Add parse ipa and apk 2020-08-12 15:28:07 +08:00
sunnylqm
f0ad8bdcde v1.1.13 2020-06-22 18:44:00 +08:00
sunnylqm
62a37a955b Fix windows bundle command 2020-06-22 18:26:18 +08:00
sunnylqm
233e661f17 v1.1.12 2020-05-30 12:58:47 +08:00
sunnylqm
3a9bf67703 Revert fs-extra 2020-05-30 12:58:34 +08:00
sunnylqm
aa5fd4b4b1 v1.1.11 2020-05-29 13:15:47 +08:00
sunnylqm
6ed6420dd1 rmdir 2020-05-29 13:15:24 +08:00
sunnylqm
92f7d7efc4 v1.1.10 2020-05-29 13:12:16 +08:00
sunnylqm
de68470060 empty dir 2020-05-29 13:11:59 +08:00
sunnylqm
dfee2dd762 v1.1.9 2020-05-29 10:26:14 +08:00
sunnylqm
d726987b2c Update pricing url 2020-05-29 10:26:01 +08:00
sunnylqm
01f11822e1 v1.1.8 2020-05-29 10:24:33 +08:00
sunnylqm
37353222ef Remove fs-extra 2020-05-29 10:24:09 +08:00
sunnylqm
b76a2eb989 v1.1.7 2020-05-07 23:45:52 +08:00
sunnylqm
05b97d1e1e Update packages 2020-05-07 23:45:29 +08:00
sunnylqm
ba4969fe68 Update payment hint 2020-05-07 23:33:49 +08:00
sunnylqm
dad63743c1 v1.1.6 2020-05-07 17:49:44 +08:00
sunnylqm
924c58d254 maxSize 2020-05-07 17:49:20 +08:00
sunnylqm
b950d3dbe8 v1.1.5 2020-05-07 17:17:10 +08:00
sunnylqm
1206cd4461 Pass extension to upload api 2020-05-07 17:16:52 +08:00
sunnylqm
0147582561 v1.1.4 2020-04-15 00:03:05 +08:00
sunnylqm
a343999cb6 Support hermes 0.5 2020-04-15 00:02:35 +08:00
sunnylqm
ef28133cfc v1.1.3 2020-04-02 00:09:21 +08:00
sunnylqm
d16292a7a6 update api endpoint 2020-04-02 00:09:03 +08:00
sunnylqm
2146940d01 v1.1.2 2020-03-27 13:53:33 +08:00
sunnylqm
755485b4cb print version when bundling 2020-03-27 13:53:17 +08:00
sunnylqm
6bc7ee7091 notify global package 2020-03-21 16:55:52 +08:00
sunnylqm
9d00325519 v1.1.1 2020-03-20 15:51:53 +08:00
sunnylqm
84facadf3d 检查bundle文件 2020-03-20 15:51:35 +08:00
sunnylqm
a9dea5c188 v1.1.0 2020-03-18 16:06:20 +08:00
sunnylqm
83fe70f840 Revert "seperate key name"
This reverts commit a6477b4f0f.

# Conflicts:
#	src/package.js
2020-03-18 16:06:03 +08:00
sunnylqm
f1dd31cd56 v1.0.9 2020-03-16 19:10:33 +08:00
sunnylqm
a6477b4f0f seperate key name 2020-03-16 19:10:02 +08:00
sunnylqm
15b82b440e v1.0.8 2020-03-10 09:24:25 +08:00
sunnylqm
cc3f5dc093 Update build time hint 2020-03-10 09:23:47 +08:00
sunnylqm
19c6656dcf v1.0.7 2020-03-08 09:02:46 +08:00
sunnylqm
27f640f305 Tweak ping args 2020-03-08 08:59:42 +08:00
sunnylqm
552db6d2b3 v1.0.6 2020-03-07 17:09:06 +08:00
sunnylqm
443e4c14f6 Downgrade tty-table 2020-03-07 17:08:55 +08:00
sunnylqm
80b45469eb v1.0.5 2020-03-07 16:11:52 +08:00
sunnylqm
6b32a4c3aa v1.0.4 2020-03-06 22:22:48 +08:00
sunnylqm
1d5379c837 v1.0.3 2020-03-06 22:09:31 +08:00
sunnylqm
7b3872265b Add user agent header 2020-03-06 22:09:13 +08:00
sunnylqm
9750984a69 v1.0.2 2020-03-06 18:56:43 +08:00
sunnylqm
0b217484b7 Support oss 2020-03-06 18:56:15 +08:00
sunnylqm
76d958c623 Remove duplicated import 2020-02-27 00:07:34 +08:00
sunnylqm
4a062c68af Deprecate new Buffer usage 2020-02-27 00:06:43 +08:00
sunnylqm
d63ba619ba Save to local after upload 2020-02-26 00:32:08 +08:00
sunnylqm
772929837f Throw error when bundle not found 2020-02-26 00:31:40 +08:00
sunnylqm
60351c64f1 Update readme 2020-02-19 23:51:45 +08:00
13 changed files with 720 additions and 1381 deletions

View File

@@ -1,14 +1,8 @@
{
"plugins": [
"syntax-object-rest-spread",
"syntax-async-functions",
"transform-es2015-arrow-functions",
"transform-async-to-generator",
"transform-es2015-modules-commonjs",
"transform-es2015-destructuring",
"transform-es2015-spread",
"transform-object-rest-spread",
"transform-es2015-parameters",
"transform-strict-mode"
"transform-object-rest-spread"
]
}

1
.gitignore vendored
View File

@@ -104,3 +104,4 @@ dist
.tern-port
lib/
.DS_Store

View File

@@ -1 +1 @@
# react-native-pushy-cli
# react-native-update-cli

View File

@@ -2,15 +2,11 @@
"useCommand": true,
"defaultCommand": "help",
"commands": {
"help": {
},
"help": {},
"login":{
},
"logout": {
},
"me": {
},
"login": {},
"logout": {},
"me": {},
"createApp": {
"options": {
@@ -29,8 +25,7 @@
}
}
},
"deleteApp": {
},
"deleteApp": {},
"selectApp": {
"options": {
"platform": {
@@ -39,10 +34,10 @@
}
},
"uploadIpa": {
},
"uploadApk": {
},
"uploadIpa": {},
"uploadApk": {},
"parseIpa": {},
"parseApk": {},
"packages": {
"options": {
"platform": {
@@ -102,7 +97,7 @@
"platform": {
"hasValue": true
},
"bundleName":{
"bundleName": {
"default": "index.bundlejs",
"hasValue": true
},
@@ -111,16 +106,14 @@
"hasValue": true
},
"intermediaDir": {
"default": "build/intermedia/${platform}",
"default": ".pushy/intermedia/${platform}",
"hasValue": true
},
"output": {
"default": "build/output/${platform}.${time}.ppk",
"default": ".pushy/output/${platform}.${time}.ppk",
"hasValue": true
},
"verbose": {
}
"verbose": {}
}
},
"release": {
@@ -154,7 +147,7 @@
}
}
},
"globalOptions":{
"globalOptions": {
"no-interactive": {
"default": false
}

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-update-cli",
"version": "1.0.1",
"version": "1.3.1",
"description": "Command tools for javaScript updater with `pushy` service for react native apps.",
"main": "index.js",
"bin": {
@@ -32,31 +32,29 @@
},
"homepage": "https://github.com/reactnativecn/react-native-pushy/tree/master/react-native-pushy-cli",
"dependencies": {
"app-info-parser": "^0.3.8",
"app-info-parser": "^1.0.0",
"cli-arguments": "^0.2.1",
"fs-extra": "^8.1.0",
"filesize-parser": "^1.5.0",
"fs-extra": "8",
"gradle-to-js": "^2.0.0",
"isomorphic-fetch": "^2.2.1",
"progress": "^1.1.8",
"node-fetch": "^2.6.1",
"progress": "^2.0.3",
"read": "^1.0.7",
"request": "^2.69.0",
"tty-table": "^2.7.0",
"update-notifier": "^4.1.0",
"request": "^2.88.2",
"tcp-ping": "^0.1.1",
"tty-table": "4.1",
"update-notifier": "^4.1.1",
"yauzl": "^2.10.0",
"yazl": "2.3.0"
"yazl": "2.5.1"
},
"devDependencies": {
"babel-cli": "^6.5.1",
"babel-eslint": "^4.1.6",
"babel-plugin-syntax-async-functions": "^6.5.0",
"babel-plugin-syntax-object-rest-spread": "^6.5.0",
"babel-plugin-transform-async-to-generator": "^6.3.13",
"babel-plugin-transform-es2015-arrow-functions": "^6.5.2",
"babel-plugin-transform-es2015-destructuring": "^6.3.15",
"babel-plugin-transform-es2015-modules-commonjs": "^6.3.16",
"babel-plugin-transform-es2015-parameters": "^6.5.0",
"babel-plugin-transform-es2015-spread": "^6.5.2",
"babel-plugin-transform-object-rest-spread": "^6.5.0",
"babel-plugin-transform-strict-mode": "^6.5.2"
"babel-cli": "^6.26.0",
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"babel-plugin-transform-es2015-spread": "^6.22.0",
"babel-plugin-transform-object-rest-spread": "^6.26.0"
},
"engines": {
"node": ">= 8"
}
}

View File

@@ -2,22 +2,35 @@
* Created by tdzl2003 on 2/13/16.
*/
const fetch = require('isomorphic-fetch');
let host = process.env.PUSHY_REGISTRY || 'https://update.reactnative.cn/api';
const fs = require('fs-extra');
const fetch = require('node-fetch');
const defaultEndpoint = 'http://u.reactnative.cn/api'
let host = process.env.PUSHY_REGISTRY || defaultEndpoint;
const fs = require('fs');
import request from 'request';
import ProgressBar from 'progress';
const packageJson = require('../package.json');
const tcpp = require('tcp-ping');
const util = require('util');
const path = require('path');
import filesizeParser from 'filesize-parser';
import { pricingPageUrl } from './utils';
const tcpPing = util.promisify(tcpp.ping);
let session = undefined;
let savedSession = undefined;
const userAgent = `react-native-update-cli/${packageJson.version}`;
exports.loadSession = async function() {
if (fs.existsSync('.update')) {
try {
exports.replaceSession(JSON.parse(fs.readFileSync('.update', 'utf8')));
savedSession = session;
} catch (e) {
console.error('Failed to parse file `.update`. Try to remove it manually.');
console.error(
'Failed to parse file `.update`. Try to remove it manually.',
);
throw e;
}
}
@@ -47,14 +60,16 @@ exports.closeSession = function() {
savedSession = undefined;
}
session = undefined;
host = process.env.PUSHY_REGISTRY || 'https://update.reactnative.cn';
host = process.env.PUSHY_REGISTRY || defaultEndpoint;
};
async function query(url, options) {
const resp = await fetch(url, options);
const json = await resp.json();
if (resp.status !== 200) {
throw Object.assign(new Error(json.message || json.error), { status: resp.status });
throw Object.assign(new Error(json.message || json.error), {
status: resp.status,
});
}
return json;
}
@@ -64,6 +79,7 @@ function queryWithoutBody(method) {
return query(host + api, {
method,
headers: {
'User-Agent': userAgent,
'X-AccessToken': session ? session.token : '',
},
});
@@ -75,6 +91,7 @@ function queryWithBody(method) {
return query(host + api, {
method,
headers: {
'User-Agent': userAgent,
'Content-Type': 'application/json',
'X-AccessToken': session ? session.token : '',
},
@@ -88,15 +105,29 @@ exports.post = queryWithBody('POST');
exports.put = queryWithBody('PUT');
exports.doDelete = queryWithBody('DELETE');
async function uploadFile(fn) {
const { url, fieldName, formData } = await exports.post('/upload', {});
async function uploadFile(fn, key) {
const { url, backupUrl, formData, maxSize } = await exports.post('/upload', {
ext: path.extname(fn)
});
let realUrl = url;
if (!/^https?\:\/\//.test(url)) {
realUrl = host + url;
if (backupUrl) {
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});
}
const fileSize = fs.statSync(fn).size;
if (maxSize && fileSize > filesizeParser(maxSize)) {
throw new Error(`此文件大小超出上限${maxSize}。您可以考虑升级付费业务以提升此限制。详情请访问:${pricingPageUrl}`)
}
const bar = new ProgressBar(' Uploading [:bar] :percent :etas', {
complete: '=',
@@ -105,6 +136,9 @@ async function uploadFile(fn) {
});
const info = await new Promise((resolve, reject) => {
if (key) {
formData.key = key;
}
formData.file = fs.createReadStream(fn);
formData.file.on('data', function(data) {
@@ -114,15 +148,27 @@ async function uploadFile(fn) {
realUrl,
{
formData,
headers: {
'User-Agent': userAgent,
'X-AccessToken': session ? session.token : '',
},
},
(err, resp, body) => {
if (err) {
return reject(err);
}
if (resp.statusCode > 299) {
return reject(Object.assign(new Error(body), { status: resp.statusCode }));
return reject(
Object.assign(new Error(body), { status: resp.statusCode }),
);
}
resolve(JSON.parse(body));
resolve(
body
? // qiniu
JSON.parse(body)
: // aliyun oss
{ hash: formData.key },
);
},
);
});

View File

@@ -2,14 +2,11 @@
* Created by tdzl2003 on 2/13/16.
*/
import {question} from './utils';
import * as fs from 'fs-extra';
import { question } from './utils';
import fs from 'fs';
const Table = require('tty-table');
const {
post,
get,
doDelete,
} = require('./api');
const { post, get, doDelete } = require('./api');
const validPlatforms = {
ios: 1,
@@ -20,28 +17,42 @@ export function checkPlatform(platform) {
if (!validPlatforms[platform]) {
throw new Error(`Invalid platform '${platform}'`);
}
return platform
return platform;
}
export function getSelectedApp(platform) {
checkPlatform(platform);
if (!fs.existsSync('update.json')){
throw new Error(`App not selected. run 'pushy selectApp --platform ${platform}' first!`);
if (!fs.existsSync('update.json')) {
throw new Error(
`App not selected. run 'pushy selectApp --platform ${platform}' first!`,
);
}
const updateInfo = JSON.parse(fs.readFileSync('update.json', 'utf8'));
if (!updateInfo[platform]) {
throw new Error(`App not selected. run 'pushy selectApp --platform ${platform}' first!`);
throw new Error(
`App not selected. run 'pushy selectApp --platform ${platform}' first!`,
);
}
return updateInfo[platform];
}
export async function listApp(platform){
const {data} = await get('/app/list');
const list = platform?data.filter(v=>v.platform===platform):data;
export async function listApp(platform) {
const { data } = await get('/app/list');
const list = platform ? data.filter((v) => v.platform === platform) : data;
const header = [
{ value: 'App Id' },
{ value: 'App Name' },
{ value: 'Platform' },
];
const rows = [];
for (const app of list) {
console.log(`${app.id}) ${app.name}(${app.platform})`);
rows.push([app.id, app.name, app.platform]);
}
console.log(Table(header, rows).render());
if (platform) {
console.log(`\nTotal ${list.length} ${platform} apps`);
} else {
@@ -55,7 +66,7 @@ export async function chooseApp(platform) {
while (true) {
const id = await question('Enter appId:');
const app = list.find(v=>v.id === (id|0));
const app = list.find((v) => v.id === (id | 0));
if (app) {
return app;
}
@@ -63,19 +74,21 @@ export async function chooseApp(platform) {
}
export const commands = {
createApp: async function ({options}) {
const name = options.name || await question('App Name:');
const {downloadUrl} = options;
const platform = checkPlatform(options.platform || await question('Platform(ios/android):'));
const {id} = await post('/app/create', {name, platform});
createApp: async function ({ options }) {
const name = options.name || (await question('App Name:'));
const { downloadUrl } = options;
const platform = checkPlatform(
options.platform || (await question('Platform(ios/android):')),
);
const { id } = await post('/app/create', { name, platform });
console.log(`Created app ${id}`);
await this.selectApp({
args: [id],
options: {platform, downloadUrl},
options: { platform, downloadUrl },
});
},
deleteApp: async function ({args, options}) {
const {platform} = options;
deleteApp: async function ({ args, options }) {
const { platform } = options;
const id = args[0] || chooseApp(platform);
if (!id) {
console.log('Canceled');
@@ -83,12 +96,14 @@ export const commands = {
await doDelete(`/app/${id}`);
console.log('Ok.');
},
apps: async function ({options}){
const {platform} = options;
apps: async function ({ options }) {
const { platform } = options;
listApp(platform);
},
selectApp: async function({args, options}) {
const platform = checkPlatform(options.platform || await question('Platform(ios/android):'));
selectApp: async function ({ args, options }) {
const platform = checkPlatform(
options.platform || (await question('Platform(ios/android):')),
);
const id = args[0] || (await chooseApp(platform)).id;
let updateInfo = {};
@@ -96,15 +111,21 @@ export const commands = {
try {
updateInfo = JSON.parse(fs.readFileSync('update.json', 'utf8'));
} catch (e) {
console.error('Failed to parse file `update.json`. Try to remove it manually.');
console.error(
'Failed to parse file `update.json`. Try to remove it manually.',
);
throw e;
}
}
const {appKey} = await get(`/app/${id}`);
const { appKey } = await get(`/app/${id}`);
updateInfo[platform] = {
appId: id,
appKey,
};
fs.writeFileSync('update.json', JSON.stringify(updateInfo, null, 4), 'utf8');
fs.writeFileSync(
'update.json',
JSON.stringify(updateInfo, null, 4),
'utf8',
);
},
}
};

View File

@@ -7,9 +7,9 @@ import { getRNVersion, translateOptions } from './utils';
import * as fs from 'fs-extra';
import { ZipFile } from 'yazl';
import { open as openZipFile } from 'yauzl';
import { question } from './utils';
import { question, printVersionCommand } from './utils';
import { checkPlatform } from './app';
const { spawn, spawnSync, execSync } = require('child_process');
const { spawn, spawnSync } = require('child_process');
const g2js = require('gradle-to-js/lib/parser');
const os = require('os');
@@ -18,24 +18,16 @@ try {
var bsdiff = require('node-bsdiff');
diff = typeof bsdiff != 'function' ? bsdiff.diff : bsdiff;
} catch (e) {
diff = function() {
diff = function () {
console.warn(
'This function needs "node-bsdiff". Please run "npm i node-bsdiff" from your project directory first!',
);
throw new Error('This function needs module "node-bsdiff". Please install it first.');
throw new Error(
'This function needs module "node-bsdiff". Please install it first.',
);
};
}
function exec(command) {
const commandResult = spawnSync(command, {
shell: true,
stdio: 'inherit',
});
if (commandResult.error) {
throw commandResult.error;
}
}
async function runReactNativeBundleCommand(
bundleName,
development,
@@ -45,19 +37,30 @@ async function runReactNativeBundleCommand(
sourcemapOutput,
config,
) {
let gradleConfig = {};
// if (platform === 'android') {
// gradleConfig = await checkGradleConfig();
// 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')
// }
// }
let reactNativeBundleArgs = [];
let envArgs = process.env.PUSHY_ENV_ARGS;
if (envArgs) {
Array.prototype.push.apply(reactNativeBundleArgs, envArgs.trim().split(/\s+/));
Array.prototype.push.apply(
reactNativeBundleArgs,
envArgs.trim().split(/\s+/),
);
}
fs.emptyDirSync(outputFolder);
Array.prototype.push.apply(reactNativeBundleArgs, [
path.join("node_modules", "react-native", "local-cli", "cli.js"),
"bundle",
path.join('node_modules', 'react-native', 'local-cli', 'cli.js'),
'bundle',
'--assets-dest',
outputFolder,
'--bundle-output',
@@ -80,22 +83,28 @@ async function runReactNativeBundleCommand(
}
const reactNativeBundleProcess = spawn('node', reactNativeBundleArgs);
console.log(`Running bundle command: node ${reactNativeBundleArgs.join(' ')}`);
console.log(
`Running bundle command: node ${reactNativeBundleArgs.join(' ')}`,
);
return new Promise((resolve, reject) => {
reactNativeBundleProcess.stdout.on('data', data => {
reactNativeBundleProcess.stdout.on('data', (data) => {
console.log(data.toString().trim());
});
reactNativeBundleProcess.stderr.on('data', data => {
reactNativeBundleProcess.stderr.on('data', (data) => {
console.error(data.toString().trim());
});
reactNativeBundleProcess.on('close', async exitCode => {
reactNativeBundleProcess.on('close', async (exitCode) => {
if (exitCode) {
reject(new Error(`"react-native bundle" command exited with code ${exitCode}.`));
reject(
new Error(
`"react-native bundle" command exited with code ${exitCode}.`,
),
);
} else {
if (platform === 'android') {
if (gradleConfig.enableHermes) {
await compileHermesByteCode(bundleName, outputFolder);
}
resolve(null);
@@ -110,30 +119,53 @@ function getHermesOSBin() {
if (os.platform() === 'linux') return 'linux64-bin';
}
async function compileHermesByteCode(bundleName, outputFolder) {
async function checkGradleConfig() {
let enableHermes = false;
let crunchPngs;
try {
const gradleConfig = await g2js.parseFile('android/app/build.gradle');
const projectConfig = gradleConfig['project.ext.react'];
crunchPngs = gradleConfig.android.buildTypes.release.crunchPngs;
for (const packagerConfig of projectConfig) {
if (packagerConfig.includes('enableHermes') && packagerConfig.includes('true')) {
if (
packagerConfig.includes('enableHermes') &&
packagerConfig.includes('true')
) {
enableHermes = true;
break;
}
}
} catch (e) {}
if (enableHermes) {
console.log(`Hermes enabled, now compiling to hermes bytecode:\n`);
const hermesPath = fs.existsSync('node_modules/hermes-engine')
? 'node_modules/hermes-engine'
: 'node_modules/hermesvm';
execSync(
`${hermesPath}/${getHermesOSBin()}/hermes -emit-binary -out ${outputFolder}/${bundleName} ${outputFolder}/${bundleName} -O`,
{ stdio: 'ignore' },
);
return {
enableHermes,
crunchPngs,
}
}
async function compileHermesByteCode(bundleName, outputFolder) {
console.log(`Hermes enabled, now compiling to hermes bytecode:\n`);
const hermesPackage = fs.existsSync('node_modules/hermes-engine')
? 'node_modules/hermes-engine' // 0.2+
: 'node_modules/hermesvm'; // < 0.2
const hermesPath = `${hermesPackage}/${getHermesOSBin()}`;
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' },
);
}
async function pack(dir, output) {
console.log('Packing');
fs.ensureDirSync(path.dirname(output));
@@ -163,10 +195,12 @@ async function pack(dir, output) {
addDirectory(dir, '');
zipfile.outputStream.on('error', err => reject(err));
zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function() {
resolve();
});
zipfile.outputStream.on('error', (err) => reject(err));
zipfile.outputStream
.pipe(fs.createWriteStream(output))
.on('close', function () {
resolve();
});
zipfile.end();
});
console.log('Bundled saved to: ' + output);
@@ -213,24 +247,30 @@ async function diffFromPPK(origin, next, output) {
if (entry.fileName === 'index.bundlejs') {
// This is source.
return readEntire(entry, zipFile).then(v => (originSource = v));
return readEntire(entry, zipFile).then((v) => (originSource = v));
}
}
});
originSource = originSource || new Buffer(0);
if (!originSource) {
throw new Error(
`Bundle file not found! Please use default bundle file name and path.`,
);
}
const copies = {};
var zipfile = new ZipFile();
const writePromise = new Promise((resolve, reject) => {
zipfile.outputStream.on('error', err => {
zipfile.outputStream.on('error', (err) => {
throw err;
});
zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function() {
resolve();
});
zipfile.outputStream
.pipe(fs.createWriteStream(output))
.on('close', function () {
resolve();
});
});
const addedEntry = {};
@@ -259,9 +299,12 @@ async function diffFromPPK(origin, next, output) {
}
} else if (entry.fileName === 'index.bundlejs') {
//console.log('Found bundle');
return readEntire(entry, nextZipfile).then(newSource => {
return readEntire(entry, nextZipfile).then((newSource) => {
//console.log('Begin diff');
zipfile.addBuffer(diff(originSource, newSource), 'index.bundlejs.patch');
zipfile.addBuffer(
diff(originSource, newSource),
'index.bundlejs.patch',
);
//console.log('End diff');
});
} else {
@@ -286,7 +329,7 @@ async function diffFromPPK(origin, next, output) {
addEntry(basename(entry.fileName));
return new Promise((resolve, reject) => {
nextZipfile.openReadStream(entry, function(err, readStream) {
nextZipfile.openReadStream(entry, function (err, readStream) {
if (err) {
return reject(err);
}
@@ -310,12 +353,21 @@ async function diffFromPPK(origin, next, output) {
}
//console.log({copies, deletes});
zipfile.addBuffer(new Buffer(JSON.stringify({ copies, deletes })), '__diff.json');
zipfile.addBuffer(
Buffer.from(JSON.stringify({ copies, deletes })),
'__diff.json',
);
zipfile.end();
await writePromise;
}
async function diffFromPackage(origin, next, output, originBundleName, transformPackagePath = v => v) {
async function diffFromPackage(
origin,
next,
output,
originBundleName,
transformPackagePath = (v) => v,
) {
fs.ensureDirSync(path.dirname(output));
const originEntries = {};
@@ -337,24 +389,30 @@ async function diffFromPackage(origin, next, output, originBundleName, transform
if (fn === originBundleName) {
// This is source.
return readEntire(entry, zipFile).then(v => (originSource = v));
return readEntire(entry, zipFile).then((v) => (originSource = v));
}
}
});
originSource = originSource || new Buffer(0);
if (!originSource) {
throw new Error(
`Bundle file not found! Please use default bundle file name and path.`,
);
}
const copies = {};
var zipfile = new ZipFile();
const writePromise = new Promise((resolve, reject) => {
zipfile.outputStream.on('error', err => {
zipfile.outputStream.on('error', (err) => {
throw err;
});
zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function() {
resolve();
});
zipfile.outputStream
.pipe(fs.createWriteStream(output))
.on('close', function () {
resolve();
});
});
await enumZipEntries(next, (entry, nextZipfile) => {
@@ -363,9 +421,12 @@ async function diffFromPackage(origin, next, output, originBundleName, transform
zipfile.addEmptyDirectory(entry.fileName);
} else if (entry.fileName === 'index.bundlejs') {
//console.log('Found bundle');
return readEntire(entry, nextZipfile).then(newSource => {
return readEntire(entry, nextZipfile).then((newSource) => {
//console.log('Begin diff');
zipfile.addBuffer(diff(originSource, newSource), 'index.bundlejs.patch');
zipfile.addBuffer(
diff(originSource, newSource),
'index.bundlejs.patch',
);
//console.log('End diff');
});
} else {
@@ -381,7 +442,7 @@ async function diffFromPackage(origin, next, output, originBundleName, transform
}
return new Promise((resolve, reject) => {
nextZipfile.openReadStream(entry, function(err, readStream) {
nextZipfile.openReadStream(entry, function (err, readStream) {
if (err) {
return reject(err);
}
@@ -395,7 +456,7 @@ async function diffFromPackage(origin, next, output, originBundleName, transform
}
});
zipfile.addBuffer(new Buffer(JSON.stringify({ copies })), '__diff.json');
zipfile.addBuffer(Buffer.from(JSON.stringify({ copies })), '__diff.json');
zipfile.end();
await writePromise;
}
@@ -408,7 +469,7 @@ function enumZipEntries(zipFn, callback) {
}
zipfile.on('end', resolve);
zipfile.on('error', reject);
zipfile.on('entry', entry => {
zipfile.on('entry', (entry) => {
const result = callback(entry, zipfile);
if (result && typeof result.then === 'function') {
result.then(() => zipfile.readEntry());
@@ -422,10 +483,19 @@ function enumZipEntries(zipFn, callback) {
}
export const commands = {
bundle: async function({ options }) {
const platform = checkPlatform(options.platform || (await question('Platform(ios/android):')));
bundle: async function ({ options }) {
const platform = checkPlatform(
options.platform || (await question('Platform(ios/android):')),
);
let { bundleName, entryFile, intermediaDir, output, dev, verbose } = translateOptions({
let {
bundleName,
entryFile,
intermediaDir,
output,
dev,
verbose,
} = translateOptions({
...options,
platform,
});
@@ -440,9 +510,16 @@ export const commands = {
const { version, major, minor } = getRNVersion();
console.log('Bundling with React Native version: ', version);
console.log('Bundling with react-native: ', version);
printVersionCommand();
await runReactNativeBundleCommand(bundleName, dev, entryFile, intermediaDir, platform);
await runReactNativeBundleCommand(
bundleName,
dev,
entryFile,
intermediaDir,
platform,
);
await pack(path.resolve(intermediaDir), realOutput);
@@ -483,7 +560,12 @@ export const commands = {
process.exit(1);
}
await diffFromPackage(origin, next, realOutput, 'assets/index.android.bundle');
await diffFromPackage(
origin,
next,
realOutput,
'assets/index.android.bundle',
);
console.log(`${realOutput} generated.`);
},
@@ -498,7 +580,7 @@ export const commands = {
process.exit(1);
}
await diffFromPackage(origin, next, realOutput, 'main.jsbundle', v => {
await diffFromPackage(origin, next, realOutput, 'main.jsbundle', (v) => {
const m = /^Payload\/[^/]+\/(.+)$/.exec(v);
return m && m[1];
});

View File

@@ -5,9 +5,10 @@
const {loadSession} = require('./api');
const updateNotifier = require('update-notifier');
import { printVersionCommand } from './utils/index.js';
const pkg = require('../package.json');
updateNotifier({pkg}).notify();
updateNotifier({pkg}).notify({isGlobal: true});
function printUsage({args}) {
// const commandName = args[0];
@@ -18,25 +19,6 @@ function printUsage({args}) {
process.exit(1);
}
function printVersionCommand() {
if (process.argv.indexOf('-v') >= 0 || process.argv[2] === 'version') {
console.log('react-native-update-cli: ' + pkg.version);
try {
const PACKAGE_JSON_PATH = path.resolve(
process.cwd(),
'node_modules',
'react-native-update',
'package.json'
);
console.log('react-native-update: ' + require(PACKAGE_JSON_PATH).version);
} catch (e) {
console.log('react-native-update: n/a - not inside a React Native project directory')
}
process.exit();
}
}
const commands = {
...require('./user').commands,
...require('./bundle').commands,
@@ -47,7 +29,10 @@ const commands = {
};
function run() {
printVersionCommand();
if (process.argv.indexOf('-v') >= 0 || process.argv[2] === 'version') {
printVersionCommand();
process.exit();
}
const argv = require('cli-arguments').parse(require('../cli.json'));
global.NO_INTERACTIVE = argv.options['no-interactive'];

View File

@@ -3,7 +3,7 @@
*/
const { get, post, uploadFile } = require('./api');
import { question } from './utils';
import { question, saveToLocal } from './utils';
import { checkPlatform, getSelectedApp } from './app';
@@ -19,7 +19,9 @@ export async function listPackage(appId) {
const { version } = pkg;
let versionInfo = '';
if (version) {
versionInfo = ` - ${version.id} ${version.hash.slice(0, 8)} ${version.name}`;
versionInfo = ` - ${version.id} ${version.hash.slice(0, 8)} ${
version.name
}`;
} else {
versionInfo = ' (newest)';
}
@@ -37,7 +39,7 @@ export async function choosePackage(appId) {
while (true) {
const id = await question('Enter Package Id:');
const app = list.find(v => v.id === (id | 0));
const app = list.find((v) => v.id === (id | 0));
if (app) {
return app;
}
@@ -45,13 +47,30 @@ export async function choosePackage(appId) {
}
export const commands = {
uploadIpa: async function({ args }) {
uploadIpa: async function ({ args }) {
const fn = args[0];
if (!fn) {
if (!fn || !fn.endsWith('.ipa')) {
throw new Error('Usage: pushy uploadIpa <ipaFile>');
}
const { versionName, buildTime } = await getIpaInfo(fn);
const { appId } = await getSelectedApp('ios');
const {
versionName,
buildTime,
appId: appIdInPkg,
appKey: appKeyInPkg,
} = await getIpaInfo(fn);
const { appId, appKey } = await getSelectedApp('ios');
if (appIdInPkg && appIdInPkg !== appId) {
throw new Error(
`appId不匹配当前ipa${appIdInPkg}, 当前update.json${appId}`,
);
}
if (appKeyInPkg && appKeyInPkg !== appKey) {
throw new Error(
`appKey不匹配当前ipa${appKeyInPkg}, 当前update.json${appKey}`,
);
}
const { hash } = await uploadFile(fn);
@@ -60,15 +79,33 @@ export const commands = {
hash,
buildTime,
});
saveToLocal(fn, `${appId}/package/${id}.ipa`);
console.log(`Ipa uploaded: ${id}`);
},
uploadApk: async function({ args }) {
uploadApk: async function ({ args }) {
const fn = args[0];
if (!fn) {
if (!fn || !fn.endsWith('.apk')) {
throw new Error('Usage: pushy uploadApk <apkFile>');
}
const { versionName, buildTime } = await getApkInfo(fn);
const { appId } = await getSelectedApp('android');
const {
versionName,
buildTime,
appId: appIdInPkg,
appKey: appKeyInPkg,
} = await getApkInfo(fn);
const { appId, appKey } = await getSelectedApp('android');
if (appIdInPkg && appIdInPkg !== appId) {
throw new Error(
`appId不匹配当前apk${appIdInPkg}, 当前update.json${appId}`,
);
}
if (appKeyInPkg && appKeyInPkg !== appKey) {
throw new Error(
`appKey不匹配当前apk${appKeyInPkg}, 当前update.json${appKey}`,
);
}
const { hash } = await uploadFile(fn);
@@ -77,10 +114,27 @@ export const commands = {
hash,
buildTime,
});
saveToLocal(fn, `${appId}/package/${id}.apk`);
console.log(`Apk uploaded: ${id}`);
},
packages: async function({ options }) {
const platform = checkPlatform(options.platform || (await question('Platform(ios/android):')));
parseIpa: async function ({ args }) {
const fn = args[0];
if (!fn || !fn.endsWith('.ipa')) {
throw new Error('Usage: pushy parseIpa <ipaFile>');
}
console.log(await getIpaInfo(fn));
},
parseApk: async function ({ args }) {
const fn = args[0];
if (!fn || !fn.endsWith('.apk')) {
throw new Error('Usage: pushy parseApk <apkFile>');
}
console.log(await getApkInfo(fn));
},
packages: async function ({ options }) {
const platform = checkPlatform(
options.platform || (await question('Platform(ios/android):')),
);
const { appId } = await getSelectedApp(platform);
await listPackage(appId);
},

View File

@@ -2,8 +2,10 @@
* Created by tdzl2003 on 2/13/16.
*/
import * as path from 'path';
import * as fs from 'fs-extra';
import fs from 'fs-extra';
import os from 'os';
import path from 'path';
const pkg = require('../../package.json');
const AppInfoParser = require('app-info-parser');
var read = require('read');
@@ -29,7 +31,7 @@ export function translateOptions(options) {
for (let key in options) {
const v = options[key];
if (typeof v === 'string') {
ret[key] = v.replace(/\$\{(\w+)\}/g, function(v, n) {
ret[key] = v.replace(/\$\{(\w+)\}/g, function (v, n) {
return options[n] || process.env[n] || v;
});
} else {
@@ -40,7 +42,9 @@ export function translateOptions(options) {
}
export function getRNVersion() {
const version = JSON.parse(fs.readFileSync(path.resolve('node_modules/react-native/package.json'))).version;
const version = JSON.parse(
fs.readFileSync(path.resolve('node_modules/react-native/package.json')),
).version;
// We only care about major and minor version.
const match = /^(\d+)\.(\d+)\./.exec(version);
@@ -53,6 +57,21 @@ export function getRNVersion() {
export async function getApkInfo(fn) {
const appInfoParser = new AppInfoParser(fn);
const bundleFile = await appInfoParser.parser.getEntry(
/assets\/index.android.bundle/,
);
if (!bundleFile) {
throw new Error(
'找不到bundle文件。请确保此apk为release版本且bundle文件名为默认的index.android.bundle',
);
}
const updateJsonFile = await appInfoParser.parser.getEntry(
/res\/raw\/update.json/,
);
let appCredential = {};
if (updateJsonFile) {
appCredential = JSON.parse(updateJsonFile.toString()).android;
}
const { versionName, application } = await appInfoParser.parse();
let buildTime = 0;
if (Array.isArray(application.metaData)) {
@@ -63,22 +82,73 @@ export async function getApkInfo(fn) {
}
}
if (buildTime == 0) {
throw new Error('Can not get build time for this app.');
throw new Error(
'无法获取此包的编译时间戳。请更新react-native-update到最新版本后重新打包上传。',
);
}
return { versionName, buildTime };
return { versionName, buildTime, ...appCredential };
}
export async function getIpaInfo(fn) {
const appInfoParser = new AppInfoParser(fn);
const { CFBundleShortVersionString: versionName } = await appInfoParser.parse();
let buildTimeTxtBuffer = await appInfoParser.parser.getEntry(/payload\/.+?\.app\/pushy_build_time.txt/);
const bundleFile = await appInfoParser.parser.getEntry(
/payload\/.+?\.app\/main.jsbundle/,
);
if (!bundleFile) {
throw new Error(
'找不到bundle文件。请确保此ipa为release版本且bundle文件名为默认的main.jsbundle',
);
}
const updateJsonFile = await appInfoParser.parser.getEntry(
/payload\/.+?\.app\/assets\/update.json/,
);
let appCredential = {};
if (updateJsonFile) {
appCredential = JSON.parse(updateJsonFile.toString()).ios;
}
const {
CFBundleShortVersionString: versionName,
} = await appInfoParser.parse();
let buildTimeTxtBuffer = await appInfoParser.parser.getEntry(
/payload\/.+?\.app\/pushy_build_time.txt/,
);
if (!buildTimeTxtBuffer) {
// Not in root bundle when use `use_frameworks`
buildTimeTxtBuffer = await appInfoParser.parser.getEntry(/payload\/.+?\.app\/frameworks\/react_native_update.framework\/pushy_build_time.txt/);
buildTimeTxtBuffer = await appInfoParser.parser.getEntry(
/payload\/.+?\.app\/frameworks\/react_native_update.framework\/pushy_build_time.txt/,
);
}
if (!buildTimeTxtBuffer) {
throw new Error('Can not get build time for this app.');
throw new Error(
'无法获取此包的编译时间戳。请更新react-native-update到最新版本后重新打包上传。',
);
}
const buildTime = buildTimeTxtBuffer.toString().replace('\n', '');
return { versionName, buildTime };
return { versionName, buildTime, ...appCredential };
}
const localDir = path.resolve(os.homedir(), '.pushy');
fs.ensureDirSync(localDir);
export function saveToLocal(originPath, destName) {
// TODO
// const destPath = path.join(localDir, destName);
// fs.ensureDirSync(path.dirname(destPath));
// fs.copyFileSync(originPath, destPath);
}
export function printVersionCommand() {
console.log('react-native-update-cli: ' + pkg.version);
try {
const PACKAGE_JSON_PATH = path.resolve(
process.cwd(),
'node_modules',
'react-native-update',
'package.json',
);
console.log('react-native-update: ' + require(PACKAGE_JSON_PATH).version);
} catch (e) {
console.log('react-native-update: 无法获取版本号,请在项目目录中运行命令');
}
}
export const pricingPageUrl = 'https://pushy.reactnative.cn/pricing.html';

View File

@@ -8,7 +8,7 @@ const {
put,
uploadFile,
} = require('./api');
import { question } from './utils';
import { question, saveToLocal } from './utils';
import { checkPlatform, getSelectedApp } from './app';
import { choosePackage } from './package';
@@ -86,6 +86,8 @@ export const commands = {
description: description || await question('Enter description:'),
metaInfo: metaInfo || await question('Enter meta info:'),
});
// TODO local diff
saveToLocal(fn, `${appId}/ppk/${id}.ppk`);
console.log(`Version published: ${id}`);
const v = await question('Would you like to bind packages to this version?(Y/N)');

1441
yarn.lock

File diff suppressed because it is too large Load Diff