diff --git a/.prettierrc.json b/.prettierrc.json index 1f9ad4c..a4a3eb3 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,5 +2,6 @@ "tabWidth": 2, "singleQuote": true, "semi": false, - "jsxSingleQuote": true + "jsxSingleQuote": true, + "trailingComma": "none" } diff --git a/lib/commands/deploy.js b/lib/commands/deploy.js index ba44e20..a1d49e7 100644 --- a/lib/commands/deploy.js +++ b/lib/commands/deploy.js @@ -9,20 +9,23 @@ const { log, succeed, error, - underline, + underline } = require('../utils') const ssh = new NodeSSH() const maxBuffer = 5000 * 1024 +// 任务列表 +let taskList + // 是否确认部署 const confirmDeploy = (message) => { return inquirer.prompt([ { type: 'confirm', name: 'confirm', - message, - }, + message + } ]) } @@ -34,30 +37,46 @@ const checkEnvCorrect = (config, env) => { 'host', 'port', 'username', - 'password', 'distPath', - 'webDir', + 'webDir' ] - if (config) { + if ( + config && + (function () { + const { privateKey, password } = config + if (!privateKey && !password) { + error( + `配置错误: 请配置 ${underline('privateKey')} 或 ${underline( + 'passwrod' + )}` + ) + process.exit(1) + } + return true + })() + ) { keys.forEach((key) => { if (!config[key] || config[key] === '/') { error( - `${underline(`${env}环境`)} ${underline(`${key}属性`)} 配置不正确` + `配置错误: ${underline(`${env}环境`)} ${underline( + `${key}属性` + )} 配置不正确` ) process.exit(1) } }) } else { - error(`未指定部署环境或指定部署环境不存在`) + error('配置错误: 未指定部署环境或指定部署环境不存在') process.exit(1) } } // 执行打包脚本 -const execBuild = async (script) => { +const execBuild = async (config, index) => { try { - log(`(1) ${script}`) + const { script } = config + log(`(${index}) ${script}`) const spinner = ora('正在打包中\n') spinner.start() @@ -85,9 +104,9 @@ const execBuild = async (script) => { } // 连接ssh -const connectSSH = async (config) => { +const connectSSH = async (config, index) => { try { - log(`(2) ssh连接 ${underline(config.host)}`) + log(`(${index}) ssh连接 ${underline(config.host)}`) await ssh.connect(config) succeed('ssh连接成功') } catch (e) { @@ -96,31 +115,21 @@ const connectSSH = async (config) => { } } -// 删除远程文件 -const removeRemoteFile = async (webDir) => { - try { - log(`(3) 删除远程文件 ${underline(webDir)}`) - await ssh.execCommand(`rm -rf ${webDir}`) - succeed('删除成功') - } catch (e) { - error(e) - process.exit(1) - } -} - // 上传本地文件 -const uploadLocalFile = async (config) => { +const uploadLocalFile = async (config, index) => { try { - log(`(4) 上传打包文件至目录 ${underline(config.webDir)}`) - + const backPath = `${config.webDir}.back` const localPath = `${process.cwd()}/${config.distPath}` + + log(`(${index}) 上传打包文件至目录 ${underline(config.webDir)}`) + const spinner = ora('正在上传中\n') spinner.start() - await ssh.putDirectory(localPath, config.webDir, { + await ssh.putDirectory(localPath, backPath, { recursive: true, - concurrency: 10, + concurrency: 10 }) spinner.stop() @@ -131,16 +140,38 @@ const uploadLocalFile = async (config) => { } } -// 断开ssh -const disconnectSSH = () => { - ssh.dispose() +// 删除远程文件 +const removeRemoteFile = async (config, index) => { + try { + const { webDir } = config + + log(`(${index}) 删除远程文件 ${underline(webDir)}`) + + await ssh.execCommand(`rm -rf ${webDir}`) + succeed('删除成功') + } catch (e) { + error(e) + process.exit(1) + } +} + +// 移动远程文件 +const moveRemoteFile = async (config) => { + try { + const { webDir } = config + + await ssh.execCommand(`mv ${webDir}.back ${webDir}`) + } catch (e) { + error(e) + process.exit(1) + } } // 删除本地打包文件 -const removeLocalFile = (distPath) => { - const localPath = `${process.cwd()}/${distPath}` +const removeLocalFile = (config, index) => { + const localPath = `${process.cwd()}/${config.distPath}` - log(`(5) 删除本地打包目录 ${underline(localPath)}`) + log(`(${index}) 删除本地打包目录 ${underline(localPath)}`) const remove = (path) => { if (fs.existsSync(path)) { @@ -160,13 +191,45 @@ const removeLocalFile = (distPath) => { succeed('删除本地打包目录成功') } +// 断开ssh +const disconnectSSH = () => { + ssh.dispose() +} + +// 创建任务列表 +const createTaskList = (config) => { + const { isRemoveRemoteFile = true } = config + + taskList = [] + taskList.push(execBuild) + taskList.push(connectSSH) + taskList.push(uploadLocalFile) + isRemoveRemoteFile && taskList.push(removeRemoteFile) + taskList.push(removeLocalFile) + taskList.push(moveRemoteFile) + taskList.push(disconnectSSH) +} + +// 执行任务列表 +const executeTaskList = async (config) => { + for (const [index, execute] of new Map( + taskList.map((execute, index) => [index, execute]) + )) { + await execute(config, index + 1) + } +} + module.exports = { description: '部署项目', apply: async (env) => { if (checkDeployConfigExists()) { const config = require(deployConfigPath) const projectName = config.projectName - const envConfig = config[env] + const envConfig = Object.assign(config[env], { + privateKey: config.privateKey, + passphrase: config.passphrase + }) + checkEnvCorrect(envConfig, env) const answers = await confirmDeploy( @@ -174,12 +237,10 @@ module.exports = { ) if (answers.confirm) { - await execBuild(envConfig.script) - await connectSSH(envConfig) - await removeRemoteFile(envConfig.webDir) - await uploadLocalFile(envConfig) - disconnectSSH() - removeLocalFile(envConfig.distPath) + createTaskList(envConfig) + + await executeTaskList(envConfig) + succeed( `恭喜您,${underline(projectName)}项目已在${underline( envConfig.name @@ -195,5 +256,5 @@ module.exports = { ) process.exit(1) } - }, + } } diff --git a/lib/commands/init.js b/lib/commands/init.js index b9189a8..5a44f28 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -5,7 +5,7 @@ const { checkDeployConfigExists, succeed, error, - underline, + underline } = require('../utils') const { inquirerConfig, deployConfigPath } = require('../config') @@ -18,6 +18,8 @@ const getUserInputInfo = () => { const createJsonObj = (userInputInfo) => { const jsonObj = { projectName: userInputInfo.projectName, + privateKey: userInputInfo.privateKey, + passphrase: userInputInfo.passphrase } const { deployEnvList } = userInputInfo @@ -31,6 +33,7 @@ const createJsonObj = (userInputInfo) => { password: userInputInfo[`${env}Password`], distPath: userInputInfo[`${env}DistPath`], webDir: userInputInfo[`${env}WebDir`], + isRemoveRemoteFile: userInputInf[`${env}IsRemoveRemoteFile`] } } @@ -68,5 +71,5 @@ module.exports = { process.exit(0) }) } - }, + } } diff --git a/lib/config/index.js b/lib/config/index.js index 31c887d..6438187 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -1,4 +1,5 @@ const fs = require('fs') +const os = require('os') const path = require('path') const devConfig = [ @@ -7,54 +8,61 @@ const devConfig = [ name: 'devName', message: '环境名称', default: '开发环境', - when: (answers) => answers.deployEnvList.includes('dev'), + when: (answers) => answers.deployEnvList.includes('dev') }, { type: 'input', name: 'devScript', message: '打包命令', default: 'npm run build:dev', - when: (answers) => answers.deployEnvList.includes('dev'), + when: (answers) => answers.deployEnvList.includes('dev') }, { type: 'input', name: 'devHost', message: '服务器地址', - when: (answers) => answers.deployEnvList.includes('dev'), + when: (answers) => answers.deployEnvList.includes('dev') }, { type: 'number', name: 'devPort', message: '服务器端口号', default: 22, - when: (answers) => answers.deployEnvList.includes('dev'), + when: (answers) => answers.deployEnvList.includes('dev') }, { type: 'input', name: 'devUsername', message: '用户名', default: 'root', - when: (answers) => answers.deployEnvList.includes('dev'), + when: (answers) => answers.deployEnvList.includes('dev') }, { type: 'password', name: 'devPassword', message: '密码', - when: (answers) => answers.deployEnvList.includes('dev'), + when: (answers) => answers.deployEnvList.includes('dev') }, { type: 'input', name: 'devDistPath', message: '本地打包目录', default: 'dist', - when: (answers) => answers.deployEnvList.includes('dev'), + when: (answers) => answers.deployEnvList.includes('dev') }, { type: 'input', name: 'devWebDir', message: '部署路径', - when: (answers) => answers.deployEnvList.includes('dev'), + when: (answers) => answers.deployEnvList.includes('dev') }, + { + type: 'confirm', + name: 'devIsRemoveRemoteFile', + message: '是否删除远程文件', + default: true, + when: (answers) => answers.deployEnvList.includes('dev') + } ] const testConfig = [ @@ -63,54 +71,61 @@ const testConfig = [ name: 'testName', message: '环境名称', default: '测试环境', - when: (answers) => answers.deployEnvList.includes('test'), + when: (answers) => answers.deployEnvList.includes('test') }, { type: 'input', name: 'testScript', message: '打包命令', default: 'npm run build:test', - when: (answers) => answers.deployEnvList.includes('test'), + when: (answers) => answers.deployEnvList.includes('test') }, { type: 'input', name: 'testHost', message: '服务器地址', - when: (answers) => answers.deployEnvList.includes('test'), + when: (answers) => answers.deployEnvList.includes('test') }, { type: 'number', name: 'testPort', message: '服务器端口号', default: 22, - when: (answers) => answers.deployEnvList.includes('test'), + when: (answers) => answers.deployEnvList.includes('test') }, { type: 'input', name: 'testUsername', message: '用户名', default: 'root', - when: (answers) => answers.deployEnvList.includes('test'), + when: (answers) => answers.deployEnvList.includes('test') }, { type: 'password', name: 'testPassword', message: '密码', - when: (answers) => answers.deployEnvList.includes('test'), + when: (answers) => answers.deployEnvList.includes('test') }, { type: 'input', name: 'testDistPath', message: '本地打包目录', default: 'dist', - when: (answers) => answers.deployEnvList.includes('test'), + when: (answers) => answers.deployEnvList.includes('test') }, { type: 'input', name: 'testWebDir', message: '部署路径', - when: (answers) => answers.deployEnvList.includes('test'), + when: (answers) => answers.deployEnvList.includes('test') }, + { + type: 'confirm', + name: 'testIsRemoveRemoteFile', + message: '是否删除远程文件', + default: true, + when: (answers) => answers.deployEnvList.includes('test') + } ] const prodConfig = [ @@ -119,54 +134,61 @@ const prodConfig = [ name: 'prodName', message: '环境名称', default: '生产环境', - when: (answers) => answers.deployEnvList.includes('prod'), + when: (answers) => answers.deployEnvList.includes('prod') }, { type: 'input', name: 'prodScript', message: '打包命令', default: 'npm run build:prod', - when: (answers) => answers.deployEnvList.includes('prod'), + when: (answers) => answers.deployEnvList.includes('prod') }, { type: 'input', name: 'prodHost', message: '服务器地址', - when: (answers) => answers.deployEnvList.includes('prod'), + when: (answers) => answers.deployEnvList.includes('prod') }, { type: 'number', name: 'prodPort', message: '服务器端口号', default: 22, - when: (answers) => answers.deployEnvList.includes('prod'), + when: (answers) => answers.deployEnvList.includes('prod') }, { type: 'input', name: 'prodUsername', message: '用户名', default: 'root', - when: (answers) => answers.deployEnvList.includes('prod'), + when: (answers) => answers.deployEnvList.includes('prod') }, { type: 'password', name: 'prodPassword', message: '密码', - when: (answers) => answers.deployEnvList.includes('prod'), + when: (answers) => answers.deployEnvList.includes('prod') }, { type: 'input', name: 'prodDistPath', message: '本地打包目录', default: 'dist', - when: (answers) => answers.deployEnvList.includes('prod'), + when: (answers) => answers.deployEnvList.includes('prod') }, { type: 'input', name: 'prodWebDir', message: '部署路径', - when: (answers) => answers.deployEnvList.includes('prod'), + when: (answers) => answers.deployEnvList.includes('prod') }, + { + type: 'confirm', + name: 'prodIsRemoveRemoteFile', + message: '是否删除远程文件', + default: true, + when: (answers) => answers.deployEnvList.includes('prod') + } ] module.exports = { @@ -179,7 +201,19 @@ module.exports = { message: '请输入项目名称', default: fs.existsSync(`${path.join(process.cwd())}/package.json`) ? require(`${process.cwd()}/package.json`).name - : '', + : '' + }, + { + type: 'input', + name: 'privateKey', + message: '请输入本地私钥地址', + default: `${os.homedir()}/.ssh/id_rsa` + }, + { + type: 'password', + name: 'passphrase', + message: '请输入本地私钥密码', + default: '' }, { type: 'checkbox', @@ -188,18 +222,18 @@ module.exports = { choices: [ { name: 'dev', - checked: true, + checked: true }, { - name: 'test', + name: 'test' }, { - name: 'prod', - }, - ], + name: 'prod' + } + ] }, ...devConfig, ...testConfig, - ...prodConfig, - ], + ...prodConfig + ] } diff --git a/package.json b/package.json index 5619101..806bdca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "deploy-cli-service", - "version": "1.0.9", + "version": "1.1.0", "description": "前端一键自动化部署脚手架服务", "main": "lib/service.js", "homepage": "https://github.com/fuchengwei/deploy-cli-service",