前端自动化部署项目到服务器


连接服务器工具

MobaXterm
securecrt
xshell
putty
powershell

第一种方式

借助ssh这些插件链接服务器部署,具体代码如下

// deploy/index.js
import scpClient from 'scp2'
import chalk from 'chalk'
import ssh from 'ssh2'
import ora from 'ora'
import path from 'path'
import server from './config.js'
const source_path = path.resolve() + '/dist'
const spinner = ora('正在发布到测试服务器...')

spinner.start()
const Client = ssh.Client
const conn = new Client()

conn.on('ready', function () {
    
    
  conn.exec('rm -rf /www/wwwroot/***(项目放置静态地址)', function (err, stream) {
    
    
    if (err) throw err

    stream.on('close', function () {
    
    
      // 在执行shell命令后,把开始上传部署项目代码放到这里面
      scpClient.scp(source_path, {
    
    
        host: server.host,
        port: server.port,
        username: server.username,
        password: server.password,
        path: server.path, // 项目放置静态地址(服务器中地址)
      }, function (err) {
    
    
        spinner.stop()

        if (err) {
    
    
          console.log(chalk.red('发布失败.\n'))
        } else {
    
    
          console.log(chalk.green('Success! 成功发布到服务器! \n'))
        }
      })
      conn.end()
    }).on('data', function (data) {
    
    
      console.log('STDOUT: ' + data)
    }).stderr.on('data', function (data) {
    
    
      console.log('STDERR: ' + data)
    })
  })
}).connect({
    
    
  host: server.host,
  port: server.port,
  username: server.username,
  password: server.password,
})




// deploy.config.js
export default {
    
    
    host: '',
    port: '',
    username: '',
    password: '',
}
// package.json
{
    
    
 "type": "module",
	"scripts": {
    
    
		"build:develop": "vite build --mode develop && node ./deploy/index.js",
	}
}


第二种

全局安装gupack部署

/build/gupack-config.js

'use strict';

const path = require('path')
const argv = require('yargs').argv;
const dist = argv['d'] || argv['dest'] || '../dist';
const distPath = path.isAbsolute(dist) ? dist : path.resolve(__dirname, dist);
//导出模块
module.exports = {
    
    
  sourceDir: path.resolve(__dirname, '../src'),
  buildDir: distPath,
  // 每次执行编译之前是否清理当前编译目录
  startClean: true,
  watch: false,
  // task任务列表
  buildTasks: require('./tasks'),
  // 部署配置
  deploy: require('./deploy'),
};

/build/deploy/index.js

/**
 * Deploy 应用部署相关, 可根据env设置对应的节点,支持多节点部署
 */
'use strict';

const path = require('path');
console.log('110', process.env)
const filters = [
  path.resolve(__dirname,`../../dist/static/**/*`),
];
const env = process.env.NODE_ENV;
const servers = {
    
    
  sit: [
    // {
    
    
    //   host: '10.91.193.190',
    //   user: '*',
    //   pass: '*',
    //   remotePath: '/*',
    //   type: 'increment',
    //   filters
    // }
  ],
  uat: [],
  prod: [],
};

module.exports = servers[env];

/build/tasks/index.js

/**
 * Build Tasks 应用编译相关任务
 */
'use strict';

// const path = require('path');
// const $ = require('gulp-load-plugins')();
// const config = require('../../config');
// const { createVersion, getVersion } = require('../webpack/utils');

// const distPath = config.dest.path;
// const sourcePath = config.source.path;

// createVersion();
module.exports = {
    
    
  // ---说明:单个任务配置

  // webpack编译
  // 'build.main': $.shell.task([
  //   `cross-env NODE_ENV=${config.env} node ${path.resolve(__dirname, '../webpack/build.js')}`,
  // ]),

  // 合并配置文件
  // 'build.concat:config': {
    
    
  //   src: [`${sourcePath}/config/env/${config.env}.js`, `${sourcePath}/config/init.js`],
  //   dest: `${distPath}/assets/js`,
  //   loader: stream => stream.pipe($.concat('index.js')),
  // },

  // 处理manifest
  // 'replace.manifest.js': {
    
    
  //   src: `${distPath}/assets/js/manifest.js`,
  //   dest: `${distPath}/assets/js`,
  //   rely: ['build.main'],
  //   loader: stream => stream.pipe($.replace(/\".js\";/g, `".js?_vc=${Date.now()}";`)),
  //   // loader: stream => stream.pipe($.replace(/\".js\";/g, `".js?_vc=${getVersion()}";`)),
  // },
};

package.json

"sit-gupack": "cross-env NODE_ENV=sit gupack build -f ./build/gupack-config.js",
"uat-gupack": "cross-env NODE_ENV=uat gupack build -f ./build/gupack-config.js",

第三种

/build/deploy/indexjs

// deploy.js

const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const node_ssh = require('node-ssh');
const archiver = require('archiver');
const {
    
     successLog, errorLog, underlineLog } = require('../utils/index');
const projectDir = process.cwd();

let ssh = new node_ssh(); // 生成ssh实例

// 部署流程入口
function deploy(config) {
    
    
  const {
    
     script } = config;
  try {
    
    
    console.log(`\n(1)${
      
      script}`);
    childProcess.execSync(`${
      
      script}`);
    successLog('  打包成功');
    startZip(config);
  } catch (err) {
    
    
    errorLog(err);
    process.exit(1);
  }
}

// 开始打包
function startZip(config) {
    
    
  let {
    
     distPath, host } = config;
  distPath = path.resolve(projectDir, distPath);
  console.log('(2)打包成zip');
  const archive = archiver('zip', {
    
    
    zlib: {
    
     level: 9 },
  }).on('error', err => {
    
    
    throw err;
  });
  const output = fs.createWriteStream(`${
      
      projectDir}/dist.zip`).on('close', err => {
    
    
    if (err) {
    
    
      console.log('  关闭archiver异常:', err);
      return;
    }
    successLog('  zip打包成功');
    console.log(`(3)连接${
      
      underlineLog(host)}`);
    uploadFile(config);
  });
  archive.pipe(output);
  archive.directory(distPath, '/');
  archive.finalize();
}

// 上传文件
function uploadFile(config) {
    
    
  const {
    
     host, port, username, password, privateKey, passphrase, } = config;
  const sshConfig = {
    
    
    host,
    port,
    username,
    password,
    privateKey,
    passphrase
  };
  ssh.connect(sshConfig)
    .then(() => {
    
    
      successLog(`  SSH连接成功`);
      console.log(`(4)上传zip至目录${
      
      underlineLog(config.webDir)}`);
      ssh.putFile(`${
      
      projectDir}/dist.zip`, `${
      
      config.webDir}/dist.zip`)
        .then(() => {
    
    
          successLog(`  zip包上传成功`);
          console.log('(5)解压zip包');
          statrRemoteShell(config);
        })
        .catch(err => {
    
    
          errorLog('  文件传输异常', err);
          process.exit(0);
        });
    })
    .catch(err => {
    
    
      errorLog('  连接失败', err);
      process.exit(0);
    });
}

// 执行Linux命令
function runCommand(command, webDir) {
    
    
  return new Promise((resolve, reject) => {
    
    
    ssh.execCommand(command, {
    
     cwd: webDir })
      .then(result => {
    
    
        resolve();
        // if (result.stdout) {
    
    
        //   successLog(result.stdout);
        // }
        if (result.stderr) {
    
    
          errorLog(result.stderr);
          process.exit(1);
        }
      })
      .catch(err => {
    
    
        reject(err);
      });
  });
}

// 开始执行远程命令
function statrRemoteShell(config) {
    
    
  const {
    
     webDir } = config;
  const commands = [`cd ${
      
      webDir}`, 'pwd', 'unzip -o dist.zip && rm -f dist.zip'];
  const promises = [];
  for (let i = 0; i < commands.length; i += 1) {
    
    
    promises.push(runCommand(commands[i], webDir));
  }
  Promise.all(promises)
    .then(() => {
    
    
      successLog('  解压成功');
      console.log('(6)开始删除本地dist.zip');
      deleteLocalZip(config);
    })
    .catch(err => {
    
    
      errorLog('  文件解压失败', err);
      process.exit(0);
    });
}

// 删除本地dist.zip包
function deleteLocalZip(config) {
    
    
  const {
    
     projectName, name } = config;
  fs.unlink(`${
      
      projectDir}/dist.zip`, err => {
    
    
    if (err) {
    
    
      errorLog('  本地dist.zip删除失败', err);
    }
    successLog('  本地dist.zip删除成功\n');
    successLog(`\n 恭喜您,${
      
      underlineLog(projectName)}项目${
      
      underlineLog(name)}部署成功了^_^\n`);
    process.exit(0);
  });
}

module.exports = deploy;

/build/deploy-cli/deploy.config.js

// deploy.config.js

module.exports = {
    
    
    privateKey: '', // 本地私钥地址,位置一般在C:/Users/xxx/.ssh/id_rsa,非必填,有私钥则配置
    passphrase: '', // 本地私钥密码,非必填,有私钥则配置
    projectName: '', // 项目名称
    dev: {
    
     // 测试环境
      name: '测试环境',
      script: "npm run build", // 测试环境打包脚本
      host: '', // 测试服务器地址
      port: 22, // ssh port,一般默认22
      username: '', // 登录服务器用户名
      password: '', // 登录服务器密码
      distPath: 'dist',  // 本地打包dist目录
      webDir: '',  // // 测试环境服务器地址
    },
    prod: {
    
      // 线上环境
      name: '线上环境',
      script: "npm run build", // 线上环境打包脚本
      host: '', // 线上服务器地址
      port: 22, // ssh port,一般默认22
      username: '', // 登录服务器用户名
      password: '', // 登录服务器密码
      distPath: 'dist',  // 本地打包dist目录
      webDir: '' // 线上环境web目录
    }
    // 再还有多余的环境按照这个格式写即可
  }
  

/build/deploy-cli/index.js

// deploy.js

const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const ora = require('ora');
const node_ssh = require('node-ssh');
const archiver = require('archiver');
const {
    
     successLog, errorLog, underlineLog } = require('../utils/index');
const projectDir = process.cwd();

let ssh = new node_ssh(); // 生成ssh实例

// 部署流程入口
async function deploy(config) {
    
    
  const {
    
     script, webDir, distPath, projectName, name } = config;
  try {
    
    
    execBuild(script);
    await startZip(distPath);
    await connectSSH(config);
    await uploadFile(webDir);
    await unzipFile(webDir);
    await deleteLocalZip();
    successLog(`\n 恭喜您,${
      
      underlineLog(projectName)}项目${
      
      underlineLog(name)}部署成功了^_^\n`);
    process.exit(0);
  } catch (err) {
    
    
    errorLog(`  部署失败 ${
      
      err}`);
    process.exit(1);
  }
}

// 第一步,执行打包脚本
function execBuild(script) {
    
    
  try {
    
    
    console.log(`\n(1)${
      
      script}`);
    const spinner = ora('正在打包中');
    spinner.start();
    console.log();
    childProcess.execSync(script, {
    
     cwd: projectDir });
    spinner.stop();
    successLog('  打包成功');
  } catch (err) {
    
    
    errorLog(err);
    process.exit(1);
  }
}

// 第二部,打包zip
function startZip(distPath) {
    
    
  return new Promise((resolve, reject) => {
    
    
    distPath = path.resolve(projectDir, distPath);
    console.log('(2)打包成zip');
    const archive = archiver('zip', {
    
    
      zlib: {
    
     level: 9 },
    }).on('error', err => {
    
    
      throw err;
    });
    const output = fs.createWriteStream(`${
      
      projectDir}/dist.zip`);
    output.on('close', err => {
    
    
      if (err) {
    
    
        errorLog(`  关闭archiver异常 ${
      
      err}`);
        reject(err);
        process.exit(1);
      }
      successLog('  zip打包成功');
      resolve();
    });
    archive.pipe(output);
    archive.directory(distPath, '/');
    archive.finalize();
  });
}

// 第三步,连接SSH
async function connectSSH(config) {
    
    
  const {
    
     host, port, username, password, privateKey, passphrase, distPath } = config;
  const sshConfig = {
    
    
    host,
    port,
    username,
    password,
    privateKey,
    passphrase
  };
  try {
    
    
    console.log(`(3)连接${
      
      underlineLog(host)}`);
    await ssh.connect(sshConfig);
    successLog('  SSH连接成功');
  } catch (err) {
    
    
    errorLog(`  连接失败 ${
      
      err}`);
    process.exit(1);
  }
}

// 第四部,上传zip包
async function uploadFile(webDir) {
    
    
  try {
    
    
    console.log(`(4)上传zip至目录${
      
      underlineLog(webDir)}`);
    await ssh.putFile(`${
      
      projectDir}/dist.zip`, `${
      
      webDir}/dist.zip`);
    successLog('  zip包上传成功');
  } catch (err) {
    
    
    errorLog(`  zip包上传失败 ${
      
      err}`);
    process.exit(1);
  }
}


// 运行命令
async function runCommand(command, webDir) {
    
    
  await ssh.execCommand(command, {
    
     cwd: webDir });
}

// 第五步,解压zip包
async function unzipFile(webDir) {
    
    
  try {
    
    
    console.log('(5)开始解压zip包');
    await runCommand(`cd ${
      
      webDir}`, webDir);
    await runCommand('unzip -o dist.zip && rm -f dist.zip', webDir);
    successLog('  zip包解压成功');
  } catch (err) {
    
    
    errorLog(`  zip包解压失败 ${
      
      err}`);
    process.exit(1);
  }
}

// 第六步,删除本地dist.zip包
async function deleteLocalZip() {
    
    
  return new Promise((resolve, reject) => {
    
    
    console.log('(6)开始删除本地zip包');
    fs.unlink(`${
      
      projectDir}/dist.zip`, err => {
    
    
      if (err) {
    
    
        errorLog(`  本地zip包删除失败 ${
      
      err}`, err);
        reject(err);
        process.exit(1);
      }
      successLog('  本地dist.zip删除成功\n');
      resolve();
    });
  });
}


module.exports = deploy;

/build/deploy-cli/init.js

// init.js

// #!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const download = require('download-git-repo');
const ora = require('ora');
const {
    
     successLog, infoLog, errorLog } = require('../utils/index');
let tmp = 'deploy';
const deployPath = path.join(process.cwd(), './deploy');
const deployConfigPath = `${
      
      deployPath}/deploy.config.js`;
const deployGit = 'dadaiwei/fe-deploy-cli-template';

// 检查部署目录及部署配置文件是否存在
const checkDeployExists = () => {
    
    
    if (fs.existsSync(deployPath) && fs.existsSync(deployConfigPath)) {
    
    
        infoLog('deploy目录下的deploy.config.js配置文件已经存在,请勿重新下载');
        process.exit(1);
        return;
    }
    downloadAndGenerate(deployGit);
};

// 下载部署脚本配置
const downloadAndGenerate = templateUrl => {
    
    
    const spinner = ora('开始生成部署模板');
    spinner.start();
    download(templateUrl, tmp, {
    
     clone: false }, err => {
    
    
        if (err) {
    
    
            console.log();
            errorLog(err);
            process.exit(1);
        }
        spinner.stop();
        successLog('模板下载成功,模板位置:deploy/deploy.config.js');
        infoLog('请配置deploy目录下的deploy.config.js配置文件');
        process.exit(0);
    });
};

module.exports = () => {
    
    
    checkDeployExists();
};

/build/deploy-cli/remore.md

与脚手架相关的npm包:

commander:node.js命令行界面的完整解决方案

download-git-repo:git仓库代码下载

ora:显示加载中的效果

inquirer:用户与命令交互的工具

child_process:npm内置模块,用于执行package.json中的打包script

引用限制

package.json设置
{
    
    
  ...
  "type": "module"
  "scripts": {
    
    ...}
}

猜你喜欢

转载自blog.csdn.net/weiCong_Ling/article/details/130771235