pushy uploadIpa
This commit is contained in:
parent
ae628b310a
commit
d0037f1217
@ -2,25 +2,42 @@
|
||||
"useCommand": true,
|
||||
"defaultCommand": "help",
|
||||
"commands": {
|
||||
"login":{
|
||||
"options": {
|
||||
"username": {
|
||||
"help": {
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
"login":{
|
||||
},
|
||||
"logout": {
|
||||
|
||||
},
|
||||
"me": {
|
||||
},
|
||||
|
||||
"createApp": {
|
||||
"options": {
|
||||
"name": {
|
||||
"hasValue": true
|
||||
},
|
||||
"platform": {
|
||||
"hasValue": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
},
|
||||
"deleteApp": {
|
||||
},
|
||||
"selectApp": {
|
||||
"options": {
|
||||
"platform": {
|
||||
"hasValue": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"uploadIpa": {
|
||||
|
||||
},
|
||||
"help": {
|
||||
|
||||
},
|
||||
"use": {
|
||||
"description": "Select app created on web and create token for future release."
|
||||
},
|
||||
"build": {
|
||||
"description": "Bundle javascript and copy assets."
|
||||
},
|
||||
|
@ -5,6 +5,9 @@
|
||||
const fetch = require('isomorphic-fetch');
|
||||
let host = process.env.PUSHY_REGISTRY || 'http://pushy.reactnative.cn';
|
||||
const fs = require('fs-promise');
|
||||
import * as fsOrigin from 'fs';
|
||||
import request from 'request';
|
||||
import ProgressBar from 'progress';
|
||||
|
||||
let session = undefined;
|
||||
let savedSession = undefined;
|
||||
@ -45,7 +48,7 @@ exports.closeSession = async function(){
|
||||
savedSession = undefined;
|
||||
}
|
||||
session = undefined;
|
||||
host = process.env.PUSHY_REGISTRY || 'http://pushy.reactnative.cn';
|
||||
host = process.env.PUSHY_REGISTRY || 'http://update.reactnative.cn';
|
||||
}
|
||||
|
||||
async function query(url, options) {
|
||||
@ -84,3 +87,44 @@ function queryWithBody(method) {
|
||||
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)) {
|
||||
url = host + url;
|
||||
}
|
||||
|
||||
const fileSize = (await fs.stat(fn)).size;
|
||||
|
||||
const bar = new ProgressBar(' Uploading [:bar] :percent :etas', {
|
||||
complete: '=',
|
||||
incomplete: ' ',
|
||||
total: fileSize,
|
||||
});
|
||||
|
||||
const info = await new Promise((resolve, reject) => {
|
||||
formData.file = fsOrigin.createReadStream(fn);
|
||||
|
||||
formData.file.on('data', function(data) {
|
||||
bar.tick(data.length);
|
||||
})
|
||||
request.post({
|
||||
url,
|
||||
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;
|
91
local-cli/src/app.js
Normal file
91
local-cli/src/app.js
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Created by tdzl2003 on 2/13/16.
|
||||
*/
|
||||
|
||||
import {question} from './utils';
|
||||
import * as fs from 'fs-promise';
|
||||
|
||||
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 async function getSelectedApp(platform) {
|
||||
checkPlatform(platform);
|
||||
|
||||
if (!await fs.exists('update.json')){
|
||||
throw new Error(`App not selected. run 'pushy selectApp --platform ${platform}' first!`);
|
||||
}
|
||||
const updateInfo = JSON.parse(await fs.readFile('update.json', 'utf8'));
|
||||
if (!updateInfo[platform]) {
|
||||
throw new Error(`App not selected. run 'pushy selectApp --platform ${platform}' first!`);
|
||||
}
|
||||
return updateInfo[platform];
|
||||
}
|
||||
|
||||
export const commands = {
|
||||
createApp: async function ({options}) {
|
||||
const name = options.name || await question('App Name:');
|
||||
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},
|
||||
});
|
||||
},
|
||||
deleteApp: async function ({args}) {
|
||||
const id = args[0] || ((await this.apps()), (await question('Choose App to delete:')));
|
||||
if (!id) {
|
||||
console.log('Canceled');
|
||||
}
|
||||
await doDelete(`/app/${id}`);
|
||||
console.log('Ok.');
|
||||
},
|
||||
apps: async function (_, 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`);
|
||||
}
|
||||
},
|
||||
selectApp: async function({args, options}) {
|
||||
const {platform} = options;
|
||||
checkPlatform(platform);
|
||||
const id = args[0] || ((await this.apps(null, platform)), (await question('Choose App used for ${platform}:')));
|
||||
|
||||
let updateInfo = {};
|
||||
if (await fs.exists('update.json')) {
|
||||
try {
|
||||
updateInfo = JSON.parse(await fs.readFile('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,
|
||||
};
|
||||
await fs.writeFile('update.json', JSON.stringify(updateInfo, null, 4), 'utf8');
|
||||
},
|
||||
}
|
@ -3,8 +3,6 @@
|
||||
*/
|
||||
|
||||
const {loadSession} = require('./api');
|
||||
const userCommands = require('./user').commands;
|
||||
import {commands as bundleCommands} from './bundle';
|
||||
|
||||
function printUsage({args}) {
|
||||
// const commandName = args[0];
|
||||
@ -16,8 +14,10 @@ function printUsage({args}) {
|
||||
}
|
||||
|
||||
const commands = {
|
||||
...userCommands,
|
||||
...bundleCommands,
|
||||
...require('./user').commands,
|
||||
...require('./bundle').commands,
|
||||
...require('./app').commands,
|
||||
...require('./package').commands,
|
||||
help: printUsage,
|
||||
};
|
||||
|
||||
@ -27,6 +27,11 @@ exports.run = function () {
|
||||
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.log(err.message);
|
||||
setTimeout(()=>{
|
||||
throw err;
|
||||
});
|
||||
|
31
local-cli/src/package.js
Normal file
31
local-cli/src/package.js
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Created by tdzl2003 on 4/2/16.
|
||||
*/
|
||||
|
||||
const {
|
||||
get,
|
||||
post,
|
||||
uploadFile,
|
||||
} = require('./api');
|
||||
|
||||
import { checkPlatform, getSelectedApp } from './app';
|
||||
|
||||
import {getIPAVersion, getApkVersion} from './utils';
|
||||
|
||||
export const commands = {
|
||||
uploadIpa: async function({args}) {
|
||||
const fn = args[0];
|
||||
if (!fn) {
|
||||
throw new Error('Usage: pushy uploadIpa <ipaFile>');
|
||||
}
|
||||
const name = await getIPAVersion(fn);
|
||||
const {appId} = await getSelectedApp('ios');
|
||||
|
||||
const {hash} = await uploadFile(fn);
|
||||
|
||||
await post(`/app/${appId}/package/create`, {
|
||||
name,
|
||||
hash,
|
||||
});
|
||||
}
|
||||
};
|
@ -13,35 +13,31 @@ const {
|
||||
const crypto = require('crypto');
|
||||
|
||||
function md5(str) {
|
||||
return crypto.createHash('md5').update(str).digest('base64');
|
||||
return crypto.createHash('md5').update(str).digest('hex');
|
||||
}
|
||||
|
||||
exports.commands = {
|
||||
login: async function ({args}){
|
||||
const login = args[0] || await question('user:');
|
||||
const email = args[0] || await question('email:');
|
||||
const pwd = args[1] || await question('password:', true);
|
||||
const {token} = await post('/user/login', {
|
||||
login,
|
||||
const {token, info} = await post('/user/login', {
|
||||
email,
|
||||
pwd: md5(pwd),
|
||||
});
|
||||
replaceSession({token});
|
||||
await saveSession();
|
||||
console.log('OK.');
|
||||
console.log(`Welcome, ${info.name}.`);
|
||||
},
|
||||
logout: async function (){
|
||||
await closeSession();
|
||||
console.log('Logged out.');
|
||||
},
|
||||
me: async function (){
|
||||
try {
|
||||
const me = await get('/user/me');
|
||||
console.log(me);
|
||||
} catch (e) {
|
||||
if (e.status === 401) {
|
||||
console.log('Not loggined.\nRun `pushy login` at your project directory to login.');
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
for (const k in me) {
|
||||
if (k !== 'ok') {
|
||||
console.log(`${k}: ${me[k]}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ export function getApkVersion(fn) {
|
||||
export function getIPAVersion(fn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ipaMetadata(fn, (err, data) => {
|
||||
err ? reject(err) : resolve(data);
|
||||
err ? reject(err) : resolve(data.metadata.CFBundleShortVersionString);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -34,7 +34,9 @@
|
||||
"mkdir-recursive": "^0.2.1",
|
||||
"node-apk-parser": "^0.2.3",
|
||||
"node-bsdiff": "^0.1.2",
|
||||
"progress": "^1.1.8",
|
||||
"read": "^1.0.7",
|
||||
"request": "^2.69.0",
|
||||
"yauzl": "^2.4.1",
|
||||
"yazl": "^2.3.0"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user