123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- import { exec } from 'child_process'
- import { Client as SSHClient } from 'ssh2'
- import ora from 'ora'
- import SftpClient from 'ssh2-sftp-client'
- import chalk from 'chalk'
- import fs from 'fs'
- import { loadEnv } from 'vite'
- // 创建分隔线
- const createSeparator = (text) => {
- const width = 60;
- const padding = Math.floor((width - text.length - 2) / 2);
- const separator = '='.repeat(padding) + ' ' + text + ' ' + '='.repeat(padding);
- return chalk.blue('\n' + separator + '\n');
- };
- const spinner = ora('正在构建项目...').start()
- spinner.stopAndPersist({
- text:
- ' __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ \n' +
- ' | |\n' +
- ' | *** 运行发布流程 *** |\n' +
- ' | 【pigx管理系统】 |\n' +
- ' | __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ |\n'
- })
- spinner.start()
- let speed = 20
- let time = 20
- const count = 400
- const getProgress = () => {
- const done = '█'
- const undone = '░'
- let progress = Math.floor((speed / count) * 50)
- if (progress > 50) {
- progress = 50
- }
- const progressBar = done.repeat(progress) + undone.repeat(50 - progress)
- return progressBar
- }
- const interval = setInterval(() => {
- spinner.text = `正在构建项目 ${getProgress()} 进度${((speed / count) * 100).toFixed(1)}% 耗时${(time / 10).toFixed(1)}秒 `
- speed++
- time++
- if (speed > count) {
- speed = 400
- }
- }, 100)
- const env = loadEnv('', process.cwd())
- const command = (list, config, host) => {
- return new Promise((resolveCommand, rejectCommand) => {
- const ssh = new SSHClient();
- const runCommand = (index) => {
- if (index >= list.length) {
- ssh.end();
- console.log(chalk.green('✓'), chalk.blue(`端口 ${host} 的远程命令执行完毕`));
- resolveCommand();
- return;
- }
- const cmd = list[index];
- console.log(chalk.blue('→'), chalk.gray(`执行命令: ${cmd}`));
- ssh.exec(cmd, (err, stream) => {
- if (err) {
- console.error(chalk.red('✗'), chalk.red(`命令执行失败: ${err}`));
- ssh.end();
- rejectCommand(err);
- return;
- }
- let hasError = false;
- stream.on('close', (code, signal) => {
- if (hasError && !cmd.includes('docker build')) {
- console.error(chalk.red('✗'), chalk.red(`命令执行失败,退出码: ${code}`));
- ssh.end();
- rejectCommand(new Error(`命令 "${cmd}" 失败,退出码: ${code}`));
- return;
- }
- console.log(chalk.green('✓'), chalk.gray(`命令执行完成: ${cmd}`));
- runCommand(index + 1);
- }).on('data', (data) => {
- console.log(chalk.gray(data.toString())); // 实时输出命令结果
- }).stderr.on('data', (data) => {
- hasError = true;
- console.error(chalk.yellow('!'), chalk.yellow(data.toString())); // 实时输出错误信息
- });
- });
- };
- ssh.on('ready', () => {
- console.log(chalk.green('✓'), chalk.blue('SSH 连接成功'));
- runCommand(0);
- }).connect(config);
- // Handle SSH connection errors
- ssh.on('error', (err) => {
- console.error(chalk.red('✗'), chalk.red('SSH 连接错误:'), err);
- rejectCommand(err);
- });
- });
- };
- const updateFiles = async () => {
- spinner.text = '正在链接服务器...';
-
- const config = {
- host: env.VITE_HOST,
- port: env.VITE_POST,
- username: env.VITE_USERNAME,
- password: env.VITE_PASSWORD
- };
- const sftp = new SftpClient();
- if (!fs.existsSync('dist')) {
- console.error(chalk.red('✗'), chalk.red('构建目录不存在'));
- spinner.fail('构建目录不存在');
- spinner.stopAndPersist({ text: '' }); // 停止并清理 spinner
- return;
- }
- try {
- await sftp.connect(config);
- spinner.succeed(chalk.green('服务器连接成功'));
- console.log(chalk.blue('→'), chalk.gray('开始上传文件...'));
- await sftp.uploadDir('dist', '/data/data-marketing-platform/build');
- console.log(chalk.green('✓'), chalk.blue('6888端口文件上传完成'));
-
- sftp.end();
- console.log(createSeparator('开始部署 6888 端口'));
- await command([
- "cd /data/data-marketing-platform && pwd",
- "cd /data/data-marketing-platform && ls -la",
- "cd /data/data-marketing-platform && docker rm -f seo-vue",
- "cd /data/data-marketing-platform && docker rmi seo-vue",
- "cd /data/data-marketing-platform && docker build -t seo-vue .",
- "cd /data/data-marketing-platform && docker run -d --name seo-vue -p 6888:80 seo-vue"
- ], config, "6888");
-
- } catch (err) {
- console.error(chalk.red('✗'), chalk.red('部署或文件上传失败:'), err);
- spinner.fail(err);
- spinner.stopAndPersist({ text: '' }); // 停止并清理 spinner
- sftp.end();
- throw err; // 向上抛出错误,让main函数捕获并处理退出
- }
- };
- // 项目构建
- console.log(createSeparator('开始构建项目'));
- // 使用 Promise 包装构建过程
- const buildWithProgress = () => {
- return new Promise((resolve, reject) => {
- // 重置进度条
- speed = 20;
- time = 20;
- spinner.start(); // 确保每次构建前启动 spinner
- const buildProcess = exec('npm run build');
- let buildOutput = '';
- buildProcess.stdout.on('data', (data) => {
- buildOutput += data;
- // 根据输出更新进度
- if (data.includes('compiled successfully')) {
- speed = count;
- }
- });
- buildProcess.stderr.on('data', (data) => {
- buildOutput += data;
- });
- buildProcess.on('close', async (code) => {
- if (code === 0) {
-
- spinner.succeed(chalk.green(`构建成功`));
- resolve(buildOutput);
- } else {
- spinner.fail(chalk.red(`构建失败`));
- reject(new Error(buildOutput));
- }
- });
- });
- };
- // 主构建流程
- const main = async () => {
- spinner.start(); // 在主流程开始时启动 spinner,用于总体的进度指示
- try {
- console.log(createSeparator('开始构建 6888 端口'));
- // 构建 6888 端口版本
- await buildWithProgress();
- // 开始部署
- await updateFiles();
- // 所有任务完成后,打印最终访问路径并停止 spinner
- clearInterval(interval); // 在所有任务完成后停止进度条动画
- console.log(createSeparator('部署完成'));
- console.log(chalk.green('🚀'), chalk.cyan(`6888 端口访问地址: http://${env.VITE_HOST}:6888`));
- spinner.stopAndPersist({ text: '' }); // 停止并清理 spinner,返回控制台
- process.exit(0); // 正常退出,返回控制台
- } catch (error) {
- console.error(chalk.red('✗'), chalk.red('发布流程出错:'), error);
- spinner.fail(error);
- spinner.stopAndPersist({ text: '' }); // 停止并清理 spinner,返回控制台
- process.exit(1); // 异常退出
- }};
- // 启动主流程
- main();
|