UNPKG

fdm

Version:

基于gulp的1688前端集成化工具

621 lines (512 loc) 14.8 kB
/** * dongming.jidm * 部署到盖娅环境需要的相关接口 */ 'use strict'; 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; }