const fs = require('fs-extra'); const { execSync } = require('child_process'); const os = require('os'); const path = require('path'); const chalk = require('chalk'); // 获取当前脚本所在的目录 const scriptDir = __dirname; const rootDir = path.resolve(scriptDir, '..'); const argv2 = process.argv[2]; const arg2 = argv2?.split(' '); const customerList = arg2?.[0] ? (arg2[0] === 'pro' ? ['ch:pro'] : ['ch']) : ['ch']; const publicDir = path.join(rootDir, 'public'); const distDir = path.join(rootDir, 'dist'); const customerListDir = path.join(rootDir, 'customer_list'); const customerProjectListDir = path.join(rootDir, 'src', 'views', 'project'); const homeDir = os.homedir(); // const deployEnv = process.argv[3]; const logColor = (text, color) => { console.log(chalk[color](text)); }; const logError = (text) => { logColor(text, 'red'); }; const logSuccess = (text) => { logColor(text, 'green'); }; /** * 检查文件是否存在 * @param {*} file * @param {*} fileText */ const checkFileExist = (file, fileText) => { // 验证源文件夹是否存在 if (!fs.existsSync(file)) { console.error(chalk.red(`${fileText} "${file}" 不存在!`)); process.exit(1); // 退出脚本 } }; const checkCustomerDirExist = (customer) => { const customerDir = path.join(customerListDir, customer); checkFileExist(customerDir, '客户文件夹'); }; /** * 检查命令,是否加上用户名。以及用户文件夹是否存在 * @param {*} command * @param {*} customer */ const checkCustomer = (command, customer) => { if (!customer) { console.error(chalk.red(`请正确使用命令 “${command} [customer]” `)); process.exit(1); // 退出脚本 } checkCustomerDirExist(customer); }; let tmpBakDir = ''; const moveProjectsToBakDir = (command) => { const customer = customerList[0]?.split(':')[0]; checkCustomer(command, customer); //#region ====================== 创建备份文件夹 ====================== let tmpBakRootDir = path.join(homeDir, 'Desktop'); if (!fs.existsSync(tmpBakRootDir)) { tmpBakRootDir = homeDir; } tmpBakDir = path.join(tmpBakRootDir, 'bak_project_list'); fs.ensureDirSync(tmpBakDir); fs.emptyDirSync(tmpBakDir); //#endregion const contents = fs.readdirSync(customerProjectListDir); for (let index = 0; index < contents.length; index++) { const item = contents[index]; if (item === customer) { continue; } const itemPath = path.join(customerProjectListDir, item); const stats = fs.statSync(itemPath); if (stats.isDirectory()) { const destBakDir = path.join(tmpBakDir, item); fs.moveSync(itemPath, destBakDir, { overwrite: true }); } } }; const restoreProjectDir = () => { const contents = fs.readdirSync(tmpBakDir); for (let index = 0; index < contents.length; index++) { const item = contents[index]; const itemPath = path.join(tmpBakDir, item); const destProjectDir = path.join(customerProjectListDir, item); fs.moveSync(itemPath, destProjectDir, { overwrite: true }); } }; /** * 复制文件到 public 文件夹中 * (只能复制一个客户文件配置) * @param {*} command * @param {*} customer */ function copyFile(command) { const customer = customerList[0]?.split(':')[0]; checkCustomer(command, customer); // 确保 public 文件,不存在则创建 fs.ensureDirSync(publicDir); // 清空 public 文件夹 fs.emptyDirSync(publicDir); // 使用 filter 过滤器,排除特定文件 const filter = (src, dest) => { const fileName = path.basename(src); // 排除特定文件 if (fileName === 'deploy.json') { return false; } // 其他文件保留 return true; }; const customerDir = path.join(customerListDir, customer); // 复制源文件夹中的所有文件到 public 文件夹 fs.copySync(customerDir, publicDir, { filter }); } /** * 上传文件到服务器 * @param {*} command */ const uploadFiles = async () => { let customerConfig = {}; for (let index = 0; index < customerList.length; index++) { const item = customerList[index]; const customerSplit = item.split(':'); const customerName = customerSplit[0]; checkCustomerDirExist(customerName); const deployJSON = path.join(customerListDir, customerName, 'deploy.json'); // 验证部署配置文件是否存在 checkFileExist(deployJSON, '配置文件'); // 读取 JSON 文件 const config = await fs.readJson(deployJSON).catch((err) => { console.error(`读取配置文件“${deployJSON}”出错:`, err); process.exit(1); }); let deployConfig = null; const deployEnv = customerSplit[1]; if (deployEnv === 'pro') { deployConfig = config?.product; } else { deployConfig = config?.test; } if (!deployConfig || Object.values(deployConfig).some((item) => !item)) { console.error(chalk.red(`${customerName} ${deployEnv} 配置不完整!`)); process.exit(1); // 退出脚本 } // 缓存部署配置 customerConfig[item] = deployConfig; } for (const customerDeployName in customerConfig) { if (Object.hasOwnProperty.call(customerConfig, customerDeployName)) { const customerName = customerDeployName.split(':')[0]; const deployConfig = customerConfig[customerDeployName]; const remoteFolderPath = deployConfig.path; // 获取用户临时目录 const userTempDir = os.tmpdir(); const timeStamp = new Date().getTime(); const uploadScriptFile = path.join(userTempDir, `psftpBatchFile${timeStamp}${customerDeployName}`); // 构建 sftp 命令 const sftpCommand = 'psftp'; // 上传 dist 中的 assets 和 index.html let uploadCommand = `put -r ${path.join(distDir, 'assets')} ${remoteFolderPath + '/assets'} put ${path.join(distDir, 'index.html')} ${remoteFolderPath + '/index.html'}\n`; const customerFolder = path.join(customerListDir, customerName); // 客户文件夹中除 deploy.json 全部上传 try { const contents = fs.readdirSync(customerFolder); for (let index = 0; index < contents.length; index++) { const item = contents[index]; if (item === 'deploy.json') { continue; } const itemPath = path.join(customerFolder, item); const remotePath = remoteFolderPath + `/${item}`; const stats = fs.statSync(itemPath); // 文件夹需要加 -r if (stats.isDirectory()) { uploadCommand += `put -r ${itemPath} ${remotePath}\n`; // 文件不需要加 } else if (stats.isFile()) { uploadCommand += `put ${itemPath} ${remotePath}\n`; } } } catch (error) { console.error(`读取“${customerFolder}”失败`, error); } try { fs.writeFileSync(uploadScriptFile, uploadCommand); const sftpArgs = [ `${deployConfig.username}@${deployConfig.host} -P ${deployConfig.port} -pw ${deployConfig.password} -b ${uploadScriptFile}`, ]; // 完整的执行命令 const fullCommand = `${sftpCommand} ${sftpArgs.join(' ')}`; logSuccess(`正在上传【${customerName}】项目至服务器【${deployConfig.host}:${remoteFolderPath}】...`); execSync(fullCommand, { stdio: ['pipe', 'inherit', 'inherit'] }); } catch (error) { console.error(chalk.red(error)); } finally { if (fs.existsSync(uploadScriptFile)) { fs.unlinkSync(uploadScriptFile); } } } } logSuccess(`🎉🎉🎉【${customerList.join(',')}】项目已成功部署!🎉🎉🎉`); }; module.exports = { customerList, //#region ====================== 文件路径 ====================== rootDir, scriptDir, publicDir, distDir, customerListDir, //#endregion //#region ====================== 工具函数 ====================== checkCustomerDirExist, //#endregion checkCustomer, copyFile, uploadFiles, //#region ====================== 打印 ====================== logError, logSuccess, //#endregion moveProjectsToBakDir, restoreProjectDir, };