fdm
Version:
基于gulp的1688前端集成化工具
621 lines (512 loc) • 14.8 kB
JavaScript
/**
* dongming.jidm
* 部署到盖娅环境需要的相关接口
*/
;
var request = require('request');
var helper = require('../utils/helper');
var shell = require('shelljs');
var Path = require('path');
var spawn = require('win-spawn');
var async = require('async');
var prettyTime = require('pretty-hrtime');
var client = require('scp2');
var gaeaWorkdir = Path.join(helper.getHomeDir(), 'neat.fdm', 'gaea');
var Connection = require('ssh2');
/**
* 申请盖娅测试机器资源
* @param {[string]} user [邮箱前缀-dongming.jidm]
* @param {[string]} crid [aone上项目的crid]
* @param {[function]} cb [回调]
*/
exports.applyGaeaEnv = function(user, crid, cb) {
var api = 'http://mytest.alibaba-inc.com/newenv/rpc/complex/projectApplyForNeat.json?projectCrid=' + crid + '&applyUser=' + user;
// var api = 'http://10.125.55.10:8080/newenv/rpc/complex/projectApplyForNeat.json?projectCrid=' + crid + '&applyUser=' + user;
var re = {
success: true,
data: {
message: '',
content: {}
}
};
request(api, function(error, response, body) {
if(!error && response.statusCode === 200) {
var data = JSON.parse(body);
var result = data.content.result;
if( result ) {
re.success = true;
re.data.content = applySuccess(data.content);
} else {
re.success = false;
re.data.message = applyFail(data.content);
}
} else {
re.success = false;
re.data.message = 'request Gaea failed';
}
cb(re);
});
};
/**
* 获取项目分支
* @param {string} crid [aone上项目的crid]
* @param {Function} cb [回调函数]
*/
exports.getProjectBranches = function(crid, cb) {
var api = 'http://aone.alibaba-inc.com/perth/externalapi/externalapi.jsp?action=projectDetailByCRID4CIC&CRID=' + crid;
var re = {
success: true,
data: {
message: '',
content: {}
}
};
request(api, function(error, response, body) {
if(!error && response.statusCode === 200) {
var backdata = getBranchesSuccess(body);
if(typeof backdata === 'string') {
re.success = false;
re.data.message = backdata;
} else {
re.success = true;
re.data.content = backdata;
}
} else {
re.success = false;
re.data.message = 'request Gaea failed';
}
cb(re);
});
}
/**
* 生成编译目录结构
* @param {object} data [分支对象]
* @param {string} crid [aone项目crid]
* @param {Function} cb [回调函数]
* @return {[type]} [description]
*/
exports.makeDirectoryStruct = function(data, crid, cb) {
var workdir = Path.join(gaeaWorkdir, crid);
var destdir = Path.join(gaeaWorkdir, crid + '-dest');
var pcStyleName = [];
var pcCommonStyleName = [];
var mobileStyleName = [];
var pcBranchReg = /style_(\w+)|style-(\w+)/i;
var mobileBranchReg = /style-m-(\w+)/i;
var re = {
"pcStyle": [],
"pcCommonStyle": [],
"mobileStyle": []
};
data.pc.forEach(function(ele, index) {
var name = pcBranchReg.exec(ele) || [];
var obj = {
"name": "",
"branch": "",
"output": ""
};
if( name[1] && name[1] !== 'fdevlib' && name[1] !== 'sys' ) {
obj.name = name[1];
obj.branch = ele;
pcStyleName.push(obj);
}
if( (name[1] && name[1] === 'fdevlib') || (name[1] && name[1] === 'sys') ) {
obj.name = name[1];
obj.branch = ele;
pcCommonStyleName.push(obj);
}
if( name[2] ) {
obj.name = name[2];
obj.branch = ele;
pcStyleName.push(obj);
}
});
data.mobile.forEach(function(ele, index) {
var name = mobileBranchReg.exec(ele) || [];
var obj = {
"name": "",
"branch": "",
"output": ""
};
if( name[1] ) {
obj.name = name[1];
obj.branch = ele;
mobileStyleName.push(obj);
}
});
shell.rm('-rf', workdir);
shell.rm('-rf', destdir);
pcStyleName.forEach(function(ele, index) {
var directory = Path.join(workdir, 'app', ele.name);
var destdirectory = Path.join(destdir, 'app', ele.name);
ele.output = directory;
ele.destdir = destdirectory;
shell.mkdir('-p', directory);
shell.mkdir('-p', destdirectory);
});
pcCommonStyleName.forEach(function(ele, index) {
var directory = Path.join(workdir, ele.name);
var destdirectory = Path.join(destdir, ele.name);
ele.output = directory;
ele.destdir = destdirectory;
shell.mkdir('-p', directory);
shell.mkdir('-p', destdirectory);
});
mobileStyleName.forEach(function(ele, index) {
var directory = Path.join(workdir, 'm', ele.name);
var destdirectory = Path.join(destdir, 'm', ele.name);
ele.output = directory;
ele.destdir = destdirectory;
shell.mkdir('-p', directory);
shell.mkdir('-p', destdirectory);
});
re.pcStyle = pcStyleName;
re.pcCommonStyle = pcCommonStyleName;
re.mobileStyle = mobileStyleName;
cb(null, re);
}
/**
* checkout下svn分支填充组织好的目录结构
* @param {object} data [分支信息]
* @param {string} svnUser [svn用户名]
* @param {Function} cb [回调函数]
* @return {[type]} [description]
*/
exports.fillDirectory = function( data, svnUser, cb ) {
var functionArr = [];
// pcStyle
data.pcStyle.forEach(function(ele, index) {
functionArr.push(async.apply(svnco, ele, svnUser));
});
// pcCommonStyle
data.pcCommonStyle.forEach(function(ele, index) {
functionArr.push(async.apply(svnco, ele, svnUser));
});
// mobileStyle
data.mobileStyle.forEach(function(ele, index) {
functionArr.push(async.apply(svnco, ele, svnUser));
});
async.series(functionArr, function(err, result) {
if( err ) {
cb(err, 'fail');
} else {
cb(null, 'ok');
}
});
}
/**
* 构建项目
* @param {object} data [分支信息]
* @param {Function} cb [回调函数]
* @return {[type]} [description]
*/
exports.buildProject = function(data, cb) {
var functionArr = [];
var start = process.hrtime();
// have common style? fdevlib/sys
var commondir = '';
var commonstyleArr = data.pcCommonStyle;
if(commonstyleArr.length) {
var styledir = commonstyleArr[0]['output'];
commondir = styledir.slice(0, styledir.indexOf(commonstyleArr[0]['name']));
}
// pcCommonStyle
data.pcCommonStyle.forEach(function(ele, index) {
functionArr.push(async.apply(buildPcProject, ele, commondir));
});
// pcStyle
data.pcStyle.forEach(function(ele, index) {
functionArr.push(async.apply(buildPcProject, ele, commondir));
});
async.series(functionArr, function(err, result) {
if( err ) {
cb(err, 'fail');
} else {
var end = process.hrtime(start);
console.log('编译整个项目共耗时:', prettyTime(end));
cb(null, 'ok');
}
});
}
/**
* 部署项目到盖娅
* @param {[string]} destdir [要推送到盖娅上的目录]
* @param {[string]} ip [盖娅机器IP]
* @param {[string]} user [盖娅机器用户名]
* @param {[string]} pwd [盖娅机器密码]
* @param {[string]} crid [aone项目crid]
* @param {Function} cb [回调]
* @return {[type]} [description]
*/
exports.pushtoGaea = function(destdir, ip, user, pwd, crid, cb) {
var loginstr = user + ':' + pwd + '@' + ip + ':/home/' + user + '/output/';
var zipName = Path.join(destdir, crid + '.zip');
var zipDir = destdir;
async.series([
async.apply(startGaeaDeploy, ip, user, pwd),
async.apply(zipProject, zipDir, zipName),
async.apply(uploadZip, loginstr, zipName),
async.apply(sshRemoteUnzip, ip, user, pwd, crid)
], function(err, result) {
if( err ) {
cb(err, 'fail');
} else {
console.log('pushtoGaea done!');
cb(null, 'ok');
}
});
}
/**
* 执行盖娅的部署过程(这个过程需要较长时间,希望以后可以解决)
* @param {[string]} ip [盖娅机器IP]
* @param {[string]} user [盖娅机器用户名]
* @param {[string]} pwd [盖娅机器密码]
* @param {Function} cb [回调]
* @return {[type]} [description]
*/
function startGaeaDeploy(ip, user, pwd, cb) {
var c = new Connection();
c.on('ready', function() {
console.log('Connection :: ready');
console.log('这个过程需要一些时间,请耐心等待,你可以去泡杯咖啡或上趟厕所');
c.exec('.testbin/style.sh style -t', function(err, stream) {
if( err ) {
cb(err, 'fail');
}
stream.on('data', function(data, extended) {
//~
console.log(data.toString());
});
stream.on('end', function() {
// console.log('Stream :: EOF');
});
stream.on('close', function() {
// console.log('Stream :: close');
});
stream.on('exit', function(code, signal) {
// console.log('Stream :: exit :: code: ' + code + ', signal: ' + signal);
c.end();
});
});
});
c.on('error', function(err) {
// console.log('Connection :: error :: ' + err);
cb(err, 'fail');
});
c.on('end', function() {
// console.log('Connection :: end');
cb(null, 'ok');
});
c.on('close', function(had_error) {
// console.log('Connection :: close');
});
c.connect({
host: ip,
port: 22,
username: user,
password: pwd
});
}
/**
* 压缩项目 采用jar包方式
* @param {[string]} zipDir [要压缩的文件地址]
* @param {[string]} zipName [压缩后的文件命名]
* @param {Function} cb [回调]
* @return {[type]} [description]
*/
function zipProject( zipDir, zipName, cb ) {
var zipjarPath = Path.join(__dirname, '../lib/zip.jar');
var errmsg = '';
// 打包编译好的项目
var zipcli = spawn('java', ['-jar', zipjarPath, zipDir, zipName], {
stdio: [process.stdin, process.stdout, 'pipe']
}).on('close', function(code) {
if( code ) {
cb('zip project failed!', 'fail');
}
if(errmsg) {
cb(errmsg, 'fail');
}
console.log('zip project fininshed');
cb(null, 'ok');
});
zipcli.stderr.on('data', function(data) {
errmsg += data;
});
}
/**
* 上传zip包到盖娅测试机
* @param {[string]} loginStr [登陆到远程机器的信息]
* @param {[string]} zipPath [要上传的zip包路径]
* @param {Function} cb [回调]
* @return {[type]} [description]
*/
function uploadZip( loginStr, zipPath, cb ) {
client.scp(zipPath, loginStr, function(err) {
if( err ) {
cb(err, 'fail');
}
cb(null, 'ok');
});
}
/**
* 远程连接到盖娅机器上执行解包操作
* @param {[string]} ip [盖娅机器IP]
* @param {[string]} user [盖娅机器用户名]
* @param {[string]} pwd [盖娅机器密码]
* @param {[string]} crid [aone项目crid]
* @param {Function} cb [回调]
* @return {[type]} [description]
*/
function sshRemoteUnzip(ip, user, pwd, crid, cb) {
var c = new Connection();
c.on('ready', function() {
console.log('Connection :: ready');
// -o 为解压时覆盖原有目录,不用询问
// -d 为解压指定到某输出目录
c.exec('unzip -o /home/admin/output/' + crid + '.zip -d /home/admin/output/', function(err, stream) {
if( err ) {
cb(err, 'fail');
}
stream.on('data', function(data, extended) {
//~
});
stream.on('end', function() {
// console.log('Stream :: EOF');
});
stream.on('close', function() {
// console.log('Stream :: close');
});
stream.on('exit', function(code, signal) {
// console.log('Stream :: exit :: code: ' + code + ', signal: ' + signal);
c.end();
});
});
});
c.on('error', function(err) {
// console.log('Connection :: error :: ' + err);
cb(err, 'fail');
});
c.on('end', function() {
// console.log('Connection :: end');
cb(null, 'ok');
});
c.on('close', function(had_error) {
// console.log('Connection :: close');
});
c.connect({
host: ip,
port: 22,
username: user,
password: pwd
});
}
/**
* fdm build操作
* @param {object} ele [svn分支信息]
* @param {string} commondir [公共分支所在目录,当项目中有公共分支例如fdevlib/sys时,此值不为空]
* @param {Function} cb [回调]
* @return {[type]} [description]
*/
function buildPcProject(ele, commondir, cb) {
var cliPath = Path.join(__dirname, '../bin/fdm');
var errmsg = '';
var buildcli = spawn(cliPath, ['build', '-s', ele.output, '-d', ele.destdir, '-an', ele.name, '-lcd', commondir], {
stdio: [process.stdin, process.stdout, 'pipe']
}).on('close', function(code) {
if( code ) {
cb('build project failed!', 'fail');
}
if( errmsg ) {
cb(errmsg, 'fail');
}
console.log(ele.name, 'build fininshed');
cb(null, 'ok');
});
buildcli.stderr.on('data', function(data) {
errmsg += data;
});
}
/**
* svn co操作
* @param {object} ele [svn分支信息对象]
* @param {string} svnUser [svn用户名]
* @param {Function} cb [回调函数]
* @return {[type]} [description]
*/
function svnco(ele, svnUser, cb) {
var errmsg = '';
var cocli = spawn('svn', ['co', ele.branch, ele.output, '--username', svnUser], {
stdio: [process.stdin, process.stdout, 'pipe']
}).on('close', function(code) {
if( code ) {
cb('svn co failed!', 'fail');
}
if( errmsg ) {
cb(errmsg, 'fail');
}
console.log(ele.name, 'checkout fininshed');
cb(null, 'ok');
});
cocli.stderr.on('data', function(data) {
errmsg += data;
});
}
/**
* 申请盖娅环境成功时的回调
* @param {object} data [请求盖娅接口返回的对象]
* @return {[type]} [description]
*/
function applySuccess( data ) {
var message = data.msg;
// var message = 'ip:10.125.55.116 user:admin pwd:admin';
var messageArr = message.split(' ');
var ip = messageArr[0].split(':')[1];
var user = messageArr[1].split(':')[1];
var pwd = messageArr[2].split(':')[1];
var content = {
hostip: ip,
loginname: user,
password: pwd
};
return content;
}
function applyFail( data ) {
return data.msg;
}
/**
* 获取项目分支成功时的回调
* @param {string} data [请求aone接口返回的数据]
* @return {[type]} [description]
*/
function getBranchesSuccess( data ) {
var branchReg = /currentBranch:"(.*?)"/i;
var pcBranchReg = /style_\w+|style-\w+/i;
var mobileBranchReg = /style-m-\w+/i;
var pcStyle = [];
var mobileStyle = [];
// 利用正则取出currentBranch字段,以为返回的结果不符合JSON规范,没法转成json对象
var branchStr = (branchReg.exec(data.trim()) || [])[1];
if( branchStr ) {
var branchArr = branchStr.split(',');
branchArr.forEach(function(ele, index) {
if(pcBranchReg.test(ele) && !mobileBranchReg.test(ele)) {
pcStyle.push(ele);
}
if(mobileBranchReg.test(ele)) {
mobileStyle.push(ele);
}
});
} else {
var message = 'branch not found in your project';
return getBranchesFail(message);
}
var content = {
pc: pcStyle,
mobile: mobileStyle
};
return content;
}
function getBranchesFail( data ) {
return data;
}