基于electron開發(fā)輕量級自動部署工具-部署狗

部署狗

  • 適用與個人與小團(tuán)隊(duì)的輕量級自動部署工具
  • 基于electron+vue+element開發(fā)
  • github

技術(shù)棧

  • electron-vue

開發(fā)

$ npm install
$ npm run dev

先來一波圖

項(xiàng)目管理

avatar

添加項(xiàng)目

avatar

發(fā)布進(jìn)度

avatar

部署流程

avatar

安裝包

windows下載

具體代碼:

  • 部署代碼 deploy.js (electron主進(jìn)程中執(zhí)行)
const path = require('path');
const node_ssh = require('node-ssh');
const zipFile = require('compressing')// 壓縮zip

let SSH = new node_ssh(); // 生成ssh實(shí)例
let mainWindow = null; // 窗口實(shí)例,用于向向渲染進(jìn)程通信

// 部署流程入口
const deploy = async (config, mainWindows) => {
    mainWindow = mainWindows
    await startZip(config);
    await connectSSH(config);
    await uploadZipBySSH(config)
}

//壓縮代碼
const startZip = async (config) => {
    return new Promise((resolve, reject) => {
        let { distPath } = config;
        let distZipPath = path.resolve(distPath, `../dist.zip`);
        mainWindow.send('deploy', '本地項(xiàng)目開始壓縮')
        zipFile.zip.compressDir(distPath, distZipPath).then(res => {
            mainWindow.send('deploy', `本地項(xiàng)目壓縮完成:${distZipPath}`)
            resolve()
        }).catch(err => {
            mainWindow.send('deploy', `壓縮失敗${err}`)
            reject()
        })
    })
}

//連接服務(wù)器
const connectSSH = async (config) => {
    return new Promise((resolve, reject) => {
        mainWindow.send('deploy', `正在連接服務(wù)器:${config.host}`)
        SSH.connect({
            host: config.host,
            username: config.username,
            password: config.password // 密碼登錄 方式二
        }).then(res => {
            mainWindow.send('deploy', `連接服務(wù)器成功:${config.host}`)
            resolve()
        }).catch(err => {
            mainWindow.send('deploy', `連接服務(wù)器失敗:${err}`)
            reject()
        })
    })
}

//清空線上目標(biāo)目錄里的舊文件
const clearOldFile = async (config) => {
    mainWindow.send('deploy', `準(zhǔn)備清空服務(wù)器部署目錄${config.webDir}內(nèi)的文件`)
    const commands = ['ls', 'rm -rf *'];
    await Promise.all(commands.map(async (item) => {
        return await SSH.execCommand(item, { cwd: config.webDir });
    }));
    mainWindow.send('deploy', `清空服務(wù)器目錄${config.webDir}內(nèi)的文件完成`)
}

//上傳zip文件到服務(wù)器
const uploadZipBySSH = async (config) => {
    let distZipPath = path.resolve(config.distPath, `../dist.zip`);
    //線上目標(biāo)文件清空
    await clearOldFile(config);
    try {
        await SSH.putFiles([{ local: distZipPath, remote: config.webDir + '/dist.zip' }]); //local 本地 ; remote 服務(wù)器 ;
        mainWindow.send('deploy', `上傳文件到服務(wù)器成功:${config.webDir}`)
        await SSH.execCommand('unzip -o dist.zip && rm -f dist.zip', { cwd: config.webDir }); //解壓
        mainWindow.send('deploy', `解壓上傳到服務(wù)器的文件成功`)
        await SSH.execCommand(`rm -rf ${config.webDir}/dist.zip`, { cwd: config.webDir }); //解壓完刪除線上壓縮包
        mainWindow.send('deploy', `刪除上傳到服務(wù)器的文件成功`)

        //將解壓后的文件夾內(nèi)的所有文件移動到目標(biāo)目錄
        var dir = path.basename(path.join(config.distPath))
        mainWindow.send('deploy', `將${config.webDir}/${dir}/內(nèi)解壓的文件移動到目錄${config.webDir}`)
        await SSH.execCommand(`mv - f ${config.webDir}/${dir}/*  ${config.webDir}`);
        await SSH.execCommand(`rm -rf ${config.webDir}/${dir}`); //移出后刪除 dist 文件夾
        mainWindow.send('deploy', `全部完成`)
        SSH.dispose(); //斷開連接
    } catch (error) {
        mainWindow.send('deploy', `文件上傳到服務(wù)器失敗:${error}`)
        // process.exit(); //退出流程
    }
}

export default deploy

  • 主進(jìn)程進(jìn)行監(jiān)聽與渲染進(jìn)行發(fā)送的命令
// 監(jiān)聽前臺傳來的deploy命令
ipcMain.on('deploy', (e, data) => {
  deploy(data, mainWindow)
});
  • 前臺部分代碼
<template>
  <div id="wrapper">
    <el-row :gutter="20" class="pro-list">
      <el-col :span="12" v-for="(item, index) in dataList" :key="index">
        <el-card class="box-card">
          <ul class="prolist">
            <li>
              <span class="label">項(xiàng)目名稱:</span>
              <span class="info">{{ item.projectName }}</span>
            </li>
            <li>
              <span class="label">服務(wù)器地址:</span>
              <span class="info">{{ item.host }}</span>
            </li>
        //    ...other
          </ul>
          <div class="btns">
            <el-button icon="el-icon-s-promotion" @click="deploy(item)" type="primary">發(fā)布</el-button>
          </div>
        </el-card>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import { ipcRenderer } from "electron";
export default {
  name: "prolist",
  created() {
    this.getDb();
    // 接收主進(jìn)程部署過程 中的 過程信息
    ipcRenderer.on("deploy", (event, arg) => {
      this.activities.unshift({
        content:arg,
        timestamp: new Date().toLocaleTimeString()
      });
    });
  },
  data() {
    return {
      dataList: [],
      activities: []
    };
  },
  methods: {

    // 部署
    deploy(config) {
    //   發(fā)送打包命令 給 主進(jìn)程
      ipcRenderer.send("deploy", config);
    },
    remove(config) {
    //   刪除項(xiàng)目
    }
  }
};
</script>

僅供大家學(xué)習(xí)參考

所有數(shù)據(jù)存在本地lowdb中,所以不用擔(dān)心安全問題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容