前端自动化部署的极简方案

5,390 阅读2分钟

打开服务器连接,找到文件夹,删掉,找到打包的目录,ctrl + C, ctrl + v 。。。。 烦的要死。。内网开发,node 的 ssh2 依赖库一时半会还导不进来。

索性,自己写一个!

原生 NodeJS 代码,不需要引用任何第三方库 win10 及以上版本,系统自带ssh 命令行工具,如果没有,需要自行安装

首先,需要生成本地秘钥;

ssh-keygen

执行上述命令后,系统会提示你输入文件保存位置和密码,如果你想使用默认位置和密码,直接按回车接受即可。这将在默认的SSH目录~/.ssh/下生成两个文件:id_rsa(私钥)和id_rsa.pub(公钥)

开启服务端使用秘钥登录

一般文件位置位于 /etc/ssh/sshd_config 中,

找到下面两行 ,取消注释,值改为 yes

RSAAuthentication yes
PubkeyAuthentication yes

将秘钥添加到服务端:

打开服务端文件 /root/.ssh/authorized_keys 将公钥 粘贴到新的一行中

重启服务端 ssh 服务

sudo service ssh restart

编写自动化上传脚本(nodejs 脚本)

// 创建文件 ./build/Autoactic.js

const { exec, spawn } = require('child_process');
const fs= require('fs');

// C:/Users/admin/.ssh/server/ServiceOptions.json
// 此处储存本地连接服务端相关配置数据(目的为不将秘钥暴露给 Git 提交代码)
// {
//     服务端关键字(记录为哪个服务器)
//     "Test90": {
//         服务器登录用户名
//         "Target": "root@255.255.255.255",
//         本地证书位置(秘钥)
//         "Pubkey": "C:/User/admin/.shh/server/file"
//     }
// }

// 温馨提示 本机储存的秘钥需要调整权限,需要删除除了自己以外其他的全部用户
const ServiceOption = Json.parse(fs.readFileSync("C:/Users/admin/.ssh/server/ServiceOptions.json"), "utf-8");

// 本地项目文件路径(dist 打包后的实际路径)
const LocalPath = "D:/Code/rmgm/jilinres/jprmcrm/dev/admin/dist"// 服务端项目路径
const ServerPath = "/home/rmgmuser/web/pmr";

// 运行单行命令 (scp 命令,上传文件使用)
const RunSSHCode = function (code) {
    return new Promise((resolve, reject) => {
        const process = exec(code, (error, sodut, stderr) => {
            if (error) {
                console.error(`执行错误: ${error}`)
                reject();
                return;
            };
            console.log(`sodut:${sodut}`);
            if (stderr) {
                console.error(`stderr:${stderr}`)
            };
            if (process && !process.killed){
                process.kill();
            };
            setTimeout(()=>{
                resolve();
            },10);
        })
    })
}

// 执行服务端命令 (执行 ssh 命令行)
const CommandHandle(command) {
    return new Promise((resolve, reject) => {
        const child = spawn('ssh', ['-i', ServiceOption.Test90.Pubkey, '-o', 'StrictHostKeyChecking=no', ServiceOption.Test90.Target], {
            stdio: ['pipe']
        });
        child.on('close',(err)=>{
            console.log(`--close--:${err}`);
            if (err === 0) {
                setTimeout(()=>{ resolve() },10)
            } else {
                reject();
            }
        })
        child.on('error',(err)=>{
            console.error(`--error--:${err}`)
        });
        console.log(`--command--:${command}`);
        child.stdin.end(command);
        child.stdout.on('data',(data)=>{
            console.log(`Stdout:${data}`);
        })
        child.stderr.on('data',(data)=>{
            console.log(`Stdout:${data}`);
        })
    }
};


// 按照顺序执行代码
!(async function (CommandHandle, RunSSHCode){
    try {
        console.log(`创建文件夹 => ${ServerPath}`);
        await CommandHandle(`mkdir -p ${ServerPath}`);
        console.log(`创建文件夹 => ${ServerPath} => 完成`);
    
        console.log(`删除历史文件 => ${ServerPath}`);
        await CommandHandle(`find ${ServerPath} -type d -exec rm -rf {} +`);
        console.log(`删除历史文件 => ${ServerPath} => 完成`);
    
        console.log(`上传本地文件 => 从 本地 ${LocalPath} 到 服务端 ${ServerPath}`);
        await RunSSHCode(`scp -i ${serviceOption.Test90.pubkey} -r ${LocalPath}/ ${serviceOption.Test90.Target}:${ServerPath}/`);
        console.log(`上传本地文件 => 从 本地 ${LocalPath} 到 服务端 ${ServerPath} => 完成`);
        
        // 吉林环境个性配置 非必须(更改访问权限)
        console.log(`更改访问权限 => ${ServerPath}`);
        await CommandHandle(`chown -R rmgmuser:rmgmuser ${ServerPath}`);
        console.log(`更改访问权限 => ${ServerPath} => 完成`);
    
    } catch (error) {
        console.error(`---END---`, error)
    }
})(CommandHandle, RunSSHCode)

更改打包命令:

// package.json
{
    // ....
    "scripts": {
        // .....

        "uploadFile" : "node ./build/Autoactic.js"
        // 原始的 build 改为 prebuild 命令
        "preBuild" : "vue-cli-service build --mode production"
        // npm 按顺序运行多个命令行
        "build" : "npm run preBuild && npm run uploadFile"
        
         // .....
    }
    //... 
}

效果 打包结束后,直接上传到服务端。

有特殊需求,例如重启服务等,可自行添加。