UNPKG

@myywlc/lin-cli

Version:

lin cli

274 lines (250 loc) 7.64 kB
import path from 'path'; import fs from 'fs'; import ora from 'ora'; import inquirer from 'inquirer'; import archiver from 'archiver'; import node_ssh from 'node-ssh'; import childProcess from 'child_process'; import { infoLog, errorLog, successLog, underlineLog, checkDeployConfig } from '../utils/utils_deploy'; const deployConfigPath = `${process.cwd()}/deploy.config.js`; const ssh = new node_ssh(); // 生成ssh实例 export const name = 'deploy'; export const cmd = { description: 'deploy package', usages: ['lin deploy init', 'lin deploy'], }; export const handle = async (key) => { switch (key) { case 'init': checkDeployExists(); break; default: handleDeploy(); break; } }; // ===================== init ========================= // 检查部署目录及部署配置文件是否存在 const checkDeployExists = () => { if (fs.existsSync(deployConfigPath)) { infoLog('根目录下的deploy.config.js配置文件已经存在!'); process.exit(1); return; } writeConfigFile(); }; const configTemplate = `const config = { privateKey: '', // 本地私钥地址,位置一般在C:/Users/xxx/.ssh/id_rsa,非必填,有私钥则配置 passphrase: '', // 本地私钥密码,非必填,有私钥则配置 projectName: '', // 项目名称 // 根据需要进行配置,如只需部署prod线上环境,请删除dev测试环境配置,反之亦然,支持多环境部署 dev: { // 测试环境 name: '测试环境', script: 'npm run build', // 测试环境打包脚本 host: '', // 测试服务器地址 port: 22, // ssh port,一般默认22 username: '', // 登录服务器用户名 password: '', // 登录服务器密码 distPath: 'dist', // 本地打包dist目录 // distPath: { // files: ['package.json', 'yarn.lock', '.gitignore', '.prettierrc.js', 'babel.config.js', 'README.md'], // directory: ['dist', 'static'] // }, webDir: '', // 测试环境服务器地址 }, prod: { // 线上环境 name: '线上环境', script: 'npm run build', // 线上环境打包脚本 host: '', // 线上服务器地址 port: 22, // ssh port,一般默认22 username: '', // 登录服务器用户名 password: '', // 登录服务器密码 distPath: 'dist', // 本地打包dist目录 // distPath: { // files: ['package.json', 'yarn.lock', '.gitignore', '.prettierrc.js', 'babel.config.js', 'README.md'], // directory: ['dist', 'static'] // }, webDir: '', // 线上环境web目录 }, }; module.exports = { config }; `; const writeConfigFile = () => { const spinner = ora('开始生成部署模板'); spinner.start(); fs.writeFile(deployConfigPath, configTemplate, { encoding: 'utf8' }, (err) => { if (err) { errorLog(err); process.exit(1); } spinner.stop(); successLog('配置模板创建成功'); infoLog('请配置根目录下的deploy.config.js配置文件'); errorLog('注意:请在.gitignore配置忽略deploy.config.js文件,避免关键信息泄露'); process.exit(0); }); }; // ===================== deploy ========================= // 部署流程入口 async function runDeploy(config) { const { script, webDir, distPath, projectName, name } = config; try { execBuild(script); await startZip(distPath); await connectSSH(config); await uploadFile(webDir); await unzipFile(webDir); await deleteLocalZip(); successLog(` ${underlineLog(projectName)}项目${underlineLog(name)}部署成功 \n`); process.exit(0); } catch (err) { errorLog(` 部署失败 ${err}`); process.exit(1); } } // 第一步,执行打包脚本 function execBuild(script) { try { console.log(`\n(1)${script}`); const spinner = ora('正在打包中'); spinner.start(); childProcess.execSync(script, { cwd: process.cwd() }); spinner.stop(); successLog(' 打包成功'); } catch (err) { errorLog(err); process.exit(1); } } // 第二部,打包zip function startZip(distPath) { return new Promise((resolve, reject) => { const output = fs.createWriteStream(`${process.cwd()}/dist.zip`); console.log('(2)打包成zip'); const archive = archiver('zip', { zlib: { level: 9 }, }).on('error', err => { throw err; }); output.on('close', err => { if (err) { errorLog(` 关闭archiver异常 ${err}`); reject(err); process.exit(1); } successLog(' zip打包成功'); resolve(); }); archive.pipe(output); if (distPath instanceof Object) { const { files, directory } = distPath; files.forEach((item) => { archive.file(`${process.cwd()}/${item}`, { name: item }); }); directory.forEach((item) => { archive.directory(`${process.cwd()}/${item}`, `/${item}`); }); } else { distPath = path.resolve(process.cwd(), distPath); archive.directory(distPath, '/'); } archive.finalize(); }); } // 第三步,连接SSH async function connectSSH(config) { const { host, port, username, password, privateKey, passphrase } = config; const sshConfig = { host, port, username, password, privateKey, passphrase, }; try { console.log(`(3)连接${underlineLog(host)}`); await ssh.connect(sshConfig); successLog(' SSH连接成功'); } catch (err) { errorLog(` 连接失败 ${err}`); process.exit(1); } } // 第四部,上传zip包 async function uploadFile(webDir) { try { console.log(`(4)上传zip至目录${underlineLog(webDir)}`); await ssh.putFile(`${process.cwd()}/dist.zip`, `${webDir}/dist.zip`); successLog(' zip包上传成功'); } catch (err) { errorLog(` zip包上传失败 ${err}`); process.exit(1); } } // 运行命令 async function runCommand(command, webDir) { await ssh.execCommand(command, { cwd: webDir }); } // 第五步,解压zip包 async function unzipFile(webDir) { try { console.log('(5)开始解压zip包'); await runCommand(`cd ${webDir}`, webDir); await runCommand('unzip -o dist.zip && rm -f dist.zip', webDir); successLog(' zip包解压成功'); } catch (err) { errorLog(` zip包解压失败 ${err}`); process.exit(1); } } // 第六步,删除本地dist.zip包 async function deleteLocalZip() { return new Promise((resolve, reject) => { console.log('(6)开始删除本地zip包'); fs.unlink(`${process.cwd()}/dist.zip`, err => { if (err) { errorLog(` 本地zip包删除失败 ${err}`, err); reject(err); process.exit(1); } successLog(' 本地zip包删除成功\n'); resolve(); }); }); } async function handleDeploy() { // 检测部署配置是否合理 const deployConfigs = await checkDeployConfig(deployConfigPath); if (!deployConfigs) { process.exit(1); } const choices = deployConfigs.map(config => { const { name } = config; return name }); inquirer .prompt([ { type: 'list', message: `将${underlineLog(deployConfigs[0].projectName)}项目是否部署到什么环境 ?`, name: 'env', choices, filter: function (val) { // 使用filter将回答变为小写 return val.toLowerCase(); } } ]) .then(answers => { const { env } = answers; if (!env) { process.exit(1); } const targetObj = deployConfigs.find(item => item.name === env); if (targetObj) { runDeploy(targetObj) } }); }