mirror of
				https://gitcode.com/github-mirrors/react-native-update-cli.git
				synced 2025-10-31 23:03:11 +08:00 
			
		
		
		
	initial commit
This commit is contained in:
		
							
								
								
									
										14
									
								
								.babelrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								.babelrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | { | ||||||
|  |   "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" | ||||||
|  |   ] | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								.prettierrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.prettierrc.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | module.exports = { | ||||||
|  |   trailingComma: 'all', | ||||||
|  |   singleQuote: true, | ||||||
|  | }; | ||||||
							
								
								
									
										162
									
								
								cli.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								cli.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | |||||||
|  | { | ||||||
|  |   "useCommand": true, | ||||||
|  |   "defaultCommand": "help", | ||||||
|  |   "commands": { | ||||||
|  |     "help": { | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     "login":{ | ||||||
|  |     }, | ||||||
|  |     "logout": { | ||||||
|  |     }, | ||||||
|  |     "me": { | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     "createApp": { | ||||||
|  |       "options": { | ||||||
|  |         "name": { | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "platform": { | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "apps": { | ||||||
|  |       "options": { | ||||||
|  |         "platform": { | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "deleteApp": { | ||||||
|  |     }, | ||||||
|  |     "selectApp": { | ||||||
|  |       "options": { | ||||||
|  |         "platform": { | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     "uploadIpa": { | ||||||
|  |     }, | ||||||
|  |     "uploadApk": { | ||||||
|  |     }, | ||||||
|  |     "packages": { | ||||||
|  |       "options": { | ||||||
|  |         "platform": { | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     "publish": { | ||||||
|  |       "options": { | ||||||
|  |         "platform": { | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "name": { | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "description": { | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "metaInfo": { | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "versions": { | ||||||
|  |       "options": { | ||||||
|  |         "platform": { | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     "update": { | ||||||
|  |       "options": { | ||||||
|  |         "platform": { | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "versionId": { | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "packageId": { | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     "build": { | ||||||
|  |       "description": "Bundle javascript and copy assets." | ||||||
|  |     }, | ||||||
|  |     "bundle": { | ||||||
|  |       "description": "Bundle javascript code only.", | ||||||
|  |       "options": { | ||||||
|  |         "dev": { | ||||||
|  |           "default": "false", | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "platform": { | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "bundleName":{ | ||||||
|  |           "default": "index.bundlejs", | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "entryFile": { | ||||||
|  |           "default": "index.js", | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "intermediaDir": { | ||||||
|  |           "default": "build/intermedia/${platform}", | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "output": { | ||||||
|  |           "default": "build/output/${platform}.${time}.ppk", | ||||||
|  |           "hasValue": true | ||||||
|  |         }, | ||||||
|  |         "verbose": { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "release": { | ||||||
|  |       "description": "Push builded file to server." | ||||||
|  |     }, | ||||||
|  |     "diff": { | ||||||
|  |       "description": "Create diff patch", | ||||||
|  |       "options": { | ||||||
|  |         "output": { | ||||||
|  |           "default": "build/output/diff", | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "diffFromApk": { | ||||||
|  |       "description": "Create diff patch from a Android package(.apk)", | ||||||
|  |       "options": { | ||||||
|  |         "output": { | ||||||
|  |           "default": "build/output/diff-${time}.apk-patch", | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "diffFromIpa": { | ||||||
|  |       "description": "Create diff patch from a iOS package(.ipa)", | ||||||
|  |       "options": { | ||||||
|  |         "output": { | ||||||
|  |           "default": "build/output/diff-${time}.ipa-patch", | ||||||
|  |           "hasValue": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "globalOptions":{ | ||||||
|  |     "no-interactive": { | ||||||
|  |       "default": false | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 2/22/16. | ||||||
|  |  */ | ||||||
|  | module.exports = require('./lib'); | ||||||
							
								
								
									
										158
									
								
								lib/api.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								lib/api.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | let query = function () { | ||||||
|  |   var _ref2 = _asyncToGenerator(function* (url, options) { | ||||||
|  |     const resp = yield fetch(url, options); | ||||||
|  |     const json = yield resp.json(); | ||||||
|  |     if (resp.status !== 200) { | ||||||
|  |       throw Object.assign(new Error(json.message || json.error), { status: resp.status }); | ||||||
|  |     } | ||||||
|  |     return json; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function query(_x, _x2) { | ||||||
|  |     return _ref2.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let uploadFile = function () { | ||||||
|  |   var _ref3 = _asyncToGenerator(function* (fn) { | ||||||
|  |     var _ref4 = yield exports.post('/upload', {}); | ||||||
|  |  | ||||||
|  |     const url = _ref4.url, | ||||||
|  |           fieldName = _ref4.fieldName, | ||||||
|  |           formData = _ref4.formData; | ||||||
|  |  | ||||||
|  |     let realUrl = url; | ||||||
|  |  | ||||||
|  |     if (!/^https?\:\/\//.test(url)) { | ||||||
|  |       realUrl = host + url; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const fileSize = fs.statSync(fn).size; | ||||||
|  |  | ||||||
|  |     const bar = new _progress2.default('  Uploading [:bar] :percent :etas', { | ||||||
|  |       complete: '=', | ||||||
|  |       incomplete: ' ', | ||||||
|  |       total: fileSize | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const info = yield new Promise(function (resolve, reject) { | ||||||
|  |       formData.file = fs.createReadStream(fn); | ||||||
|  |  | ||||||
|  |       formData.file.on('data', function (data) { | ||||||
|  |         bar.tick(data.length); | ||||||
|  |       }); | ||||||
|  |       _request2.default.post(realUrl, { | ||||||
|  |         formData | ||||||
|  |       }, function (err, resp, body) { | ||||||
|  |         if (err) { | ||||||
|  |           return reject(err); | ||||||
|  |         } | ||||||
|  |         if (resp.statusCode > 299) { | ||||||
|  |           return reject(Object.assign(new Error(body), { status: resp.statusCode })); | ||||||
|  |         } | ||||||
|  |         resolve(JSON.parse(body)); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |     return info; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function uploadFile(_x3) { | ||||||
|  |     return _ref3.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | var _request = require('request'); | ||||||
|  |  | ||||||
|  | var _request2 = _interopRequireDefault(_request); | ||||||
|  |  | ||||||
|  | var _progress = require('progress'); | ||||||
|  |  | ||||||
|  | var _progress2 = _interopRequireDefault(_progress); | ||||||
|  |  | ||||||
|  | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||||||
|  |  | ||||||
|  | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 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'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | let session = undefined; | ||||||
|  | let savedSession = undefined; | ||||||
|  |  | ||||||
|  | exports.loadSession = _asyncToGenerator(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.'); | ||||||
|  |       throw e; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | exports.getSession = function () { | ||||||
|  |   return session; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.replaceSession = function (newSession) { | ||||||
|  |   session = newSession; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.saveSession = function () { | ||||||
|  |   // Only save on change. | ||||||
|  |   if (session !== savedSession) { | ||||||
|  |     const current = session; | ||||||
|  |     const data = JSON.stringify(current, null, 4); | ||||||
|  |     fs.writeFileSync('.update', data, 'utf8'); | ||||||
|  |     savedSession = current; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.closeSession = function () { | ||||||
|  |   if (fs.existsSync('.update')) { | ||||||
|  |     fs.unlinkSync('.update'); | ||||||
|  |     savedSession = undefined; | ||||||
|  |   } | ||||||
|  |   session = undefined; | ||||||
|  |   host = process.env.PUSHY_REGISTRY || 'https://update.reactnative.cn'; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | function queryWithoutBody(method) { | ||||||
|  |   return function (api) { | ||||||
|  |     return query(host + api, { | ||||||
|  |       method, | ||||||
|  |       headers: { | ||||||
|  |         'X-AccessToken': session ? session.token : '' | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function queryWithBody(method) { | ||||||
|  |   return function (api, body) { | ||||||
|  |     return query(host + api, { | ||||||
|  |       method, | ||||||
|  |       headers: { | ||||||
|  |         'Content-Type': 'application/json', | ||||||
|  |         'X-AccessToken': session ? session.token : '' | ||||||
|  |       }, | ||||||
|  |       body: JSON.stringify(body) | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | exports.get = queryWithoutBody('GET'); | ||||||
|  | exports.post = queryWithBody('POST'); | ||||||
|  | exports.put = queryWithBody('PUT'); | ||||||
|  | exports.doDelete = queryWithBody('DELETE'); | ||||||
|  |  | ||||||
|  | exports.uploadFile = uploadFile; | ||||||
							
								
								
									
										188
									
								
								lib/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								lib/app.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | Object.defineProperty(exports, "__esModule", { | ||||||
|  |   value: true | ||||||
|  | }); | ||||||
|  | exports.commands = exports.chooseApp = exports.listApp = undefined; | ||||||
|  |  | ||||||
|  | let listApp = exports.listApp = function () { | ||||||
|  |   var _ref = _asyncToGenerator(function* (platform) { | ||||||
|  |     var _ref2 = yield get('/app/list'); | ||||||
|  |  | ||||||
|  |     const data = _ref2.data; | ||||||
|  |  | ||||||
|  |     const list = platform ? data.filter(function (v) { | ||||||
|  |       return v.platform === platform; | ||||||
|  |     }) : data; | ||||||
|  |     for (const app of list) { | ||||||
|  |       console.log(`${app.id}) ${app.name}(${app.platform})`); | ||||||
|  |     } | ||||||
|  |     if (platform) { | ||||||
|  |       console.log(`\nTotal ${list.length} ${platform} apps`); | ||||||
|  |     } else { | ||||||
|  |       console.log(`\nTotal ${list.length} apps`); | ||||||
|  |     } | ||||||
|  |     return list; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function listApp(_x) { | ||||||
|  |     return _ref.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let chooseApp = exports.chooseApp = function () { | ||||||
|  |   var _ref3 = _asyncToGenerator(function* (platform) { | ||||||
|  |     const list = yield listApp(platform); | ||||||
|  |  | ||||||
|  |     while (true) { | ||||||
|  |       const id = yield (0, _utils.question)('Enter appId:'); | ||||||
|  |       const app = list.find(function (v) { | ||||||
|  |         return v.id === (id | 0); | ||||||
|  |       }); | ||||||
|  |       if (app) { | ||||||
|  |         return app; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function chooseApp(_x2) { | ||||||
|  |     return _ref3.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | exports.checkPlatform = checkPlatform; | ||||||
|  | exports.getSelectedApp = getSelectedApp; | ||||||
|  |  | ||||||
|  | var _utils = require('./utils'); | ||||||
|  |  | ||||||
|  | var _fsExtra = require('fs-extra'); | ||||||
|  |  | ||||||
|  | var fs = _interopRequireWildcard(_fsExtra); | ||||||
|  |  | ||||||
|  | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||||||
|  |  | ||||||
|  | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } /** | ||||||
|  |                                                                                                                                                                                                                                                                                                                                                                                                                                                                             * Created by tdzl2003 on 2/13/16. | ||||||
|  |                                                                                                                                                                                                                                                                                                                                                                                                                                                                             */ | ||||||
|  |  | ||||||
|  | var _require = require('./api'); | ||||||
|  |  | ||||||
|  | const post = _require.post, | ||||||
|  |       get = _require.get, | ||||||
|  |       doDelete = _require.doDelete; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | const validPlatforms = { | ||||||
|  |   ios: 1, | ||||||
|  |   android: 1 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | function checkPlatform(platform) { | ||||||
|  |   if (!validPlatforms[platform]) { | ||||||
|  |     throw new Error(`Invalid platform '${platform}'`); | ||||||
|  |   } | ||||||
|  |   return platform; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function getSelectedApp(platform) { | ||||||
|  |   checkPlatform(platform); | ||||||
|  |  | ||||||
|  |   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!`); | ||||||
|  |   } | ||||||
|  |   return updateInfo[platform]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const commands = exports.commands = { | ||||||
|  |   createApp: function () { | ||||||
|  |     var _ref4 = _asyncToGenerator(function* (_ref5) { | ||||||
|  |       let options = _ref5.options; | ||||||
|  |  | ||||||
|  |       const name = options.name || (yield (0, _utils.question)('App Name:')); | ||||||
|  |       const downloadUrl = options.downloadUrl; | ||||||
|  |  | ||||||
|  |       const platform = checkPlatform(options.platform || (yield (0, _utils.question)('Platform(ios/android):'))); | ||||||
|  |  | ||||||
|  |       var _ref6 = yield post('/app/create', { name, platform }); | ||||||
|  |  | ||||||
|  |       const id = _ref6.id; | ||||||
|  |  | ||||||
|  |       console.log(`Created app ${id}`); | ||||||
|  |       yield this.selectApp({ | ||||||
|  |         args: [id], | ||||||
|  |         options: { platform, downloadUrl } | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function createApp(_x3) { | ||||||
|  |       return _ref4.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |   deleteApp: function () { | ||||||
|  |     var _ref7 = _asyncToGenerator(function* (_ref8) { | ||||||
|  |       let args = _ref8.args, | ||||||
|  |           options = _ref8.options; | ||||||
|  |       const platform = options.platform; | ||||||
|  |  | ||||||
|  |       const id = args[0] || chooseApp(platform); | ||||||
|  |       if (!id) { | ||||||
|  |         console.log('Canceled'); | ||||||
|  |       } | ||||||
|  |       yield doDelete(`/app/${id}`); | ||||||
|  |       console.log('Ok.'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function deleteApp(_x4) { | ||||||
|  |       return _ref7.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |   apps: function () { | ||||||
|  |     var _ref9 = _asyncToGenerator(function* (_ref10) { | ||||||
|  |       let options = _ref10.options; | ||||||
|  |       const platform = options.platform; | ||||||
|  |  | ||||||
|  |       listApp(platform); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function apps(_x5) { | ||||||
|  |       return _ref9.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |   selectApp: function () { | ||||||
|  |     var _ref11 = _asyncToGenerator(function* (_ref12) { | ||||||
|  |       let args = _ref12.args, | ||||||
|  |           options = _ref12.options; | ||||||
|  |  | ||||||
|  |       const platform = checkPlatform(options.platform || (yield (0, _utils.question)('Platform(ios/android):'))); | ||||||
|  |       const id = args[0] || (yield chooseApp(platform)).id; | ||||||
|  |  | ||||||
|  |       let updateInfo = {}; | ||||||
|  |       if (fs.existsSync('update.json')) { | ||||||
|  |         try { | ||||||
|  |           updateInfo = JSON.parse(fs.readFileSync('update.json', 'utf8')); | ||||||
|  |         } catch (e) { | ||||||
|  |           console.error('Failed to parse file `update.json`. Try to remove it manually.'); | ||||||
|  |           throw e; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       var _ref13 = yield get(`/app/${id}`); | ||||||
|  |  | ||||||
|  |       const appKey = _ref13.appKey; | ||||||
|  |  | ||||||
|  |       updateInfo[platform] = { | ||||||
|  |         appId: id, | ||||||
|  |         appKey | ||||||
|  |       }; | ||||||
|  |       fs.writeFileSync('update.json', JSON.stringify(updateInfo, null, 4), 'utf8'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function selectApp(_x6) { | ||||||
|  |       return _ref11.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }() | ||||||
|  | }; | ||||||
							
								
								
									
										597
									
								
								lib/bundle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										597
									
								
								lib/bundle.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,597 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | Object.defineProperty(exports, "__esModule", { | ||||||
|  |   value: true | ||||||
|  | }); | ||||||
|  | exports.commands = undefined; | ||||||
|  |  | ||||||
|  | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); | ||||||
|  |  | ||||||
|  | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||||||
|  |  | ||||||
|  | let runReactNativeBundleCommand = function () { | ||||||
|  |   var _ref = _asyncToGenerator(function* (bundleName, development, entryFile, outputFolder, platform, sourcemapOutput, config) { | ||||||
|  |     let reactNativeBundleArgs = []; | ||||||
|  |  | ||||||
|  |     let envArgs = process.env.PUSHY_ENV_ARGS; | ||||||
|  |  | ||||||
|  |     if (envArgs) { | ||||||
|  |       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", '--assets-dest', outputFolder, '--bundle-output', path.join(outputFolder, bundleName), '--dev', development, '--entry-file', entryFile, '--platform', platform, '--reset-cache']); | ||||||
|  |  | ||||||
|  |     if (sourcemapOutput) { | ||||||
|  |       reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (config) { | ||||||
|  |       reactNativeBundleArgs.push('--config', config); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const reactNativeBundleProcess = spawn('node', reactNativeBundleArgs); | ||||||
|  |     console.log(`Running bundle command: node ${reactNativeBundleArgs.join(' ')}`); | ||||||
|  |  | ||||||
|  |     return new Promise(function (resolve, reject) { | ||||||
|  |       reactNativeBundleProcess.stdout.on('data', function (data) { | ||||||
|  |         console.log(data.toString().trim()); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       reactNativeBundleProcess.stderr.on('data', function (data) { | ||||||
|  |         console.error(data.toString().trim()); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       reactNativeBundleProcess.on('close', function () { | ||||||
|  |         var _ref2 = _asyncToGenerator(function* (exitCode) { | ||||||
|  |           if (exitCode) { | ||||||
|  |             reject(new Error(`"react-native bundle" command exited with code ${exitCode}.`)); | ||||||
|  |           } else { | ||||||
|  |             if (platform === 'android') { | ||||||
|  |               yield compileHermesByteCode(bundleName, outputFolder); | ||||||
|  |             } | ||||||
|  |             resolve(null); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         return function (_x8) { | ||||||
|  |           return _ref2.apply(this, arguments); | ||||||
|  |         }; | ||||||
|  |       }()); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function runReactNativeBundleCommand(_x, _x2, _x3, _x4, _x5, _x6, _x7) { | ||||||
|  |     return _ref.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let compileHermesByteCode = function () { | ||||||
|  |   var _ref3 = _asyncToGenerator(function* (bundleName, outputFolder) { | ||||||
|  |     let enableHermes = false; | ||||||
|  |     try { | ||||||
|  |       const gradleConfig = yield g2js.parseFile('android/app/build.gradle'); | ||||||
|  |       const projectConfig = gradleConfig['project.ext.react']; | ||||||
|  |       for (const packagerConfig of projectConfig) { | ||||||
|  |         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 function compileHermesByteCode(_x9, _x10) { | ||||||
|  |     return _ref3.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let pack = function () { | ||||||
|  |   var _ref4 = _asyncToGenerator(function* (dir, output) { | ||||||
|  |     console.log('Packing'); | ||||||
|  |     fs.ensureDirSync(path.dirname(output)); | ||||||
|  |     yield new Promise(function (resolve, reject) { | ||||||
|  |       var zipfile = new _yazl.ZipFile(); | ||||||
|  |  | ||||||
|  |       function addDirectory(root, rel) { | ||||||
|  |         if (rel) { | ||||||
|  |           zipfile.addEmptyDirectory(rel); | ||||||
|  |         } | ||||||
|  |         const childs = fs.readdirSync(root); | ||||||
|  |         for (const name of childs) { | ||||||
|  |           if (name === '.' || name === '..') { | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |           const fullPath = path.join(root, name); | ||||||
|  |           const stat = fs.statSync(fullPath); | ||||||
|  |           if (stat.isFile()) { | ||||||
|  |             //console.log('adding: ' + rel+name); | ||||||
|  |             zipfile.addFile(fullPath, rel + name); | ||||||
|  |           } else if (stat.isDirectory()) { | ||||||
|  |             //console.log('adding: ' + rel+name+'/'); | ||||||
|  |             addDirectory(fullPath, rel + name + '/'); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       addDirectory(dir, ''); | ||||||
|  |  | ||||||
|  |       zipfile.outputStream.on('error', function (err) { | ||||||
|  |         return reject(err); | ||||||
|  |       }); | ||||||
|  |       zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function () { | ||||||
|  |         resolve(); | ||||||
|  |       }); | ||||||
|  |       zipfile.end(); | ||||||
|  |     }); | ||||||
|  |     console.log('Bundled saved to: ' + output); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function pack(_x11, _x12) { | ||||||
|  |     return _ref4.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let diffFromPPK = function () { | ||||||
|  |   var _ref5 = _asyncToGenerator(function* (origin, next, output) { | ||||||
|  |     fs.ensureDirSync(path.dirname(output)); | ||||||
|  |  | ||||||
|  |     const originEntries = {}; | ||||||
|  |     const originMap = {}; | ||||||
|  |  | ||||||
|  |     let originSource; | ||||||
|  |  | ||||||
|  |     yield enumZipEntries(origin, function (entry, zipFile) { | ||||||
|  |       originEntries[entry.fileName] = entry; | ||||||
|  |       if (!/\/$/.test(entry.fileName)) { | ||||||
|  |         // isFile | ||||||
|  |         originMap[entry.crc32] = entry.fileName; | ||||||
|  |  | ||||||
|  |         if (entry.fileName === 'index.bundlejs') { | ||||||
|  |           // This is source. | ||||||
|  |           return readEntire(entry, zipFile).then(function (v) { | ||||||
|  |             return originSource = v; | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     originSource = originSource || new Buffer(0); | ||||||
|  |  | ||||||
|  |     const copies = {}; | ||||||
|  |  | ||||||
|  |     var zipfile = new _yazl.ZipFile(); | ||||||
|  |  | ||||||
|  |     const writePromise = new Promise(function (resolve, reject) { | ||||||
|  |       zipfile.outputStream.on('error', function (err) { | ||||||
|  |         throw err; | ||||||
|  |       }); | ||||||
|  |       zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function () { | ||||||
|  |         resolve(); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const addedEntry = {}; | ||||||
|  |  | ||||||
|  |     function addEntry(fn) { | ||||||
|  |       //console.log(fn); | ||||||
|  |       if (!fn || addedEntry[fn]) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       const base = basename(fn); | ||||||
|  |       if (base) { | ||||||
|  |         addEntry(base); | ||||||
|  |       } | ||||||
|  |       zipfile.addEmptyDirectory(fn); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const newEntries = {}; | ||||||
|  |  | ||||||
|  |     yield enumZipEntries(next, function (entry, nextZipfile) { | ||||||
|  |       newEntries[entry.fileName] = entry; | ||||||
|  |  | ||||||
|  |       if (/\/$/.test(entry.fileName)) { | ||||||
|  |         // Directory | ||||||
|  |         if (!originEntries[entry.fileName]) { | ||||||
|  |           addEntry(entry.fileName); | ||||||
|  |         } | ||||||
|  |       } else if (entry.fileName === 'index.bundlejs') { | ||||||
|  |         //console.log('Found bundle'); | ||||||
|  |         return readEntire(entry, nextZipfile).then(function (newSource) { | ||||||
|  |           //console.log('Begin diff'); | ||||||
|  |           zipfile.addBuffer(diff(originSource, newSource), 'index.bundlejs.patch'); | ||||||
|  |           //console.log('End diff'); | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         // If same file. | ||||||
|  |         const originEntry = originEntries[entry.fileName]; | ||||||
|  |         if (originEntry && originEntry.crc32 === entry.crc32) { | ||||||
|  |           // ignore | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // If moved from other place | ||||||
|  |         if (originMap[entry.crc32]) { | ||||||
|  |           const base = basename(entry.fileName); | ||||||
|  |           if (!originEntries[base]) { | ||||||
|  |             addEntry(base); | ||||||
|  |           } | ||||||
|  |           copies[entry.fileName] = originMap[entry.crc32]; | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // New file. | ||||||
|  |         addEntry(basename(entry.fileName)); | ||||||
|  |  | ||||||
|  |         return new Promise(function (resolve, reject) { | ||||||
|  |           nextZipfile.openReadStream(entry, function (err, readStream) { | ||||||
|  |             if (err) { | ||||||
|  |               return reject(err); | ||||||
|  |             } | ||||||
|  |             zipfile.addReadStream(readStream, entry.fileName); | ||||||
|  |             readStream.on('end', function () { | ||||||
|  |               //console.log('add finished'); | ||||||
|  |               resolve(); | ||||||
|  |             }); | ||||||
|  |           }); | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const deletes = {}; | ||||||
|  |  | ||||||
|  |     for (var k in originEntries) { | ||||||
|  |       if (!newEntries[k]) { | ||||||
|  |         console.log('Delete ' + k); | ||||||
|  |         deletes[k] = 1; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     //console.log({copies, deletes}); | ||||||
|  |     zipfile.addBuffer(new Buffer(JSON.stringify({ copies, deletes })), '__diff.json'); | ||||||
|  |     zipfile.end(); | ||||||
|  |     yield writePromise; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function diffFromPPK(_x13, _x14, _x15) { | ||||||
|  |     return _ref5.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let diffFromPackage = function () { | ||||||
|  |   var _ref6 = _asyncToGenerator(function* (origin, next, output, originBundleName) { | ||||||
|  |     let transformPackagePath = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : function (v) { | ||||||
|  |       return v; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     fs.ensureDirSync(path.dirname(output)); | ||||||
|  |  | ||||||
|  |     const originEntries = {}; | ||||||
|  |     const originMap = {}; | ||||||
|  |  | ||||||
|  |     let originSource; | ||||||
|  |  | ||||||
|  |     yield enumZipEntries(origin, function (entry, zipFile) { | ||||||
|  |       if (!/\/$/.test(entry.fileName)) { | ||||||
|  |         const fn = transformPackagePath(entry.fileName); | ||||||
|  |         if (!fn) { | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //console.log(fn); | ||||||
|  |         // isFile | ||||||
|  |         originEntries[fn] = entry.crc32; | ||||||
|  |         originMap[entry.crc32] = fn; | ||||||
|  |  | ||||||
|  |         if (fn === originBundleName) { | ||||||
|  |           // This is source. | ||||||
|  |           return readEntire(entry, zipFile).then(function (v) { | ||||||
|  |             return originSource = v; | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     originSource = originSource || new Buffer(0); | ||||||
|  |  | ||||||
|  |     const copies = {}; | ||||||
|  |  | ||||||
|  |     var zipfile = new _yazl.ZipFile(); | ||||||
|  |  | ||||||
|  |     const writePromise = new Promise(function (resolve, reject) { | ||||||
|  |       zipfile.outputStream.on('error', function (err) { | ||||||
|  |         throw err; | ||||||
|  |       }); | ||||||
|  |       zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function () { | ||||||
|  |         resolve(); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     yield enumZipEntries(next, function (entry, nextZipfile) { | ||||||
|  |       if (/\/$/.test(entry.fileName)) { | ||||||
|  |         // Directory | ||||||
|  |         zipfile.addEmptyDirectory(entry.fileName); | ||||||
|  |       } else if (entry.fileName === 'index.bundlejs') { | ||||||
|  |         //console.log('Found bundle'); | ||||||
|  |         return readEntire(entry, nextZipfile).then(function (newSource) { | ||||||
|  |           //console.log('Begin diff'); | ||||||
|  |           zipfile.addBuffer(diff(originSource, newSource), 'index.bundlejs.patch'); | ||||||
|  |           //console.log('End diff'); | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         // If same file. | ||||||
|  |         if (originEntries[entry.fileName] === entry.crc32) { | ||||||
|  |           copies[entry.fileName] = ''; | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |         // If moved from other place | ||||||
|  |         if (originMap[entry.crc32]) { | ||||||
|  |           copies[entry.fileName] = originMap[entry.crc32]; | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return new Promise(function (resolve, reject) { | ||||||
|  |           nextZipfile.openReadStream(entry, function (err, readStream) { | ||||||
|  |             if (err) { | ||||||
|  |               return reject(err); | ||||||
|  |             } | ||||||
|  |             zipfile.addReadStream(readStream, entry.fileName); | ||||||
|  |             readStream.on('end', function () { | ||||||
|  |               //console.log('add finished'); | ||||||
|  |               resolve(); | ||||||
|  |             }); | ||||||
|  |           }); | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     zipfile.addBuffer(new Buffer(JSON.stringify({ copies })), '__diff.json'); | ||||||
|  |     zipfile.end(); | ||||||
|  |     yield writePromise; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function diffFromPackage(_x16, _x17, _x18, _x19) { | ||||||
|  |     return _ref6.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | var _utils = require('./utils'); | ||||||
|  |  | ||||||
|  | var _fsExtra = require('fs-extra'); | ||||||
|  |  | ||||||
|  | var fs = _interopRequireWildcard(_fsExtra); | ||||||
|  |  | ||||||
|  | var _yazl = require('yazl'); | ||||||
|  |  | ||||||
|  | var _yauzl = require('yauzl'); | ||||||
|  |  | ||||||
|  | var _app = require('./app'); | ||||||
|  |  | ||||||
|  | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||||||
|  |  | ||||||
|  | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 2/22/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | const path = require('path'); | ||||||
|  |  | ||||||
|  | var _require = require('child_process'); | ||||||
|  |  | ||||||
|  | const spawn = _require.spawn, | ||||||
|  |       spawnSync = _require.spawnSync, | ||||||
|  |       execSync = _require.execSync; | ||||||
|  |  | ||||||
|  | const g2js = require('gradle-to-js/lib/parser'); | ||||||
|  | const os = require('os'); | ||||||
|  |  | ||||||
|  | var diff; | ||||||
|  | try { | ||||||
|  |   var bsdiff = require('node-bsdiff'); | ||||||
|  |   diff = typeof bsdiff != 'function' ? bsdiff.diff : bsdiff; | ||||||
|  | } catch (e) { | ||||||
|  |   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.'); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function exec(command) { | ||||||
|  |   const commandResult = spawnSync(command, { | ||||||
|  |     shell: true, | ||||||
|  |     stdio: 'inherit' | ||||||
|  |   }); | ||||||
|  |   if (commandResult.error) { | ||||||
|  |     throw commandResult.error; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function getHermesOSBin() { | ||||||
|  |   if (os.platform() === 'win32') return 'win64-bin'; | ||||||
|  |   if (os.platform() === 'darwin') return 'osx-bin'; | ||||||
|  |   if (os.platform() === 'linux') return 'linux64-bin'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function readEntire(entry, zipFile) { | ||||||
|  |   const buffers = []; | ||||||
|  |   return new Promise(function (resolve, reject) { | ||||||
|  |     zipFile.openReadStream(entry, function (err, stream) { | ||||||
|  |       stream.pipe({ | ||||||
|  |         write(chunk) { | ||||||
|  |           buffers.push(chunk); | ||||||
|  |         }, | ||||||
|  |         end() { | ||||||
|  |           resolve(Buffer.concat(buffers)); | ||||||
|  |         }, | ||||||
|  |         prependListener() {}, | ||||||
|  |         on() {}, | ||||||
|  |         once() {}, | ||||||
|  |         emit() {} | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function basename(fn) { | ||||||
|  |   const m = /^(.+\/)[^\/]+\/?$/.exec(fn); | ||||||
|  |   return m && m[1]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function enumZipEntries(zipFn, callback) { | ||||||
|  |   return new Promise(function (resolve, reject) { | ||||||
|  |     (0, _yauzl.open)(zipFn, { lazyEntries: true }, function (err, zipfile) { | ||||||
|  |       if (err) { | ||||||
|  |         return reject(err); | ||||||
|  |       } | ||||||
|  |       zipfile.on('end', resolve); | ||||||
|  |       zipfile.on('error', reject); | ||||||
|  |       zipfile.on('entry', function (entry) { | ||||||
|  |         const result = callback(entry, zipfile); | ||||||
|  |         if (result && typeof result.then === 'function') { | ||||||
|  |           result.then(function () { | ||||||
|  |             return zipfile.readEntry(); | ||||||
|  |           }); | ||||||
|  |         } else { | ||||||
|  |           zipfile.readEntry(); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |       zipfile.readEntry(); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const commands = exports.commands = { | ||||||
|  |   bundle: function () { | ||||||
|  |     var _ref7 = _asyncToGenerator(function* (_ref8) { | ||||||
|  |       let options = _ref8.options; | ||||||
|  |  | ||||||
|  |       const platform = (0, _app.checkPlatform)(options.platform || (yield (0, _utils.question)('Platform(ios/android):'))); | ||||||
|  |  | ||||||
|  |       var _translateOptions = (0, _utils.translateOptions)(_extends({}, options, { | ||||||
|  |         platform | ||||||
|  |       })); | ||||||
|  |  | ||||||
|  |       let bundleName = _translateOptions.bundleName, | ||||||
|  |           entryFile = _translateOptions.entryFile, | ||||||
|  |           intermediaDir = _translateOptions.intermediaDir, | ||||||
|  |           output = _translateOptions.output, | ||||||
|  |           dev = _translateOptions.dev, | ||||||
|  |           verbose = _translateOptions.verbose; | ||||||
|  |  | ||||||
|  |       // const sourcemapOutput = path.join(intermediaDir, bundleName + ".map"); | ||||||
|  |  | ||||||
|  |       const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); | ||||||
|  |  | ||||||
|  |       if (!platform) { | ||||||
|  |         throw new Error('Platform must be specified.'); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       var _getRNVersion = (0, _utils.getRNVersion)(); | ||||||
|  |  | ||||||
|  |       const version = _getRNVersion.version, | ||||||
|  |             major = _getRNVersion.major, | ||||||
|  |             minor = _getRNVersion.minor; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |       console.log('Bundling with React Native version: ', version); | ||||||
|  |  | ||||||
|  |       yield runReactNativeBundleCommand(bundleName, dev, entryFile, intermediaDir, platform); | ||||||
|  |  | ||||||
|  |       yield pack(path.resolve(intermediaDir), realOutput); | ||||||
|  |  | ||||||
|  |       const v = yield (0, _utils.question)('Would you like to publish it?(Y/N)'); | ||||||
|  |       if (v.toLowerCase() === 'y') { | ||||||
|  |         yield this.publish({ | ||||||
|  |           args: [realOutput], | ||||||
|  |           options: { | ||||||
|  |             platform | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function bundle(_x21) { | ||||||
|  |       return _ref7.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |  | ||||||
|  |   diff(_ref9) { | ||||||
|  |     let args = _ref9.args, | ||||||
|  |         options = _ref9.options; | ||||||
|  |     return _asyncToGenerator(function* () { | ||||||
|  |       var _args = _slicedToArray(args, 2); | ||||||
|  |  | ||||||
|  |       const origin = _args[0], | ||||||
|  |             next = _args[1]; | ||||||
|  |       const output = options.output; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |       const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); | ||||||
|  |  | ||||||
|  |       if (!origin || !next) { | ||||||
|  |         console.error('pushy diff <origin> <next>'); | ||||||
|  |         process.exit(1); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       yield diffFromPPK(origin, next, realOutput, 'index.bundlejs'); | ||||||
|  |       console.log(`${realOutput} generated.`); | ||||||
|  |     })(); | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   diffFromApk(_ref10) { | ||||||
|  |     let args = _ref10.args, | ||||||
|  |         options = _ref10.options; | ||||||
|  |     return _asyncToGenerator(function* () { | ||||||
|  |       var _args2 = _slicedToArray(args, 2); | ||||||
|  |  | ||||||
|  |       const origin = _args2[0], | ||||||
|  |             next = _args2[1]; | ||||||
|  |       const output = options.output; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |       const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); | ||||||
|  |  | ||||||
|  |       if (!origin || !next) { | ||||||
|  |         console.error('pushy diffFromApk <origin> <next>'); | ||||||
|  |         process.exit(1); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       yield diffFromPackage(origin, next, realOutput, 'assets/index.android.bundle'); | ||||||
|  |       console.log(`${realOutput} generated.`); | ||||||
|  |     })(); | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   diffFromIpa(_ref11) { | ||||||
|  |     let args = _ref11.args, | ||||||
|  |         options = _ref11.options; | ||||||
|  |     return _asyncToGenerator(function* () { | ||||||
|  |       var _args3 = _slicedToArray(args, 2); | ||||||
|  |  | ||||||
|  |       const origin = _args3[0], | ||||||
|  |             next = _args3[1]; | ||||||
|  |       const output = options.output; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |       const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); | ||||||
|  |  | ||||||
|  |       if (!origin || !next) { | ||||||
|  |         console.error('pushy diffFromIpa <origin> <next>'); | ||||||
|  |         process.exit(1); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       yield diffFromPackage(origin, next, realOutput, 'main.jsbundle', function (v) { | ||||||
|  |         const m = /^Payload\/[^/]+\/(.+)$/.exec(v); | ||||||
|  |         return m && m[1]; | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       console.log(`${realOutput} generated.`); | ||||||
|  |     })(); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										65
									
								
								lib/index.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										65
									
								
								lib/index.js
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | #!/usr/bin/env node | ||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 2/13/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | var _require = require('./api'); | ||||||
|  |  | ||||||
|  | const loadSession = _require.loadSession; | ||||||
|  |  | ||||||
|  | const updateNotifier = require('update-notifier'); | ||||||
|  | const pkg = require('../package.json'); | ||||||
|  |  | ||||||
|  | updateNotifier({ pkg }).notify(); | ||||||
|  |  | ||||||
|  | function printUsage(_ref) { | ||||||
|  |   let args = _ref.args; | ||||||
|  |  | ||||||
|  |   // const commandName = args[0]; | ||||||
|  |   // TODO: print usage of commandName, or print global usage. | ||||||
|  |  | ||||||
|  |   console.log('Usage is under development now.'); | ||||||
|  |   console.log('Visit `https://github.com/reactnativecn/react-native-pushy` for early document.'); | ||||||
|  |   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 = _extends({}, require('./user').commands, require('./bundle').commands, require('./app').commands, require('./package').commands, require('./versions').commands, { | ||||||
|  |   help: printUsage | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | function run() { | ||||||
|  |   printVersionCommand(); | ||||||
|  |  | ||||||
|  |   const argv = require('cli-arguments').parse(require('../cli.json')); | ||||||
|  |   global.NO_INTERACTIVE = argv.options['no-interactive']; | ||||||
|  |  | ||||||
|  |   loadSession().then(function () { | ||||||
|  |     return commands[argv.command](argv); | ||||||
|  |   }).catch(function (err) { | ||||||
|  |     if (err.status === 401) { | ||||||
|  |       console.log('Not loggined.\nRun `pushy login` at your project directory to login.'); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     console.error(err.stack); | ||||||
|  |     process.exit(-1); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | run(); | ||||||
							
								
								
									
										170
									
								
								lib/package.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								lib/package.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,170 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | Object.defineProperty(exports, "__esModule", { | ||||||
|  |   value: true | ||||||
|  | }); | ||||||
|  | exports.commands = exports.choosePackage = exports.listPackage = undefined; | ||||||
|  |  | ||||||
|  | let listPackage = exports.listPackage = function () { | ||||||
|  |   var _ref = _asyncToGenerator(function* (appId) { | ||||||
|  |     var _ref2 = yield get(`/app/${appId}/package/list?limit=1000`); | ||||||
|  |  | ||||||
|  |     const data = _ref2.data; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     const header = [{ value: 'Package Id' }, { value: 'Version' }]; | ||||||
|  |     const rows = []; | ||||||
|  |     for (const pkg of data) { | ||||||
|  |       const version = pkg.version; | ||||||
|  |  | ||||||
|  |       let versionInfo = ''; | ||||||
|  |       if (version) { | ||||||
|  |         versionInfo = ` - ${version.id} ${version.hash.slice(0, 8)} ${version.name}`; | ||||||
|  |       } else { | ||||||
|  |         versionInfo = ' (newest)'; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       rows.push([pkg.id, `${pkg.name}(${pkg.status})${versionInfo}`]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     console.log(Table(header, rows).render()); | ||||||
|  |     console.log(`\nTotal ${data.length} package(s).`); | ||||||
|  |     return data; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function listPackage(_x) { | ||||||
|  |     return _ref.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let choosePackage = exports.choosePackage = function () { | ||||||
|  |   var _ref3 = _asyncToGenerator(function* (appId) { | ||||||
|  |     const list = yield listPackage(appId); | ||||||
|  |  | ||||||
|  |     while (true) { | ||||||
|  |       const id = yield (0, _utils.question)('Enter Package Id:'); | ||||||
|  |       const app = list.find(function (v) { | ||||||
|  |         return v.id === (id | 0); | ||||||
|  |       }); | ||||||
|  |       if (app) { | ||||||
|  |         return app; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function choosePackage(_x2) { | ||||||
|  |     return _ref3.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | var _utils = require('./utils'); | ||||||
|  |  | ||||||
|  | var _app = require('./app'); | ||||||
|  |  | ||||||
|  | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 4/2/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | var _require = require('./api'); | ||||||
|  |  | ||||||
|  | const get = _require.get, | ||||||
|  |       post = _require.post, | ||||||
|  |       uploadFile = _require.uploadFile; | ||||||
|  |  | ||||||
|  | const Table = require('tty-table'); | ||||||
|  |  | ||||||
|  | const commands = exports.commands = { | ||||||
|  |   uploadIpa: function () { | ||||||
|  |     var _ref4 = _asyncToGenerator(function* (_ref5) { | ||||||
|  |       let args = _ref5.args; | ||||||
|  |  | ||||||
|  |       const fn = args[0]; | ||||||
|  |       if (!fn) { | ||||||
|  |         throw new Error('Usage: pushy uploadIpa <ipaFile>'); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       var _ref6 = yield (0, _utils.getIpaInfo)(fn); | ||||||
|  |  | ||||||
|  |       const versionName = _ref6.versionName, | ||||||
|  |             buildTime = _ref6.buildTime; | ||||||
|  |  | ||||||
|  |       var _ref7 = yield (0, _app.getSelectedApp)('ios'); | ||||||
|  |  | ||||||
|  |       const appId = _ref7.appId; | ||||||
|  |  | ||||||
|  |       var _ref8 = yield uploadFile(fn); | ||||||
|  |  | ||||||
|  |       const hash = _ref8.hash; | ||||||
|  |  | ||||||
|  |       var _ref9 = yield post(`/app/${appId}/package/create`, { | ||||||
|  |         name: versionName, | ||||||
|  |         hash, | ||||||
|  |         buildTime | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       const id = _ref9.id; | ||||||
|  |  | ||||||
|  |       console.log(`Ipa uploaded: ${id}`); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function uploadIpa(_x3) { | ||||||
|  |       return _ref4.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |   uploadApk: function () { | ||||||
|  |     var _ref10 = _asyncToGenerator(function* (_ref11) { | ||||||
|  |       let args = _ref11.args; | ||||||
|  |  | ||||||
|  |       const fn = args[0]; | ||||||
|  |       if (!fn) { | ||||||
|  |         throw new Error('Usage: pushy uploadApk <apkFile>'); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       var _ref12 = yield (0, _utils.getApkInfo)(fn); | ||||||
|  |  | ||||||
|  |       const versionName = _ref12.versionName, | ||||||
|  |             buildTime = _ref12.buildTime; | ||||||
|  |  | ||||||
|  |       var _ref13 = yield (0, _app.getSelectedApp)('android'); | ||||||
|  |  | ||||||
|  |       const appId = _ref13.appId; | ||||||
|  |  | ||||||
|  |       var _ref14 = yield uploadFile(fn); | ||||||
|  |  | ||||||
|  |       const hash = _ref14.hash; | ||||||
|  |  | ||||||
|  |       var _ref15 = yield post(`/app/${appId}/package/create`, { | ||||||
|  |         name: versionName, | ||||||
|  |         hash, | ||||||
|  |         buildTime | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       const id = _ref15.id; | ||||||
|  |  | ||||||
|  |       console.log(`Apk uploaded: ${id}`); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function uploadApk(_x4) { | ||||||
|  |       return _ref10.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |   packages: function () { | ||||||
|  |     var _ref16 = _asyncToGenerator(function* (_ref17) { | ||||||
|  |       let options = _ref17.options; | ||||||
|  |  | ||||||
|  |       const platform = (0, _app.checkPlatform)(options.platform || (yield (0, _utils.question)('Platform(ios/android):'))); | ||||||
|  |  | ||||||
|  |       var _ref18 = yield (0, _app.getSelectedApp)(platform); | ||||||
|  |  | ||||||
|  |       const appId = _ref18.appId; | ||||||
|  |  | ||||||
|  |       yield listPackage(appId); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function packages(_x5) { | ||||||
|  |       return _ref16.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }() | ||||||
|  | }; | ||||||
							
								
								
									
										72
									
								
								lib/user.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								lib/user.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | var _utils = require('./utils'); | ||||||
|  |  | ||||||
|  | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } /** | ||||||
|  |                                                                                                                                                                                                                                                                                                                                                                                                                                                                             * Created by tdzl2003 on 2/13/16. | ||||||
|  |                                                                                                                                                                                                                                                                                                                                                                                                                                                                             */ | ||||||
|  |  | ||||||
|  | var _require = require('./api'); | ||||||
|  |  | ||||||
|  | const post = _require.post, | ||||||
|  |       get = _require.get, | ||||||
|  |       replaceSession = _require.replaceSession, | ||||||
|  |       saveSession = _require.saveSession, | ||||||
|  |       closeSession = _require.closeSession; | ||||||
|  |  | ||||||
|  | const crypto = require('crypto'); | ||||||
|  |  | ||||||
|  | function md5(str) { | ||||||
|  |   return crypto.createHash('md5').update(str).digest('hex'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | exports.commands = { | ||||||
|  |   login: function () { | ||||||
|  |     var _ref = _asyncToGenerator(function* (_ref2) { | ||||||
|  |       let args = _ref2.args; | ||||||
|  |  | ||||||
|  |       const email = args[0] || (yield (0, _utils.question)('email:')); | ||||||
|  |       const pwd = args[1] || (yield (0, _utils.question)('password:', true)); | ||||||
|  |  | ||||||
|  |       var _ref3 = yield post('/user/login', { | ||||||
|  |         email, | ||||||
|  |         pwd: md5(pwd) | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       const token = _ref3.token, | ||||||
|  |             info = _ref3.info; | ||||||
|  |  | ||||||
|  |       replaceSession({ token }); | ||||||
|  |       yield saveSession(); | ||||||
|  |       console.log(`Welcome, ${info.name}.`); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function login(_x) { | ||||||
|  |       return _ref.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |   logout: function () { | ||||||
|  |     var _ref4 = _asyncToGenerator(function* () { | ||||||
|  |       yield closeSession(); | ||||||
|  |       console.log('Logged out.'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function logout() { | ||||||
|  |       return _ref4.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |   me: function () { | ||||||
|  |     var _ref5 = _asyncToGenerator(function* () { | ||||||
|  |       const me = yield get('/user/me'); | ||||||
|  |       for (const k in me) { | ||||||
|  |         if (k !== 'ok') { | ||||||
|  |           console.log(`${k}: ${me[k]}`); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function me() { | ||||||
|  |       return _ref5.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }() | ||||||
|  | }; | ||||||
							
								
								
									
										123
									
								
								lib/utils/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								lib/utils/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | Object.defineProperty(exports, "__esModule", { | ||||||
|  |   value: true | ||||||
|  | }); | ||||||
|  | exports.getIpaInfo = exports.getApkInfo = undefined; | ||||||
|  |  | ||||||
|  | let getApkInfo = exports.getApkInfo = function () { | ||||||
|  |   var _ref = _asyncToGenerator(function* (fn) { | ||||||
|  |     const appInfoParser = new AppInfoParser(fn); | ||||||
|  |  | ||||||
|  |     var _ref2 = yield appInfoParser.parse(); | ||||||
|  |  | ||||||
|  |     const versionName = _ref2.versionName, | ||||||
|  |           application = _ref2.application; | ||||||
|  |  | ||||||
|  |     let buildTime = 0; | ||||||
|  |     if (Array.isArray(application.metaData)) { | ||||||
|  |       for (const meta of application.metaData) { | ||||||
|  |         if (meta.name === 'pushy_build_time') { | ||||||
|  |           buildTime = meta.value[0]; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (buildTime == 0) { | ||||||
|  |       throw new Error('Can not get build time for this app.'); | ||||||
|  |     } | ||||||
|  |     return { versionName, buildTime }; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function getApkInfo(_x) { | ||||||
|  |     return _ref.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let getIpaInfo = exports.getIpaInfo = function () { | ||||||
|  |   var _ref3 = _asyncToGenerator(function* (fn) { | ||||||
|  |     const appInfoParser = new AppInfoParser(fn); | ||||||
|  |  | ||||||
|  |     var _ref4 = yield appInfoParser.parse(); | ||||||
|  |  | ||||||
|  |     const versionName = _ref4.CFBundleShortVersionString; | ||||||
|  |  | ||||||
|  |     let buildTimeTxtBuffer = yield appInfoParser.parser.getEntry(/payload\/.+?\.app\/pushy_build_time.txt/); | ||||||
|  |     if (!buildTimeTxtBuffer) { | ||||||
|  |       // Not in root bundle when use `use_frameworks` | ||||||
|  |       buildTimeTxtBuffer = yield 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.'); | ||||||
|  |     } | ||||||
|  |     const buildTime = buildTimeTxtBuffer.toString().replace('\n', ''); | ||||||
|  |     return { versionName, buildTime }; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function getIpaInfo(_x2) { | ||||||
|  |     return _ref3.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | exports.question = question; | ||||||
|  | exports.translateOptions = translateOptions; | ||||||
|  | exports.getRNVersion = getRNVersion; | ||||||
|  |  | ||||||
|  | var _path = require('path'); | ||||||
|  |  | ||||||
|  | var path = _interopRequireWildcard(_path); | ||||||
|  |  | ||||||
|  | var _fsExtra = require('fs-extra'); | ||||||
|  |  | ||||||
|  | var fs = _interopRequireWildcard(_fsExtra); | ||||||
|  |  | ||||||
|  | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||||||
|  |  | ||||||
|  | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } /** | ||||||
|  |                                                                                                                                                                                                                                                                                                                                                                                                                                                                             * Created by tdzl2003 on 2/13/16. | ||||||
|  |                                                                                                                                                                                                                                                                                                                                                                                                                                                                             */ | ||||||
|  |  | ||||||
|  | const AppInfoParser = require('app-info-parser'); | ||||||
|  |  | ||||||
|  | var read = require('read'); | ||||||
|  |  | ||||||
|  | function question(query, password) { | ||||||
|  |   if (NO_INTERACTIVE) { | ||||||
|  |     return Promise.resolve(''); | ||||||
|  |   } | ||||||
|  |   return new Promise(function (resolve, reject) { | ||||||
|  |     return read({ | ||||||
|  |       prompt: query, | ||||||
|  |       silent: password, | ||||||
|  |       replace: password ? '*' : undefined | ||||||
|  |     }, function (err, result) { | ||||||
|  |       return err ? reject(err) : resolve(result); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function translateOptions(options) { | ||||||
|  |   const ret = {}; | ||||||
|  |   for (let key in options) { | ||||||
|  |     const v = options[key]; | ||||||
|  |     if (typeof v === 'string') { | ||||||
|  |       ret[key] = v.replace(/\$\{(\w+)\}/g, function (v, n) { | ||||||
|  |         return options[n] || process.env[n] || v; | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       ret[key] = v; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function getRNVersion() { | ||||||
|  |   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); | ||||||
|  |   return { | ||||||
|  |     version, | ||||||
|  |     major: match[1] | 0, | ||||||
|  |     minor: match[2] | 0 | ||||||
|  |   }; | ||||||
|  | } | ||||||
							
								
								
									
										199
									
								
								lib/versions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								lib/versions.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | Object.defineProperty(exports, "__esModule", { | ||||||
|  |   value: true | ||||||
|  | }); | ||||||
|  | exports.commands = undefined; | ||||||
|  |  | ||||||
|  | let showVersion = function () { | ||||||
|  |   var _ref = _asyncToGenerator(function* (appId, offset) { | ||||||
|  |     var _ref2 = yield get(`/app/${appId}/version/list`); | ||||||
|  |  | ||||||
|  |     const data = _ref2.data, | ||||||
|  |           count = _ref2.count; | ||||||
|  |  | ||||||
|  |     console.log(`Offset ${offset}`); | ||||||
|  |     for (const version of data) { | ||||||
|  |       let packageInfo = version.packages.slice(0, 3).map(function (v) { | ||||||
|  |         return v.name; | ||||||
|  |       }).join(', '); | ||||||
|  |       const count = version.packages.length; | ||||||
|  |       if (count > 3) { | ||||||
|  |         packageInfo += `...and ${count - 3} more`; | ||||||
|  |       } | ||||||
|  |       if (count === 0) { | ||||||
|  |         packageInfo = `(no package)`; | ||||||
|  |       } else { | ||||||
|  |         packageInfo = `[${packageInfo}]`; | ||||||
|  |       } | ||||||
|  |       console.log(`${version.id}) ${version.hash.slice(0, 8)} ${version.name} ${packageInfo}`); | ||||||
|  |     } | ||||||
|  |     return data; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function showVersion(_x, _x2) { | ||||||
|  |     return _ref.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let listVersions = function () { | ||||||
|  |   var _ref3 = _asyncToGenerator(function* (appId) { | ||||||
|  |     let offset = 0; | ||||||
|  |     while (true) { | ||||||
|  |       yield showVersion(appId, offset); | ||||||
|  |       const cmd = yield (0, _utils.question)('page Up/page Down/Begin/Quit(U/D/B/Q)'); | ||||||
|  |       switch (cmd.toLowerCase()) { | ||||||
|  |         case 'u': | ||||||
|  |           offset = Math.max(0, offset - 10);break; | ||||||
|  |         case 'd': | ||||||
|  |           offset += 10;break; | ||||||
|  |         case 'b': | ||||||
|  |           offset = 0;break; | ||||||
|  |         case 'q': | ||||||
|  |           return; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function listVersions(_x3) { | ||||||
|  |     return _ref3.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | let chooseVersion = function () { | ||||||
|  |   var _ref4 = _asyncToGenerator(function* (appId) { | ||||||
|  |     let offset = 0; | ||||||
|  |     while (true) { | ||||||
|  |       const data = yield showVersion(appId, offset); | ||||||
|  |       const cmd = yield (0, _utils.question)('Enter versionId or page Up/page Down/Begin(U/D/B)'); | ||||||
|  |       switch (cmd.toLowerCase()) { | ||||||
|  |         case 'U': | ||||||
|  |           offset = Math.max(0, offset - 10);break; | ||||||
|  |         case 'D': | ||||||
|  |           offset += 10;break; | ||||||
|  |         case 'B': | ||||||
|  |           offset = 0;break; | ||||||
|  |         default: | ||||||
|  |           { | ||||||
|  |             const v = data.find(function (v) { | ||||||
|  |               return v.id === (cmd | 0); | ||||||
|  |             }); | ||||||
|  |             if (v) { | ||||||
|  |               return v; | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return function chooseVersion(_x4) { | ||||||
|  |     return _ref4.apply(this, arguments); | ||||||
|  |   }; | ||||||
|  | }(); | ||||||
|  |  | ||||||
|  | var _utils = require('./utils'); | ||||||
|  |  | ||||||
|  | var _app = require('./app'); | ||||||
|  |  | ||||||
|  | var _package = require('./package'); | ||||||
|  |  | ||||||
|  | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 4/2/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | var _require = require('./api'); | ||||||
|  |  | ||||||
|  | const get = _require.get, | ||||||
|  |       post = _require.post, | ||||||
|  |       put = _require.put, | ||||||
|  |       uploadFile = _require.uploadFile; | ||||||
|  | const commands = exports.commands = { | ||||||
|  |   publish: function () { | ||||||
|  |     var _ref5 = _asyncToGenerator(function* (_ref6) { | ||||||
|  |       let args = _ref6.args, | ||||||
|  |           options = _ref6.options; | ||||||
|  |  | ||||||
|  |       const fn = args[0]; | ||||||
|  |       const name = options.name, | ||||||
|  |             description = options.description, | ||||||
|  |             metaInfo = options.metaInfo; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |       if (!fn || !fn.endsWith('.ppk')) { | ||||||
|  |         throw new Error('Usage: pushy publish <ppkFile> --platform ios|android'); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       const platform = (0, _app.checkPlatform)(options.platform || (yield (0, _utils.question)('Platform(ios/android):'))); | ||||||
|  |  | ||||||
|  |       var _ref7 = yield (0, _app.getSelectedApp)(platform); | ||||||
|  |  | ||||||
|  |       const appId = _ref7.appId; | ||||||
|  |  | ||||||
|  |       var _ref8 = yield uploadFile(fn); | ||||||
|  |  | ||||||
|  |       const hash = _ref8.hash; | ||||||
|  |  | ||||||
|  |       var _ref9 = yield post(`/app/${appId}/version/create`, { | ||||||
|  |         name: name || (yield (0, _utils.question)('Enter version name:')) || '(未命名)', | ||||||
|  |         hash, | ||||||
|  |         description: description || (yield (0, _utils.question)('Enter description:')), | ||||||
|  |         metaInfo: metaInfo || (yield (0, _utils.question)('Enter meta info:')) | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       const id = _ref9.id; | ||||||
|  |  | ||||||
|  |       console.log(`Version published: ${id}`); | ||||||
|  |  | ||||||
|  |       const v = yield (0, _utils.question)('Would you like to bind packages to this version?(Y/N)'); | ||||||
|  |       if (v.toLowerCase() === 'y') { | ||||||
|  |         yield this.update({ args: [], options: { versionId: id, platform } }); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function publish(_x5) { | ||||||
|  |       return _ref5.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |   versions: function () { | ||||||
|  |     var _ref10 = _asyncToGenerator(function* (_ref11) { | ||||||
|  |       let options = _ref11.options; | ||||||
|  |  | ||||||
|  |       const platform = (0, _app.checkPlatform)(options.platform || (yield (0, _utils.question)('Platform(ios/android):'))); | ||||||
|  |  | ||||||
|  |       var _ref12 = yield (0, _app.getSelectedApp)(platform); | ||||||
|  |  | ||||||
|  |       const appId = _ref12.appId; | ||||||
|  |  | ||||||
|  |       yield listVersions(appId); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function versions(_x6) { | ||||||
|  |       return _ref10.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }(), | ||||||
|  |   update: function () { | ||||||
|  |     var _ref13 = _asyncToGenerator(function* (_ref14) { | ||||||
|  |       let args = _ref14.args, | ||||||
|  |           options = _ref14.options; | ||||||
|  |  | ||||||
|  |       const platform = (0, _app.checkPlatform)(options.platform || (yield (0, _utils.question)('Platform(ios/android):'))); | ||||||
|  |  | ||||||
|  |       var _ref15 = yield (0, _app.getSelectedApp)(platform); | ||||||
|  |  | ||||||
|  |       const appId = _ref15.appId; | ||||||
|  |  | ||||||
|  |       const versionId = options.versionId || (yield chooseVersion(appId)).id; | ||||||
|  |       const pkgId = options.packageId || (yield (0, _package.choosePackage)(appId)).id; | ||||||
|  |       yield put(`/app/${appId}/package/${pkgId}`, { | ||||||
|  |         versionId | ||||||
|  |       }); | ||||||
|  |       console.log('Ok.'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return function update(_x7) { | ||||||
|  |       return _ref13.apply(this, arguments); | ||||||
|  |     }; | ||||||
|  |   }() | ||||||
|  | }; | ||||||
							
								
								
									
										57
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | { | ||||||
|  |   "name": "react-native-update-cli", | ||||||
|  |   "version": "1.0.0", | ||||||
|  |   "description": "Command tools for javaScript updater with `pushy` service for react native apps.", | ||||||
|  |   "main": "index.js", | ||||||
|  |   "bin": { | ||||||
|  |     "pushy": "lib/index.js" | ||||||
|  |   }, | ||||||
|  |   "scripts": { | ||||||
|  |     "test": "echo \"Error: no test specified\" && exit 1", | ||||||
|  |     "prepublish": "babel src --out-dir lib" | ||||||
|  |   }, | ||||||
|  |   "repository": { | ||||||
|  |     "type": "git", | ||||||
|  |     "url": "git+https://github.com/reactnativecn/react-native-pushy-cli.git" | ||||||
|  |   }, | ||||||
|  |   "keywords": [ | ||||||
|  |     "react-native", | ||||||
|  |     "ios", | ||||||
|  |     "android", | ||||||
|  |     "update" | ||||||
|  |   ], | ||||||
|  |   "author": "reactnativecn", | ||||||
|  |   "license": "BSD-3-Clause", | ||||||
|  |   "bugs": { | ||||||
|  |     "url": "https://github.com/reactnativecn/react-native-pushy/issues" | ||||||
|  |   }, | ||||||
|  |   "homepage": "https://github.com/reactnativecn/react-native-pushy/tree/master/react-native-pushy-cli", | ||||||
|  |   "dependencies": { | ||||||
|  |     "app-info-parser": "^0.3.8", | ||||||
|  |     "cli-arguments": "^0.2.1", | ||||||
|  |     "fs-extra": "^8.1.0", | ||||||
|  |     "gradle-to-js": "^2.0.0", | ||||||
|  |     "isomorphic-fetch": "^2.2.1", | ||||||
|  |     "progress": "^1.1.8", | ||||||
|  |     "read": "^1.0.7", | ||||||
|  |     "request": "^2.69.0", | ||||||
|  |     "tty-table": "^2.7.0", | ||||||
|  |     "update-notifier": "^4.1.0", | ||||||
|  |     "yauzl": "^2.10.0", | ||||||
|  |     "yazl": "2.3.0" | ||||||
|  |   }, | ||||||
|  |   "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" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										132
									
								
								src/api.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/api.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | |||||||
|  | /** | ||||||
|  |  * 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'); | ||||||
|  | import request from 'request'; | ||||||
|  | import ProgressBar from 'progress'; | ||||||
|  |  | ||||||
|  | let session = undefined; | ||||||
|  | let savedSession = undefined; | ||||||
|  |  | ||||||
|  | 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.'); | ||||||
|  |       throw e; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.getSession = function() { | ||||||
|  |   return session; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.replaceSession = function(newSession) { | ||||||
|  |   session = newSession; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.saveSession = function() { | ||||||
|  |   // Only save on change. | ||||||
|  |   if (session !== savedSession) { | ||||||
|  |     const current = session; | ||||||
|  |     const data = JSON.stringify(current, null, 4); | ||||||
|  |     fs.writeFileSync('.update', data, 'utf8'); | ||||||
|  |     savedSession = current; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.closeSession = function() { | ||||||
|  |   if (fs.existsSync('.update')) { | ||||||
|  |     fs.unlinkSync('.update'); | ||||||
|  |     savedSession = undefined; | ||||||
|  |   } | ||||||
|  |   session = undefined; | ||||||
|  |   host = process.env.PUSHY_REGISTRY || 'https://update.reactnative.cn'; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | 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 }); | ||||||
|  |   } | ||||||
|  |   return json; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function queryWithoutBody(method) { | ||||||
|  |   return function(api) { | ||||||
|  |     return query(host + api, { | ||||||
|  |       method, | ||||||
|  |       headers: { | ||||||
|  |         'X-AccessToken': session ? session.token : '', | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function queryWithBody(method) { | ||||||
|  |   return function(api, body) { | ||||||
|  |     return query(host + api, { | ||||||
|  |       method, | ||||||
|  |       headers: { | ||||||
|  |         'Content-Type': 'application/json', | ||||||
|  |         'X-AccessToken': session ? session.token : '', | ||||||
|  |       }, | ||||||
|  |       body: JSON.stringify(body), | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | exports.get = queryWithoutBody('GET'); | ||||||
|  | exports.post = queryWithBody('POST'); | ||||||
|  | exports.put = queryWithBody('PUT'); | ||||||
|  | exports.doDelete = queryWithBody('DELETE'); | ||||||
|  |  | ||||||
|  | async function uploadFile(fn) { | ||||||
|  |   const { url, fieldName, formData } = await exports.post('/upload', {}); | ||||||
|  |   let realUrl = url; | ||||||
|  |  | ||||||
|  |   if (!/^https?\:\/\//.test(url)) { | ||||||
|  |     realUrl = host + url; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const fileSize = fs.statSync(fn).size; | ||||||
|  |  | ||||||
|  |   const bar = new ProgressBar('  Uploading [:bar] :percent :etas', { | ||||||
|  |     complete: '=', | ||||||
|  |     incomplete: ' ', | ||||||
|  |     total: fileSize, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const info = await new Promise((resolve, reject) => { | ||||||
|  |     formData.file = fs.createReadStream(fn); | ||||||
|  |  | ||||||
|  |     formData.file.on('data', function(data) { | ||||||
|  |       bar.tick(data.length); | ||||||
|  |     }); | ||||||
|  |     request.post( | ||||||
|  |       realUrl, | ||||||
|  |       { | ||||||
|  |         formData, | ||||||
|  |       }, | ||||||
|  |       (err, resp, body) => { | ||||||
|  |         if (err) { | ||||||
|  |           return reject(err); | ||||||
|  |         } | ||||||
|  |         if (resp.statusCode > 299) { | ||||||
|  |           return reject(Object.assign(new Error(body), { status: resp.statusCode })); | ||||||
|  |         } | ||||||
|  |         resolve(JSON.parse(body)); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  |   return info; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | exports.uploadFile = uploadFile; | ||||||
							
								
								
									
										110
									
								
								src/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/app.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 2/13/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import {question} from './utils'; | ||||||
|  | import * as fs from 'fs-extra'; | ||||||
|  |  | ||||||
|  | const { | ||||||
|  |   post, | ||||||
|  |   get, | ||||||
|  |   doDelete, | ||||||
|  | } = require('./api'); | ||||||
|  |  | ||||||
|  | const validPlatforms = { | ||||||
|  |   ios: 1, | ||||||
|  |   android: 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export function checkPlatform(platform) { | ||||||
|  |   if (!validPlatforms[platform]) { | ||||||
|  |     throw new Error(`Invalid platform '${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!`); | ||||||
|  |   } | ||||||
|  |   const updateInfo = JSON.parse(fs.readFileSync('update.json', 'utf8')); | ||||||
|  |   if (!updateInfo[platform]) { | ||||||
|  |     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; | ||||||
|  |   for (const app of list) { | ||||||
|  |     console.log(`${app.id}) ${app.name}(${app.platform})`); | ||||||
|  |   } | ||||||
|  |   if (platform) { | ||||||
|  |     console.log(`\nTotal ${list.length} ${platform} apps`); | ||||||
|  |   } else { | ||||||
|  |     console.log(`\nTotal ${list.length} apps`); | ||||||
|  |   } | ||||||
|  |   return list; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function chooseApp(platform) { | ||||||
|  |   const list = await listApp(platform); | ||||||
|  |  | ||||||
|  |   while (true) { | ||||||
|  |     const id = await question('Enter appId:'); | ||||||
|  |     const app = list.find(v=>v.id === (id|0)); | ||||||
|  |     if (app) { | ||||||
|  |       return app; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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}); | ||||||
|  |     console.log(`Created app ${id}`); | ||||||
|  |     await this.selectApp({ | ||||||
|  |       args: [id], | ||||||
|  |       options: {platform, downloadUrl}, | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  |   deleteApp: async function ({args, options}) { | ||||||
|  |     const {platform} = options; | ||||||
|  |     const id = args[0] || chooseApp(platform); | ||||||
|  |     if (!id) { | ||||||
|  |       console.log('Canceled'); | ||||||
|  |     } | ||||||
|  |     await doDelete(`/app/${id}`); | ||||||
|  |     console.log('Ok.'); | ||||||
|  |   }, | ||||||
|  |   apps: async function ({options}){ | ||||||
|  |     const {platform} = options; | ||||||
|  |     listApp(platform); | ||||||
|  |   }, | ||||||
|  |   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 = {}; | ||||||
|  |     if (fs.existsSync('update.json')) { | ||||||
|  |       try { | ||||||
|  |         updateInfo = JSON.parse(fs.readFileSync('update.json', 'utf8')); | ||||||
|  |       } catch (e) { | ||||||
|  |         console.error('Failed to parse file `update.json`. Try to remove it manually.'); | ||||||
|  |         throw e; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     const {appKey} = await get(`/app/${id}`); | ||||||
|  |     updateInfo[platform] = { | ||||||
|  |       appId: id, | ||||||
|  |       appKey, | ||||||
|  |     }; | ||||||
|  |     fs.writeFileSync('update.json', JSON.stringify(updateInfo, null, 4), 'utf8'); | ||||||
|  |   }, | ||||||
|  | } | ||||||
							
								
								
									
										508
									
								
								src/bundle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										508
									
								
								src/bundle.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,508 @@ | |||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 2/22/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | const path = require('path'); | ||||||
|  | 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 { checkPlatform } from './app'; | ||||||
|  | const { spawn, spawnSync, execSync } = require('child_process'); | ||||||
|  | const g2js = require('gradle-to-js/lib/parser'); | ||||||
|  | const os = require('os'); | ||||||
|  |  | ||||||
|  | var diff; | ||||||
|  | try { | ||||||
|  |   var bsdiff = require('node-bsdiff'); | ||||||
|  |   diff = typeof bsdiff != 'function' ? bsdiff.diff : bsdiff; | ||||||
|  | } catch (e) { | ||||||
|  |   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.'); | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function exec(command) { | ||||||
|  |   const commandResult = spawnSync(command, { | ||||||
|  |     shell: true, | ||||||
|  |     stdio: 'inherit', | ||||||
|  |   }); | ||||||
|  |   if (commandResult.error) { | ||||||
|  |     throw commandResult.error; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function runReactNativeBundleCommand( | ||||||
|  |   bundleName, | ||||||
|  |   development, | ||||||
|  |   entryFile, | ||||||
|  |   outputFolder, | ||||||
|  |   platform, | ||||||
|  |   sourcemapOutput, | ||||||
|  |   config, | ||||||
|  | ) { | ||||||
|  |   let reactNativeBundleArgs = []; | ||||||
|  |  | ||||||
|  |   let envArgs = process.env.PUSHY_ENV_ARGS; | ||||||
|  |  | ||||||
|  |   if (envArgs) { | ||||||
|  |     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", | ||||||
|  |     '--assets-dest', | ||||||
|  |     outputFolder, | ||||||
|  |     '--bundle-output', | ||||||
|  |     path.join(outputFolder, bundleName), | ||||||
|  |     '--dev', | ||||||
|  |     development, | ||||||
|  |     '--entry-file', | ||||||
|  |     entryFile, | ||||||
|  |     '--platform', | ||||||
|  |     platform, | ||||||
|  |     '--reset-cache', | ||||||
|  |   ]); | ||||||
|  |  | ||||||
|  |   if (sourcemapOutput) { | ||||||
|  |     reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (config) { | ||||||
|  |     reactNativeBundleArgs.push('--config', config); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const reactNativeBundleProcess = spawn('node', reactNativeBundleArgs); | ||||||
|  |   console.log(`Running bundle command: node ${reactNativeBundleArgs.join(' ')}`); | ||||||
|  |  | ||||||
|  |   return new Promise((resolve, reject) => { | ||||||
|  |     reactNativeBundleProcess.stdout.on('data', data => { | ||||||
|  |       console.log(data.toString().trim()); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     reactNativeBundleProcess.stderr.on('data', data => { | ||||||
|  |       console.error(data.toString().trim()); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     reactNativeBundleProcess.on('close', async exitCode => { | ||||||
|  |       if (exitCode) { | ||||||
|  |         reject(new Error(`"react-native bundle" command exited with code ${exitCode}.`)); | ||||||
|  |       } else { | ||||||
|  |         if (platform === 'android') { | ||||||
|  |           await compileHermesByteCode(bundleName, outputFolder); | ||||||
|  |         } | ||||||
|  |         resolve(null); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function getHermesOSBin() { | ||||||
|  |   if (os.platform() === 'win32') return 'win64-bin'; | ||||||
|  |   if (os.platform() === 'darwin') return 'osx-bin'; | ||||||
|  |   if (os.platform() === 'linux') return 'linux64-bin'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function compileHermesByteCode(bundleName, outputFolder) { | ||||||
|  |   let enableHermes = false; | ||||||
|  |   try { | ||||||
|  |     const gradleConfig = await g2js.parseFile('android/app/build.gradle'); | ||||||
|  |     const projectConfig = gradleConfig['project.ext.react']; | ||||||
|  |     for (const packagerConfig of projectConfig) { | ||||||
|  |       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' }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function pack(dir, output) { | ||||||
|  |   console.log('Packing'); | ||||||
|  |   fs.ensureDirSync(path.dirname(output)); | ||||||
|  |   await new Promise((resolve, reject) => { | ||||||
|  |     var zipfile = new ZipFile(); | ||||||
|  |  | ||||||
|  |     function addDirectory(root, rel) { | ||||||
|  |       if (rel) { | ||||||
|  |         zipfile.addEmptyDirectory(rel); | ||||||
|  |       } | ||||||
|  |       const childs = fs.readdirSync(root); | ||||||
|  |       for (const name of childs) { | ||||||
|  |         if (name === '.' || name === '..') { | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  |         const fullPath = path.join(root, name); | ||||||
|  |         const stat = fs.statSync(fullPath); | ||||||
|  |         if (stat.isFile()) { | ||||||
|  |           //console.log('adding: ' + rel+name); | ||||||
|  |           zipfile.addFile(fullPath, rel + name); | ||||||
|  |         } else if (stat.isDirectory()) { | ||||||
|  |           //console.log('adding: ' + rel+name+'/'); | ||||||
|  |           addDirectory(fullPath, rel + name + '/'); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     addDirectory(dir, ''); | ||||||
|  |  | ||||||
|  |     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); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function readEntire(entry, zipFile) { | ||||||
|  |   const buffers = []; | ||||||
|  |   return new Promise((resolve, reject) => { | ||||||
|  |     zipFile.openReadStream(entry, (err, stream) => { | ||||||
|  |       stream.pipe({ | ||||||
|  |         write(chunk) { | ||||||
|  |           buffers.push(chunk); | ||||||
|  |         }, | ||||||
|  |         end() { | ||||||
|  |           resolve(Buffer.concat(buffers)); | ||||||
|  |         }, | ||||||
|  |         prependListener() {}, | ||||||
|  |         on() {}, | ||||||
|  |         once() {}, | ||||||
|  |         emit() {}, | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function basename(fn) { | ||||||
|  |   const m = /^(.+\/)[^\/]+\/?$/.exec(fn); | ||||||
|  |   return m && m[1]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function diffFromPPK(origin, next, output) { | ||||||
|  |   fs.ensureDirSync(path.dirname(output)); | ||||||
|  |  | ||||||
|  |   const originEntries = {}; | ||||||
|  |   const originMap = {}; | ||||||
|  |  | ||||||
|  |   let originSource; | ||||||
|  |  | ||||||
|  |   await enumZipEntries(origin, (entry, zipFile) => { | ||||||
|  |     originEntries[entry.fileName] = entry; | ||||||
|  |     if (!/\/$/.test(entry.fileName)) { | ||||||
|  |       // isFile | ||||||
|  |       originMap[entry.crc32] = entry.fileName; | ||||||
|  |  | ||||||
|  |       if (entry.fileName === 'index.bundlejs') { | ||||||
|  |         // This is source. | ||||||
|  |         return readEntire(entry, zipFile).then(v => (originSource = v)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   originSource = originSource || new Buffer(0); | ||||||
|  |  | ||||||
|  |   const copies = {}; | ||||||
|  |  | ||||||
|  |   var zipfile = new ZipFile(); | ||||||
|  |  | ||||||
|  |   const writePromise = new Promise((resolve, reject) => { | ||||||
|  |     zipfile.outputStream.on('error', err => { | ||||||
|  |       throw err; | ||||||
|  |     }); | ||||||
|  |     zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function() { | ||||||
|  |       resolve(); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const addedEntry = {}; | ||||||
|  |  | ||||||
|  |   function addEntry(fn) { | ||||||
|  |     //console.log(fn); | ||||||
|  |     if (!fn || addedEntry[fn]) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     const base = basename(fn); | ||||||
|  |     if (base) { | ||||||
|  |       addEntry(base); | ||||||
|  |     } | ||||||
|  |     zipfile.addEmptyDirectory(fn); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const newEntries = {}; | ||||||
|  |  | ||||||
|  |   await enumZipEntries(next, (entry, nextZipfile) => { | ||||||
|  |     newEntries[entry.fileName] = entry; | ||||||
|  |  | ||||||
|  |     if (/\/$/.test(entry.fileName)) { | ||||||
|  |       // Directory | ||||||
|  |       if (!originEntries[entry.fileName]) { | ||||||
|  |         addEntry(entry.fileName); | ||||||
|  |       } | ||||||
|  |     } else if (entry.fileName === 'index.bundlejs') { | ||||||
|  |       //console.log('Found bundle'); | ||||||
|  |       return readEntire(entry, nextZipfile).then(newSource => { | ||||||
|  |         //console.log('Begin diff'); | ||||||
|  |         zipfile.addBuffer(diff(originSource, newSource), 'index.bundlejs.patch'); | ||||||
|  |         //console.log('End diff'); | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       // If same file. | ||||||
|  |       const originEntry = originEntries[entry.fileName]; | ||||||
|  |       if (originEntry && originEntry.crc32 === entry.crc32) { | ||||||
|  |         // ignore | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // If moved from other place | ||||||
|  |       if (originMap[entry.crc32]) { | ||||||
|  |         const base = basename(entry.fileName); | ||||||
|  |         if (!originEntries[base]) { | ||||||
|  |           addEntry(base); | ||||||
|  |         } | ||||||
|  |         copies[entry.fileName] = originMap[entry.crc32]; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // New file. | ||||||
|  |       addEntry(basename(entry.fileName)); | ||||||
|  |  | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         nextZipfile.openReadStream(entry, function(err, readStream) { | ||||||
|  |           if (err) { | ||||||
|  |             return reject(err); | ||||||
|  |           } | ||||||
|  |           zipfile.addReadStream(readStream, entry.fileName); | ||||||
|  |           readStream.on('end', () => { | ||||||
|  |             //console.log('add finished'); | ||||||
|  |             resolve(); | ||||||
|  |           }); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const deletes = {}; | ||||||
|  |  | ||||||
|  |   for (var k in originEntries) { | ||||||
|  |     if (!newEntries[k]) { | ||||||
|  |       console.log('Delete ' + k); | ||||||
|  |       deletes[k] = 1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   //console.log({copies, deletes}); | ||||||
|  |   zipfile.addBuffer(new Buffer(JSON.stringify({ copies, deletes })), '__diff.json'); | ||||||
|  |   zipfile.end(); | ||||||
|  |   await writePromise; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function diffFromPackage(origin, next, output, originBundleName, transformPackagePath = v => v) { | ||||||
|  |   fs.ensureDirSync(path.dirname(output)); | ||||||
|  |  | ||||||
|  |   const originEntries = {}; | ||||||
|  |   const originMap = {}; | ||||||
|  |  | ||||||
|  |   let originSource; | ||||||
|  |  | ||||||
|  |   await enumZipEntries(origin, (entry, zipFile) => { | ||||||
|  |     if (!/\/$/.test(entry.fileName)) { | ||||||
|  |       const fn = transformPackagePath(entry.fileName); | ||||||
|  |       if (!fn) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       //console.log(fn); | ||||||
|  |       // isFile | ||||||
|  |       originEntries[fn] = entry.crc32; | ||||||
|  |       originMap[entry.crc32] = fn; | ||||||
|  |  | ||||||
|  |       if (fn === originBundleName) { | ||||||
|  |         // This is source. | ||||||
|  |         return readEntire(entry, zipFile).then(v => (originSource = v)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   originSource = originSource || new Buffer(0); | ||||||
|  |  | ||||||
|  |   const copies = {}; | ||||||
|  |  | ||||||
|  |   var zipfile = new ZipFile(); | ||||||
|  |  | ||||||
|  |   const writePromise = new Promise((resolve, reject) => { | ||||||
|  |     zipfile.outputStream.on('error', err => { | ||||||
|  |       throw err; | ||||||
|  |     }); | ||||||
|  |     zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function() { | ||||||
|  |       resolve(); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   await enumZipEntries(next, (entry, nextZipfile) => { | ||||||
|  |     if (/\/$/.test(entry.fileName)) { | ||||||
|  |       // Directory | ||||||
|  |       zipfile.addEmptyDirectory(entry.fileName); | ||||||
|  |     } else if (entry.fileName === 'index.bundlejs') { | ||||||
|  |       //console.log('Found bundle'); | ||||||
|  |       return readEntire(entry, nextZipfile).then(newSource => { | ||||||
|  |         //console.log('Begin diff'); | ||||||
|  |         zipfile.addBuffer(diff(originSource, newSource), 'index.bundlejs.patch'); | ||||||
|  |         //console.log('End diff'); | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       // If same file. | ||||||
|  |       if (originEntries[entry.fileName] === entry.crc32) { | ||||||
|  |         copies[entry.fileName] = ''; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       // If moved from other place | ||||||
|  |       if (originMap[entry.crc32]) { | ||||||
|  |         copies[entry.fileName] = originMap[entry.crc32]; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         nextZipfile.openReadStream(entry, function(err, readStream) { | ||||||
|  |           if (err) { | ||||||
|  |             return reject(err); | ||||||
|  |           } | ||||||
|  |           zipfile.addReadStream(readStream, entry.fileName); | ||||||
|  |           readStream.on('end', () => { | ||||||
|  |             //console.log('add finished'); | ||||||
|  |             resolve(); | ||||||
|  |           }); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   zipfile.addBuffer(new Buffer(JSON.stringify({ copies })), '__diff.json'); | ||||||
|  |   zipfile.end(); | ||||||
|  |   await writePromise; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function enumZipEntries(zipFn, callback) { | ||||||
|  |   return new Promise((resolve, reject) => { | ||||||
|  |     openZipFile(zipFn, { lazyEntries: true }, (err, zipfile) => { | ||||||
|  |       if (err) { | ||||||
|  |         return reject(err); | ||||||
|  |       } | ||||||
|  |       zipfile.on('end', resolve); | ||||||
|  |       zipfile.on('error', reject); | ||||||
|  |       zipfile.on('entry', entry => { | ||||||
|  |         const result = callback(entry, zipfile); | ||||||
|  |         if (result && typeof result.then === 'function') { | ||||||
|  |           result.then(() => zipfile.readEntry()); | ||||||
|  |         } else { | ||||||
|  |           zipfile.readEntry(); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |       zipfile.readEntry(); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export const commands = { | ||||||
|  |   bundle: async function({ options }) { | ||||||
|  |     const platform = checkPlatform(options.platform || (await question('Platform(ios/android):'))); | ||||||
|  |  | ||||||
|  |     let { bundleName, entryFile, intermediaDir, output, dev, verbose } = translateOptions({ | ||||||
|  |       ...options, | ||||||
|  |       platform, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     // const sourcemapOutput = path.join(intermediaDir, bundleName + ".map"); | ||||||
|  |  | ||||||
|  |     const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); | ||||||
|  |  | ||||||
|  |     if (!platform) { | ||||||
|  |       throw new Error('Platform must be specified.'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const { version, major, minor } = getRNVersion(); | ||||||
|  |  | ||||||
|  |     console.log('Bundling with React Native version: ', version); | ||||||
|  |  | ||||||
|  |     await runReactNativeBundleCommand(bundleName, dev, entryFile, intermediaDir, platform); | ||||||
|  |  | ||||||
|  |     await pack(path.resolve(intermediaDir), realOutput); | ||||||
|  |  | ||||||
|  |     const v = await question('Would you like to publish it?(Y/N)'); | ||||||
|  |     if (v.toLowerCase() === 'y') { | ||||||
|  |       await this.publish({ | ||||||
|  |         args: [realOutput], | ||||||
|  |         options: { | ||||||
|  |           platform, | ||||||
|  |         }, | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   async diff({ args, options }) { | ||||||
|  |     const [origin, next] = args; | ||||||
|  |     const { output } = options; | ||||||
|  |  | ||||||
|  |     const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); | ||||||
|  |  | ||||||
|  |     if (!origin || !next) { | ||||||
|  |       console.error('pushy diff <origin> <next>'); | ||||||
|  |       process.exit(1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     await diffFromPPK(origin, next, realOutput, 'index.bundlejs'); | ||||||
|  |     console.log(`${realOutput} generated.`); | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   async diffFromApk({ args, options }) { | ||||||
|  |     const [origin, next] = args; | ||||||
|  |     const { output } = options; | ||||||
|  |  | ||||||
|  |     const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); | ||||||
|  |  | ||||||
|  |     if (!origin || !next) { | ||||||
|  |       console.error('pushy diffFromApk <origin> <next>'); | ||||||
|  |       process.exit(1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     await diffFromPackage(origin, next, realOutput, 'assets/index.android.bundle'); | ||||||
|  |     console.log(`${realOutput} generated.`); | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   async diffFromIpa({ args, options }) { | ||||||
|  |     const [origin, next] = args; | ||||||
|  |     const { output } = options; | ||||||
|  |  | ||||||
|  |     const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); | ||||||
|  |  | ||||||
|  |     if (!origin || !next) { | ||||||
|  |       console.error('pushy diffFromIpa <origin> <next>'); | ||||||
|  |       process.exit(1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     await diffFromPackage(origin, next, realOutput, 'main.jsbundle', v => { | ||||||
|  |       const m = /^Payload\/[^/]+\/(.+)$/.exec(v); | ||||||
|  |       return m && m[1]; | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     console.log(`${realOutput} generated.`); | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										67
									
								
								src/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | |||||||
|  | #!/usr/bin/env node | ||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 2/13/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | const {loadSession} = require('./api'); | ||||||
|  | const updateNotifier = require('update-notifier'); | ||||||
|  | const pkg = require('../package.json'); | ||||||
|  |  | ||||||
|  | updateNotifier({pkg}).notify(); | ||||||
|  |  | ||||||
|  | function printUsage({args}) { | ||||||
|  |   // const commandName = args[0]; | ||||||
|  |   // TODO: print usage of commandName, or print global usage. | ||||||
|  |  | ||||||
|  |   console.log('Usage is under development now.') | ||||||
|  |   console.log('Visit `https://github.com/reactnativecn/react-native-pushy` for early document.'); | ||||||
|  |   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, | ||||||
|  |   ...require('./app').commands, | ||||||
|  |   ...require('./package').commands, | ||||||
|  |   ...require('./versions').commands, | ||||||
|  |   help: printUsage, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | function run() { | ||||||
|  |   printVersionCommand(); | ||||||
|  |    | ||||||
|  |   const argv = require('cli-arguments').parse(require('../cli.json')); | ||||||
|  |   global.NO_INTERACTIVE = argv.options['no-interactive']; | ||||||
|  |  | ||||||
|  |   loadSession() | ||||||
|  |     .then(()=>commands[argv.command](argv)) | ||||||
|  |     .catch(err=>{ | ||||||
|  |       if (err.status === 401) { | ||||||
|  |         console.log('Not loggined.\nRun `pushy login` at your project directory to login.'); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       console.error(err.stack); | ||||||
|  |       process.exit(-1); | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | run(); | ||||||
							
								
								
									
										87
									
								
								src/package.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/package.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 4/2/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | const { get, post, uploadFile } = require('./api'); | ||||||
|  | import { question } from './utils'; | ||||||
|  |  | ||||||
|  | import { checkPlatform, getSelectedApp } from './app'; | ||||||
|  |  | ||||||
|  | import { getApkInfo, getIpaInfo } from './utils'; | ||||||
|  | const Table = require('tty-table'); | ||||||
|  |  | ||||||
|  | export async function listPackage(appId) { | ||||||
|  |   const { data } = await get(`/app/${appId}/package/list?limit=1000`); | ||||||
|  |  | ||||||
|  |   const header = [{ value: 'Package Id' }, { value: 'Version' }]; | ||||||
|  |   const rows = []; | ||||||
|  |   for (const pkg of data) { | ||||||
|  |     const { version } = pkg; | ||||||
|  |     let versionInfo = ''; | ||||||
|  |     if (version) { | ||||||
|  |       versionInfo = ` - ${version.id} ${version.hash.slice(0, 8)} ${version.name}`; | ||||||
|  |     } else { | ||||||
|  |       versionInfo = ' (newest)'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     rows.push([pkg.id, `${pkg.name}(${pkg.status})${versionInfo}`]); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   console.log(Table(header, rows).render()); | ||||||
|  |   console.log(`\nTotal ${data.length} package(s).`); | ||||||
|  |   return data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function choosePackage(appId) { | ||||||
|  |   const list = await listPackage(appId); | ||||||
|  |  | ||||||
|  |   while (true) { | ||||||
|  |     const id = await question('Enter Package Id:'); | ||||||
|  |     const app = list.find(v => v.id === (id | 0)); | ||||||
|  |     if (app) { | ||||||
|  |       return app; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export const commands = { | ||||||
|  |   uploadIpa: async function({ args }) { | ||||||
|  |     const fn = args[0]; | ||||||
|  |     if (!fn) { | ||||||
|  |       throw new Error('Usage: pushy uploadIpa <ipaFile>'); | ||||||
|  |     } | ||||||
|  |     const { versionName, buildTime } = await getIpaInfo(fn); | ||||||
|  |     const { appId } = await getSelectedApp('ios'); | ||||||
|  |  | ||||||
|  |     const { hash } = await uploadFile(fn); | ||||||
|  |  | ||||||
|  |     const { id } = await post(`/app/${appId}/package/create`, { | ||||||
|  |       name: versionName, | ||||||
|  |       hash, | ||||||
|  |       buildTime, | ||||||
|  |     }); | ||||||
|  |     console.log(`Ipa uploaded: ${id}`); | ||||||
|  |   }, | ||||||
|  |   uploadApk: async function({ args }) { | ||||||
|  |     const fn = args[0]; | ||||||
|  |     if (!fn) { | ||||||
|  |       throw new Error('Usage: pushy uploadApk <apkFile>'); | ||||||
|  |     } | ||||||
|  |     const { versionName, buildTime } = await getApkInfo(fn); | ||||||
|  |     const { appId } = await getSelectedApp('android'); | ||||||
|  |  | ||||||
|  |     const { hash } = await uploadFile(fn); | ||||||
|  |  | ||||||
|  |     const { id } = await post(`/app/${appId}/package/create`, { | ||||||
|  |       name: versionName, | ||||||
|  |       hash, | ||||||
|  |       buildTime, | ||||||
|  |     }); | ||||||
|  |     console.log(`Apk uploaded: ${id}`); | ||||||
|  |   }, | ||||||
|  |   packages: async function({ options }) { | ||||||
|  |     const platform = checkPlatform(options.platform || (await question('Platform(ios/android):'))); | ||||||
|  |     const { appId } = await getSelectedApp(platform); | ||||||
|  |     await listPackage(appId); | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										43
									
								
								src/user.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/user.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 2/13/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import {question} from './utils'; | ||||||
|  | const { | ||||||
|  |   post, | ||||||
|  |   get, | ||||||
|  |   replaceSession, | ||||||
|  |   saveSession, | ||||||
|  |   closeSession, | ||||||
|  | } = require('./api'); | ||||||
|  | const crypto = require('crypto'); | ||||||
|  |  | ||||||
|  | function md5(str) { | ||||||
|  |   return crypto.createHash('md5').update(str).digest('hex'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | exports.commands = { | ||||||
|  |   login: async function ({args}){ | ||||||
|  |     const email = args[0] || await question('email:'); | ||||||
|  |     const pwd = args[1] || await question('password:', true); | ||||||
|  |     const {token, info} = await post('/user/login', { | ||||||
|  |       email, | ||||||
|  |       pwd: md5(pwd), | ||||||
|  |     }); | ||||||
|  |     replaceSession({token}); | ||||||
|  |     await saveSession(); | ||||||
|  |     console.log(`Welcome, ${info.name}.`); | ||||||
|  |   }, | ||||||
|  |   logout: async function (){ | ||||||
|  |     await closeSession(); | ||||||
|  |     console.log('Logged out.'); | ||||||
|  |   }, | ||||||
|  |   me: async function (){ | ||||||
|  |     const me = await get('/user/me'); | ||||||
|  |     for (const k in me) { | ||||||
|  |       if (k !== 'ok') { | ||||||
|  |         console.log(`${k}: ${me[k]}`); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | } | ||||||
							
								
								
									
										84
									
								
								src/utils/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/utils/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 2/13/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import * as path from 'path'; | ||||||
|  | import * as fs from 'fs-extra'; | ||||||
|  | const AppInfoParser = require('app-info-parser'); | ||||||
|  |  | ||||||
|  | var read = require('read'); | ||||||
|  |  | ||||||
|  | export function question(query, password) { | ||||||
|  |   if (NO_INTERACTIVE) { | ||||||
|  |     return Promise.resolve(''); | ||||||
|  |   } | ||||||
|  |   return new Promise((resolve, reject) => | ||||||
|  |     read( | ||||||
|  |       { | ||||||
|  |         prompt: query, | ||||||
|  |         silent: password, | ||||||
|  |         replace: password ? '*' : undefined, | ||||||
|  |       }, | ||||||
|  |       (err, result) => (err ? reject(err) : resolve(result)), | ||||||
|  |     ), | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function translateOptions(options) { | ||||||
|  |   const ret = {}; | ||||||
|  |   for (let key in options) { | ||||||
|  |     const v = options[key]; | ||||||
|  |     if (typeof v === 'string') { | ||||||
|  |       ret[key] = v.replace(/\$\{(\w+)\}/g, function(v, n) { | ||||||
|  |         return options[n] || process.env[n] || v; | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       ret[key] = v; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getRNVersion() { | ||||||
|  |   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); | ||||||
|  |   return { | ||||||
|  |     version, | ||||||
|  |     major: match[1] | 0, | ||||||
|  |     minor: match[2] | 0, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function getApkInfo(fn) { | ||||||
|  |   const appInfoParser = new AppInfoParser(fn); | ||||||
|  |   const { versionName, application } = await appInfoParser.parse(); | ||||||
|  |   let buildTime = 0; | ||||||
|  |   if (Array.isArray(application.metaData)) { | ||||||
|  |     for (const meta of application.metaData) { | ||||||
|  |       if (meta.name === 'pushy_build_time') { | ||||||
|  |         buildTime = meta.value[0]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (buildTime == 0) { | ||||||
|  |     throw new Error('Can not get build time for this app.'); | ||||||
|  |   } | ||||||
|  |   return { versionName, buildTime }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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/); | ||||||
|  |   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/); | ||||||
|  |   } | ||||||
|  |   if (!buildTimeTxtBuffer) { | ||||||
|  |     throw new Error('Can not get build time for this app.'); | ||||||
|  |   } | ||||||
|  |   const buildTime = buildTimeTxtBuffer.toString().replace('\n', ''); | ||||||
|  |   return { versionName, buildTime }; | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								src/versions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/versions.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | /** | ||||||
|  |  * Created by tdzl2003 on 4/2/16. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | const { | ||||||
|  |   get, | ||||||
|  |   post, | ||||||
|  |   put, | ||||||
|  |   uploadFile, | ||||||
|  | } = require('./api'); | ||||||
|  | import { question } from './utils'; | ||||||
|  |  | ||||||
|  | import { checkPlatform, getSelectedApp } from './app'; | ||||||
|  | import { choosePackage } from './package'; | ||||||
|  |  | ||||||
|  | async function showVersion(appId, offset) { | ||||||
|  |   const { data, count } = await get(`/app/${appId}/version/list`); | ||||||
|  |   console.log(`Offset ${offset}`); | ||||||
|  |   for (const version of data) { | ||||||
|  |     let packageInfo = version.packages.slice(0, 3).map(v=>v.name).join(', '); | ||||||
|  |     const count = version.packages.length; | ||||||
|  |     if (count > 3) { | ||||||
|  |       packageInfo += `...and ${count-3} more`; | ||||||
|  |     } | ||||||
|  |     if (count === 0) { | ||||||
|  |       packageInfo = `(no package)`; | ||||||
|  |     } else { | ||||||
|  |       packageInfo = `[${packageInfo}]`; | ||||||
|  |     } | ||||||
|  |     console.log(`${version.id}) ${version.hash.slice(0, 8)} ${version.name} ${packageInfo}`); | ||||||
|  |   } | ||||||
|  |   return data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function listVersions(appId) { | ||||||
|  |   let offset = 0; | ||||||
|  |   while (true) { | ||||||
|  |     await showVersion(appId, offset); | ||||||
|  |     const cmd = await question('page Up/page Down/Begin/Quit(U/D/B/Q)'); | ||||||
|  |     switch (cmd.toLowerCase()) { | ||||||
|  |       case 'u': offset = Math.max(0, offset - 10); break; | ||||||
|  |       case 'd': offset += 10; break; | ||||||
|  |       case 'b': offset = 0; break; | ||||||
|  |       case 'q': return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function chooseVersion(appId) { | ||||||
|  |   let offset = 0; | ||||||
|  |   while (true) { | ||||||
|  |     const data = await showVersion(appId, offset); | ||||||
|  |     const cmd = await question('Enter versionId or page Up/page Down/Begin(U/D/B)'); | ||||||
|  |     switch (cmd.toLowerCase()) { | ||||||
|  |       case 'U': offset = Math.max(0, offset - 10); break; | ||||||
|  |       case 'D': offset += 10; break; | ||||||
|  |       case 'B': offset = 0; break; | ||||||
|  |       default: | ||||||
|  |       { | ||||||
|  |         const v = data.find(v=>v.id === (cmd | 0)); | ||||||
|  |         if (v) { | ||||||
|  |           return v; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export const commands = { | ||||||
|  |   publish: async function({args, options}) { | ||||||
|  |     const fn = args[0]; | ||||||
|  |     const {name, description, metaInfo } = options; | ||||||
|  |  | ||||||
|  |     if (!fn || !fn.endsWith('.ppk')) { | ||||||
|  |       throw new Error('Usage: pushy publish <ppkFile> --platform ios|android'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const platform = checkPlatform(options.platform || await question('Platform(ios/android):')); | ||||||
|  |     const { appId } = await getSelectedApp(platform); | ||||||
|  |  | ||||||
|  |     const { hash } = await uploadFile(fn); | ||||||
|  |  | ||||||
|  |     const { id } = await post(`/app/${appId}/version/create`, { | ||||||
|  |       name: name || await question('Enter version name:') || '(未命名)', | ||||||
|  |       hash, | ||||||
|  |       description: description || await question('Enter description:'), | ||||||
|  |       metaInfo: metaInfo || await question('Enter meta info:'), | ||||||
|  |     }); | ||||||
|  |     console.log(`Version published: ${id}`); | ||||||
|  |  | ||||||
|  |     const v = await question('Would you like to bind packages to this version?(Y/N)'); | ||||||
|  |     if (v.toLowerCase() === 'y') { | ||||||
|  |       await this.update({args:[], options:{versionId: id, platform}}); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   versions: async function({options}) { | ||||||
|  |     const platform = checkPlatform(options.platform || await question('Platform(ios/android):')); | ||||||
|  |     const { appId } = await getSelectedApp(platform); | ||||||
|  |     await listVersions(appId); | ||||||
|  |   }, | ||||||
|  |   update: async function({args, options}) { | ||||||
|  |     const platform = checkPlatform(options.platform || await question('Platform(ios/android):')); | ||||||
|  |     const { appId } = await getSelectedApp(platform); | ||||||
|  |     const versionId = options.versionId || (await chooseVersion(appId)).id; | ||||||
|  |     const pkgId = options.packageId || (await choosePackage(appId)).id; | ||||||
|  |     await put(`/app/${appId}/package/${pkgId}`, { | ||||||
|  |       versionId, | ||||||
|  |     }); | ||||||
|  |     console.log('Ok.'); | ||||||
|  |   } | ||||||
|  | }; | ||||||
		Reference in New Issue
	
	Block a user
	 sunnylqm
					sunnylqm