1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee

pushy uploadIpa

This commit is contained in:
tdzl2003 2016-04-02 23:39:09 +08:00
parent ae628b310a
commit d0037f1217
8 changed files with 218 additions and 32 deletions

View File

@ -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."
},

View File

@ -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
View 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');
},
}

View File

@ -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
View 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,
});
}
};

View File

@ -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]}`);
}
}
},
}

View File

@ -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);
});
});
}

View File

@ -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"
},