基於vue cli的前端自動部署,自動備份index.html,可版本退回

指望:javascript

  1. 但願經過npm命令實現遠程服務端部署
  2. 部署分測試環境、生產環境
  3. 打包部署前必須保證本地代碼爲最新代碼,與git服務器同步
  4. 須要保留vue cli的版本管理,上傳代碼前備份index.html,方便版本退回

思路html

  1. 定義測試環境與生產環境的配置對象,包括host、port、username、password、path(部署路徑)、outputDir(打包文件夾)
  2. 拉取所在分支最新代碼,保證本地代碼與git服務器一致(git fetch --all && git reset --hard origin/branchName && git pull,操做給出提示,確保本地有用代碼已經提交)
  3. 經過env環境打對應的包
  4. 經過ssh2,遠程執行linux stat命令,獲取index.html文件的最後修改時間,工具這個時間生成版本號
  5. 經過ssh2,執行linux的cp命令,拷貝服務器上index.html,重命名爲index.201911081804.html(201911081804爲index.html的最後修改時間)
  6. 用scp2插件上傳打包好的文件到服務器目標目錄

實現 (代碼比較潦草,須要整理)vue

deploy.jsjava

// /deploy.js
const scpClient = require("scp2");
const exec = require("child_process").exec;
const execSync = require("child_process").execSync;
const readline = require("readline");
/*
 *定義多個服務器帳號
 */
const SERVER_LIST = {
  // 測試環境
  test: {
    name: "測試環境",
    host: "", // ip
    port: 22, // 端口
    username: "root", // 登陸服務器的帳號
    password: "@", // 登陸服務器的帳號
    path: "/home/static/cnhpec", // 發佈至靜態服務器的項目路徑
    outputDir: "dist/" // 打包的目錄 對應vue.config的outputDir配置
  },
  // 開發環境
  prod: {
    name: "生產環境",
    host: "",
    port: 22,
    username: "root",
    password: "@",
    path: "/home/static/cnhpec",
    outputDir: "dist/"
  }
};

const argv = process.argv.slice(2);
const env = argv[0] ? (argv[0] === "prod" ? "prod" : "test") : "test";
const indexFile = SERVER_LIST[env].path + "/index.html";

const Client = require("ssh2").Client;

const conn = new Client();
conn
  .on("ready", function() {
    init();
  })
  .on("error", function(err) {
    if (err) throw err;
  })
  .connect({
    host: SERVER_LIST[env].host,
    port: SERVER_LIST[env].port,
    username: SERVER_LIST[env].username,
    password: SERVER_LIST[env].password
  });

async function init() {
  await pull(); // 拉git最新代碼
  await build(); // 打包
  await rename(); // 備份服務器代碼,生成版本
  await upload(); // 上傳代碼
}

function pull() {
  return new Promise(function(resolve, reject) {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    });
    select();
    function select() {
      rl.question(
        "此操做git項目強制與遠端同步!!!本地代碼沒提交不要輕易進行操做!!!\n\n此操做git項目強制與遠端同步!!!本地代碼沒提交不要輕易進行操做!!!\n\n此操做git項目強制與遠端同步!!!本地代碼沒提交不要輕易進行操做!!!(y/n)",
        function(answer) {
          answer = answer.trim().toLowerCase();
          if (answer === "y") {
            const branch = execSync("git symbolic-ref --short HEAD");
            exec(`git fetch --all && git reset --hard origin/${branch.toString()} && git pull`, function(err, stdout, stderr) {
              if (err) {
                console.error("項目強制與遠端同步失敗!!!");
                reject();
                throw err;
              }
              console.info(stdout.toString());
              console.error(stderr);
              console.log("項目強制與遠端同步成功");
              resolve();
            });
            rl.close();
          } else if (answer === "n") {
            reject();
            rl.close();
            process.exit(0);
          } else {
            select();
          }
        }
      );
    }
  });
}

function build() {
  return new Promise(function(resolve, reject) {
    console.log(`開始打包${env === "prod" ? "生產" : "測試"}環境。。。。。。`);
    exec("npm run " + env, function(err, stdout, stderr) {
      if (err) {
        console.error("打包失敗!!!");
        reject();
        process.exit(1);
        throw err;
      }
      console.info(stdout.toString());
      console.error(stderr);
      console.log("打包成功");
      resolve();
    });
  });
}

function rename() {
  return new Promise(function(resolve, reject) {
    console.log("開始備份服務器版本。。。。。。");
    conn.exec("stat " + indexFile, function(err, stream) {
      if (err) {
        console.error("服務器版本備份失敗。。。。。。");
        conn.end();
        reject();
        process.exit(1);
        throw err;
      }
      let mtime;
      stream
        .on("close", function(code) {
          if (code == 0) {
            copy(mtime, resolve, reject);
          } else {
            resolve();
          }
        })
        .on("data", function(data) {
          console.info(data.toString());
          mtime = data.toString().split("\n")[7];
          mtime = mtime.replace(/[\u4e00-\u9fa5]/g, "").replace(":", "");
          mtime = formatDate(mtime);
        })
        .stderr.on("data", function(data) {
          console.warn(data.toString());
        });
    });
  });
}

function copy(mtime, resolve, reject) {
  conn.exec("/bin/cp " + indexFile + " " + SERVER_LIST[env].path + "/index." + mtime + ".html", function(err, stream) {
    if (err) {
      console.error("服務器版本備份失敗。。。。。。");
      conn.end();
      reject();
      process.exit(1);
      throw err;
    }
    stream
      .on("close", function(code) {
        if (code == 0) {
          console.log("服務器版本備份成功");
          resolve();
        } else {
          console.error("服務器版本備份失敗。。。。。。");
          conn.end();
          reject();
          process.exit(1);
        }
      })
      .on("data", function(data) {
        console.info(data.toString());
      })
      .stderr.on("data", function(data) {
        console.error(data.toString());
      });
  });
}

function upload() {
  return new Promise(function(resolve, reject) {
    scpClient.scp(
      SERVER_LIST[env].outputDir,
      {
        host: SERVER_LIST[env].host,
        port: SERVER_LIST[env].port,
        username: SERVER_LIST[env].username,
        password: SERVER_LIST[env].password,
        path: SERVER_LIST[env].path
      },
      function(err) {
        conn.end();
        if (err) {
          reject();
          process.exit(1);
          throw err;
        } else {
          console.log("Success! 成功發佈到" + (env === "prod" ? "生產" : "測試") + "服務器!");
          resolve();
          process.exit(0);
        }
      }
    );
  });
}

function formatDate(date) {
  const isZero = m => (m < 10 ? "0" + m : m),
    time = new Date(date),
    y = time.getFullYear(),
    m = time.getMonth() + 1,
    d = time.getDate(),
    h = time.getHours(),
    mm = time.getMinutes(),
    s = time.getSeconds();
  return y + "" + isZero(m) + "" + isZero(d) + "" + isZero(h) + "" + isZero(mm) + "" + isZero(s);
}

package.json添加部署命令node

{
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build --mode build",
    "test": "vue-cli-service build --mode test",
    "lint": "vue-cli-service lint",
    "i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'",
    "deploy:test": "node ./deploy test",
    "deploy:prod": "node ./deploy prod"
  },
  "devDependencies": {
     "scp2": "^0.5.0"
  }
}

總結linux

  1. 確保了代碼最新來打包
  2. 保證版本能夠退回
  3. 未考慮基於vue cli多頁開發,html文件的版本備份
  4. 沒有作成vue cli的插件
  5.  仍是本地部署的,沒有實現服務端部署,像jenkins同樣(作出vue cli插件,在部署服務器上啓動vue ui,訪問http://ip:8000/tasks在裏面點擊deploy,可實現類型jenkins部署效果)
相關文章
相關標籤/搜索