CI_CD

什么是CI/CD

在前端开发中,CI/CD 是 Continuous Integration (持续集成)和 Continuous Deployment/Continuous Delivery(持续部署/持续交付)的简称。它是一种软件开发实践,自动化了应用的构建、测试和发布过程。

CI/CD的作用

  1. 持续集成(CI):当开发者将代码推送到版本控制系统(如 GitHub、GitLab)时,CI 工具会自动构建和测试应用代码,以确保代码没有引入新的问题。前端代码在此过程中会进行:
  • 自动化单元测试和集成测试
  • 代码质量检查(如 ESLint)
  • 编译和构建打包(例如 Webpack 或 Vite)
  1. 持续部署/持续交付(CD)
  • 持续交付:构建和测试通过后,代码自动发布到一个准备好的环境中(如 staging 环境)进行手动审核,之后再发布到生产环境。
  • 持续部署:代码通过测试后,自动部署到生产环境,使更新可以快速上线。对于前端,这通常会将最新的代码发布到 Web 服务器或 CDN。

实现CI/CD

1.1 编写配置文件

javascript 复制代码
// 配置
const config = [
    {
        name: '项目1',
        value: '项目1',
        ssh: {
            host: 'xxxx',
            port: 'xxxx',
            username: 'xxxx',
            password: 'xxxx',
            passphrase: 'xxxx'
        },
        targetDir: 'G:/桌面/code/xmzs/cicd-project/dist',
        targetFile: 'dist.zip',
        deployDir: '/www/wwwroot/',
        releaseDir: 'web',
        buildCommand: 'pnpm build'
    }
]

export default config

配置文件为什么是一个数组呢?

因为可以支持多个项目上传不同的服务器

1.2 编写程序入口

javascript 复制代码
function main() {}

main()

1.3 读取配置文件的内容

有了入口之后,我们只需要使用node 启动就可以完成程序的运行。有了配置文件之后,我们需要读取其中的配置完成后续的动作,那么,我们要怎么读取配置文件的内容呢?。我们可以采用命令行的方式来读取。因此,我们需要安装inquirer这个库。

javascript 复制代码
// 命令行交互工具
import inquirer from "inquirer";
import config from "../config.js";

async function commanderLine () {
    const res = await inquirer.prompt([
            {
                type: 'list',
                message: '请选择项目',
                name: 'project',
                choices: config
            }
        ])
        return config.find(item => item.value === res.project)
}

export default commanderLine

通过以上函数,可以实现一下效果:

1.4 压缩

那么,我们可以根据命令行获取的路径参数对需要上传到服务器的文件夹进行压缩,需要用到archiver这个库来对目录进行压缩。

javascript 复制代码
// 编写压缩文件的代码
import archiver from 'archiver'
import fs from 'fs'
/**
 * 
 * @param {*} targetDir 压缩的目录的位置
 * @param {*} localFile 压缩之后压缩包存放的位置
 */
function compressFile (targetDir, localFile) {
    return new Promise(resolve => {
        // 创建可写流
        const output = fs.createWriteStream(localFile)
        const archive = archiver('zip', {
            zlib: { level: 9 }
        })
        archive.pipe(output)
        archive.directory(targetDir, 'dist')
        archive.finalize()

        archive.on('close', () => {
            console.log((archive.pointer() / 1024 / 1024).toFixed(2), 'MB')
            resolve()
        })
    })
}

export default compressFile

1.5 使用ssh连接远程服务器

为了连接远程服务器,我们可以使用node-ssh这个库来完成

javascript 复制代码
// 连接ssh服务
import * as ssh from 'node-ssh'

const sshClient = new ssh.NodeSSH()

function sshConnect (sshConfig) {
    return new Promise(resolve => {
        sshClient.connect(sshConfig).then(res => {
            console.log('connect success')
            resolve(res)
        })
    })
}

export default {
    sshConnect,
    ssh: sshClient
}
javascript 复制代码
import service from './src/ssh.js'
async main() {
  ...
  await service.sshConnect(options.ssh)
}

1.6 上传文件

javascript 复制代码
// 上传远端服务器的代码
// local表示需要上传的目录
// config.deployDir + config.releaseDir表示上传到服务器的目录的路径
function uploadFile (ssh, config, local) {
    return new Promise(resolve => {
        ssh.putFile(local, config.deployDir + config.releaseDir).then(() => {
            console.log('upload success')
            resolve()
        }).catch(err => {
            console.log(err)
        })
    })
}

export default uploadFile

通过以上步骤,已经基本实现文件的上传,但是目前我们上传的是一个压缩包,因此需要对他进行解压,还有就是现在只能上传一次,因为服务器不允许同名文件出现,因此我们第二次上传时需要将上一次上传的给删掉。但是我们要怎么删除呢?

  1. 可以使用linux命令
  2. node-ssh提供了执行linux命令的方法
javascript 复制代码
// 操作ssh命令的文件
function runCommander (ssh, command, path) {
    return new Promise(resolve => {
        ssh.execCommand(command, { cwd: path }).then((result) => {
            resolve()
        })
    })
}

export default runCommander

通过以上方法,我们可以完成对linux命令的执行

  1. rm -rf 完成对原先文件的删除
  2. unzip 完成对压缩包的解压
  3. mv 完成对文件的重命名

1.7 执行对于需要上传的文件的打包

当我们对项目进行修改时,服务器上的内容并没有更新,因为我们并没有对其进行重新打包,因此我们还需要在上传文件到服务器之前对项目进行重新打包。那么,要如何在执行main 脚本的同时执行项目的打包脚本呢?我们可以使用node 的原生模块child_process子进程。

javascript 复制代码
import { execSync } from 'child_process'
function runBuild (path, command) {
    return new Promise(resolve => {
        execSync(command, {
            cwd: path,
            stdio: 'inherit'
        })
        resolve()
    })
}

export default runBuild

通过以上函数,我们可以完成对项目的打包。

1.8 最终的启动文件

javascript 复制代码
import commanderLine from './src/helper.js'
import compressFile from './src/compressFile.js'
import service from './src/ssh.js'
import uploadFile from './src/uploadFile.js'
import runCommander from './src/handleCommand.js'
import path from 'path'
import runBuild from './src/build.js'
async function main () {
    const options = await commanderLine()
    const local = path.join(process.cwd(), options.targetFile)
    await runBuild(options.targetDir, options.buildCommand)
    await compressFile(options.targetDir, local)
    await service.sshConnect(options.ssh)
    await runCommander(service.ssh, `rm -rf ${options.releaseDir}`, options.deployDir)
    await uploadFile(service.ssh, options, local)
    await runCommander(service.ssh, `unzip ${options.releaseDir}`, options.deployDir)
    await runCommander(service.ssh, `rm -rf ${options.releaseDir}`, options.deployDir)
    await runCommander(service.ssh, `mv dist ${options.releaseDir}`, options.deployDir)
    service.ssh.dispose() // 断开ssh
}
main()

最终,我们只需要执行node app.js就能完成对项目的打包以及部署到服务器了。

相关推荐
安静读书3 小时前
持续集成与持续部署:CI/CD简介
运维·ci/cd
安静读书1 天前
持续集成与持续部署:CI/CD实现教程
ci/cd
AliCloudROS3 天前
2分钟在阿里云ECS控制台部署个人应用(图文示例)
阿里云·ci/cd·持续部署
天草二十六_简村人3 天前
jenkins用户在执行scp的时候如何做免密登录
运维·ci/cd·node.js·jenkins·php·devops
Lalolander3 天前
通过华为鲲鹏认证发行上市的集成平台产品推荐
大数据·人工智能·科技·安全·ci/cd·华为·持续集成
golitter.4 天前
CI/CD认识
运维·ci/cd
莫尔道嘎老范4 天前
vue+vite前端项目ci过程中遇到的问题
前端·vue.js·ci/cd
小技与小术5 天前
CICD持续集成与持续交付
ci/cd·云原生·kubernetes
杨哥带你写代码5 天前
Spring Boot编程训练系统:敏捷开发与持续集成
spring boot·ci/cd·敏捷流程
qq_4337169510 天前
编写第一个 Appium 测试脚本:从安装到运行!
自动化测试·软件测试·jmeter·ci/cd·职场和发展·appium·jenkins