Node реализует автоматическое развертывание переднего плана с нуля

Node.js

с нуля

обновить:🎉Он был обновлен до кроссплатформенного инструмента автоматического развертывания, см. подробности.Инструмент автоматического развертывания сборки с открытым исходным кодом ⚡ auto-deploy-app

Показать результаты

1. Проект для развертывания упакован и собран локально

本地打包构建目录

2. Определите каталог удаленного развертывания и папку выпуска.

远端部署目录

3. Измените конфигурацию

修改配置文件

4. Запустите автоматическое развертывание

选择配置信息

自动化部署

5. Просмотр удаленных эффектов

远端部署目录

6. Повторное развертывание. Резервная копия исходного каталога создана (откройте удаленную резервную копию, чтобы изменения вступили в силу).

远端部署目录

предисловие

Когда интерфейсный проект развернут, после завершения настройки nginx вам нужно только загрузить упакованные файлы в указанную директорию сервера.

Как правило, это делается следующими способами:

  • xshellПодождите, пока инструмент командной строки загрузится
  • ftpи другие инструменты визуализации для загрузки
  • jenkinsи другие службы автоматизированного развертывания

Для простых интерфейсных проектов при частом развертыванииxshell,ftpОба метода более сложные иjenkinsДругим службам автоматизированного развертывания необходимо заранее установить программное обеспечение и ознакомиться с процессом настройки. Поэтому есть надежда, что местныеnodeСервис реализует загрузку интерфейсных упакованных файлов, что не требует дополнительных программ установки на сервер, но также помогает нам добиться быстрой загрузки и развертывания, а также может помочь нам глубже понятьnode.

PS: Раньше я готовил реализацию соответствующего внешнего автоматизированного развертывания и только недавно увидел ее.да да, обратитесь к статьям, упомянутым в статьеnode-ssh а такжеarchiver, завершить реализацию проекта, Спасибо здесь (тоже спасибоnode-ssh ,archiver,inquirerавтор).

Прикрепил:npm地址

Начинать

1. Определите потребности

Перед разработкой необходимо сначала уточнить требования.Согласно общему процессу развертывания интерфейса, вкратце описан следующий процесс:

前端部署流程

Определите необходимость автоматического развертывания на основе процесса развертывания:

明确需求

2. Подготовка перед разработкой

2.1 Импорт зависимых модулей

Поскольку необходимо реализовать сжатие файлов, подключиться к удаленному серверу и реализовать удаленный вызов команд, требуются как минимум следующие модули:

  • sshМодуль (может выполнять общие операции, такие как подключение к серверу, вызов команд и т. д.)
  • 文件压缩модуль (достижимый.zipнапример, локальная упаковка общих сжатых файлов)
  • 命令行选择Модуль (может реализовать выбор и использование нескольких файлов элементов конфигурации)

Поиск информации, окончательный выборnode-ssh,archiver,inquirerсоответственно реализовать вышеуказанные функции.

# 安装依赖
npm i node-ssh --save
npm i archiver --save
npm i inquirer --save

2.2 Как реализовать спецификацию

чтобы реализовать требования解耦合理а также逻辑清晰/灵活, нужно обратить внимание на общую логику программы, выбирать здесь封装соответствующие функции реализованы и主程序Средний свободный график (гибкий调用/关闭/修改связанные функции) и выдавать подсказки для текущих выполняемых функций, чтобы убедиться, что функции реализованы.完整性а также异常提示.

потому что文件压缩,文件上传,执行远端命令и т.д. процесс асинхронный, поэтому порядок выполнения функций должен быть четким, т.е.应在前置任务完成的回调中开启当前任务, чтобы контролировать асинхронный процесс и логика кода понятна, здесь мы выбираем использование ES6PromiseСинтаксический сахар в сочетании с ES7async awiatРеализовать логическое управление потоком.

На этом разборка построения программной функции завершена, и теперь начинается реализация проекта.

3. Реализация функции

Предварительный просмотр каталога проектаВот каталог проекта конечного результата для справки:工程目录

  • node_modules
  • utils
    • CompressFile.js [сжимать локальные файлы]
    • handleCommand.js [вызов удаленной команды]
    • handleTime.js [время обработки]
    • helper.js [запрос выбора проекта развертывания]
    • ssh.js [подключиться к удаленному серверу]
    • uploadFile.js [загрузить локальный файл]
  • app.js [основная программа]
  • config.js [файл конфигурации]
  • dist.zip [сжатый файл] (текущая версия не удаляется автоматически)
  • package.json
  • README.md【Введение в проект】

3.1 Сжатие локальных файлов

compressFileперенимать需要压缩的目录а также打包生成文件, чтобы обеспечить локальное сжатие файла после его передачи.

compressFile.jsКод ссылки

// compressFile.js
const fs = require('fs')
const archiver = require('archiver')

function compressFile (targetDir, localFile) {
  return new Promise((resolve, reject)=>{
    console.log('1-正在压缩文件...')
    let output = fs.createWriteStream(localFile) // 创建文件写入流
    const archive = archiver('zip', {
      zlib: { level: 9 } // 设置压缩等级
    })
    output.on('close', () => {
      resolve(
        console.log('2-压缩完成!共计 ' + (archive.pointer() / 1024 /1024).toFixed(3) + 'MB')
      )
    }).on('error', (err) => {
      reject(console.error('压缩失败', err))
    })
    archive.pipe(output) // 管道存档数据到文件
    archive.directory(targetDir, 'dist') // 存储目标文件并重命名
    archive.finalize() // 完成文件追加 确保写入流完成
  })
}

module.exports = compressFile

3.2 Подключиться к удаленному серверу

connectServeПолучить дальний конецip,用户名,密码и другую информацию для завершения подключения к удаленному серверу, пожалуйста, обратитесь к конкретной конфигурацииconfig.js.

ssh.jsКод ссылки

// ssh.js
const node_ssh = require('node-ssh')
const ssh = new node_ssh()

function connectServe (sshInfo) {
  return new Promise((resolve, reject) => {
    ssh.connect({ ...sshInfo }).then(() => {
      resolve(console.log('3-' + sshInfo.host + ' 连接成功'))
    }).catch((err) => {
      reject(console.error('3-' + sshInfo.host + ' 连接失败', err))
    })
  })
}

module.exports = connectServe

3.3 Удаленное выполнение команд

runCommandперенимать需执行的命令а также执行命令的远端路径, здесь она разделена отдельно, что удобно主程序Отдельный звонок тоже удобен文件上传и другие функции пакета модулей для достижения解耦Эффект.

handleCommand.jsКод ссылки

// handleCommand.js
// run linux shell(ssh对象、shell指令、执行路径)
function runCommand (ssh, command, path) {
  return new Promise((resolve, reject) => {
    ssh.execCommand(command, {
      cwd: path
    }).then((res) => {
      if (res.stderr) {
        reject(console.error('命令执行发生错误:' + res.stderr))
        process.exit()
      } else {
        resolve(console.log(command + ' 执行完成!'))
      }
    })
  })
}

module.exports = runCommand

3.4 Загрузка файла

uploadFileперенимать系统配置参数,待上传的本地文件, завершите загрузку локального файла в указанный каталог сервера, что также представлено здесьhandleCommand.js,handleTime.js,согласно сopenBackUpВключить ли удаленное резервное копирование и завершить обработку существующих каталогов с тем же именем после распаковки. Ссылка на конкретную конфигурациюconfig.js.

uploadFile.jsКод ссылки

// uploadFile.js
const runCommand = require ('./handleCommand')
const getCurrentTime = require ('./handleTime')

// 文件上传(ssh对象、配置信息、本地待上传文件)
async function uploadFile (ssh, config, localFile) {
  return new Promise((resolve, reject) => {
    console.log('4-开始文件上传')
    handleSourceFile(ssh, config)
    ssh.putFile(localFile, config.deployDir + config.targetFile).then(async () => {
      resolve(console.log('5-文件上传完成'))
    }, (err) => {
      reject(console.error('5-上传失败!', err))
    })
  })
}

// 处理源文件(ssh对象、配置信息)
async function handleSourceFile (ssh, config) {
  if (config.openBackUp) {
    console.log('已开启远端备份!')
    await runCommand(
      ssh,
      `
      if [ -d ${config.releaseDir} ];
      then mv ${config.releaseDir} ${config.releaseDir}_${getCurrentTime()}
      fi
      `,
      config.deployDir)
  } else {
    console.log('提醒:未开启远端备份!')
    await runCommand(
      ssh,
      `
      if [ -d ${config.releaseDir} ];
      then mv ${config.releaseDir} /tmp/${config.releaseDir}_${getCurrentTime()}
      fi
      `,
      config.deployDir)
  }
}

module.exports = uploadFile

3.5 Обработка времени

getCurrentTimeПолучить и вернуть текущее время, которое используется для удаленного удаленного резервного копирования.

handleTime.jsКод ссылки

// 获取当前时间
function getCurrentTime () {
  const date = new Date
  const yyyy = date.getFullYear()
  const MM = coverEachUnit(date.getMonth() + 1)
  const dd = coverEachUnit(date.getDate())
  const HH = coverEachUnit(date.getHours())
  const mm = coverEachUnit(date.getMinutes())
  const ss = coverEachUnit(date.getSeconds())
  return `${yyyy}-${MM}-${dd}_${HH}:${mm}:${ss}`
}

// 转换时间中一位至两位
function coverEachUnit (val) {
  return val < 10 ? '0' + val : val
}

module.exports = getCurrentTime

3.6 Основная программа

Когда все функциональные модули инкапсулированы,异步流程оба используютPromiseобработать, а затем объединитьasync awiatРеализация не только обеспечивает порядок реализации функций, но и делает комбинацию функций более лаконичной и элегантной.

mainСовмещение функций таможенного оформления в функции реализует процесс автоматического развертывания, а остальные функции добавляются позже.主程序Введите и объедините, чтобы завершить обновление.

app.jsКод ссылки

const config = require ('./config')
const helper = require ('./utils/helper')
const compressFile = require ('./utils/compressFile')
const sshServer = require ('./utils/ssh')
const uploadFile = require ('./utils/uploadFile')
const runCommand = require ('./utils/handleCommand')

// 主程序(可单独执行)
async function main () {
  try {
    console.log('请确保文件解压后为dist目录!!!')
    const SELECT_CONFIG = (await helper(config)).value // 所选部署项目的配置信息
    console.log('您选择了部署 ' + SELECT_CONFIG.name)
    const localFile =  __dirname + '/' + SELECT_CONFIG.targetFile // 待上传本地文件
    SELECT_CONFIG.openCompress ? await compressFile(SELECT_CONFIG.targetDir, localFile) : '' //压缩
    await sshServer.connectServe(SELECT_CONFIG.ssh) // 连接
    await uploadFile(sshServer.ssh, SELECT_CONFIG, localFile) // 上传
    await runCommand(sshServer.ssh, 'unzip ' + SELECT_CONFIG.targetFile, SELECT_CONFIG.deployDir) // 解压
    await runCommand(sshServer.ssh, 'mv dist ' + SELECT_CONFIG.releaseDir, SELECT_CONFIG.deployDir) // 修改文件名称
    await runCommand(sshServer.ssh, 'rm -f ' + SELECT_CONFIG.targetFile, SELECT_CONFIG.deployDir) // 删除
  } catch (err) {
    console.log('部署过程出现错误!', err)
  } finally {
    process.exit()
  }
}

// run main
main()

3.7 Файлы конфигурации

Для облегчения автоматического внешнего развертывания здесь извлекается ключевая информация и создается файл конфигурации. Пользователям нужно только изменить файл конфигурации для достижения自动化部署.

config.jsКод ссылки

/*
config.js
说明:
  请确保解压后的文件目录为dist
  ssh: 连接服务器用户信息
  targetDir: 需要压缩的文件目录(启用本地压缩后生效)
  targetFile: 指定上传文件名称(config.js同级目录)
  openCompress: 关闭后,将跳过本地文件压缩,直接上传同级目录下指定文件
  openBackUp: 开启后,若远端存在相同目录,则会修改原始目录名称,不会直接覆盖
  deployDir: 指定远端部署地址
  releaseDir: 指定远端部署地址下的发布目录名称
更新:
  🎉现已支持添加多个配置信息,自动化部署时支持选择配置信息运行
  🎉现已支修改服务器连接端口,支持ssh私钥及解密密码连接(ps:不使用此方法时,请注释privateKey)
  🎉现已更新模块引用逻辑,远端备份时间格式改为 `yyyy-MM-dd_HH:mm:ss`
  */

const config = [
  {
    name: '项目A-dev',
    ssh: {
      host: '192.168.0.110',
      port: 22,
      username: 'root',
      password: 'root',
      // privateKey: 'E:/id_rsa', // ssh私钥(不使用此方法时请勿填写, 注释即可)
      passphrase: '123456' // ssh私钥对应解密密码(不存在设为''即可)
    },
    targetDir: 'E:/private/my-vue-cli/dist', // 目标压缩目录(可使用相对地址)
    targetFile: 'dist.zip', // 目标文件
    openCompress: true, // 是否开启本地压缩
    openBackUp: true, // 是否开启远端备份
    deployDir: '/home/node_test' + '/', // 远端目录
    releaseDir: 'web' // 发布目录
  },
  {
    name: '项目A-prod',
    ssh: {
      host: '192.168.0.110',
      port: 22,
      username: 'root',
      password: 'root',
      privateKey: 'E:/id_rsa', // ssh私钥(不使用此方法时请勿填写, 注释即可)
      passphrase: '123456' // ssh私钥对应解密密码(不存在设为''即可)
    },
    targetDir: 'E:/private/my-vue-cli/dist', // 目标压缩目录(可使用相对地址)
    targetFile: 'dist.zip', // 目标文件
    openCompress: true, // 是否开启本地压缩
    openBackUp: true, // 是否开启远端备份
    deployDir: '/home/node_test' + '/', // 远端目录
    releaseDir: 'web2' // 发布目录
  }
]

module.exports = config

использовать

Извлеките исходный код, установите зависимости, измените файл конфигурации и запустите его.

npm install
npm run deploy

наконец

🎉Исходный код проекта открыт дляgithubДобро пожаловать на загрузку и использование, в будущем будет улучшено больше функций 🎉Исходный код и описание проекта

Наконечник:Не забудьте, если вам нравитсяstarО 😘, если есть вопросы 🧐 добро пожаловатьprа такжеissuesи активно общаться.

Другие статьи:

React Hook реализует веб-сайт онлайн-обоев с нуля