如何为 Vue 项目配置 CI/CD 流水线,包括自动化测试、代码质量检查和多环境部署?

各位观众老爷,晚上好!我是今晚的讲师,准备好迎接一场 Vue 项目 CI/CD 的饕餮盛宴了吗?今天咱们不搞虚的,直接上干货,手把手教你打造一条龙的自动化流水线,让你的 Vue 项目从此告别手动部署的苦逼日子,拥抱丝滑流畅的 CI/CD 体验!

第一部分:工欲善其事,必先利其器——工具链选择

要搭建 CI/CD 流水线,首先得选好趁手的兵器。别跟我说你还打算用记事本写代码然后手动 FTP 上传,那是远古时代的故事了!

  • 版本控制系统:Git (这是必须的,没得选)
  • CI/CD 平台:

    • GitHub Actions: 如果你的代码托管在 GitHub 上,强烈推荐,免费额度够用,而且集成方便。
    • GitLab CI/CD: GitLab 自带 CI/CD,功能强大,可以自建,也可以用 SaaS 版本。
    • Jenkins: 老牌 CI/CD 工具,灵活可定制,但配置相对复杂。
    • CircleCI: 云原生 CI/CD,上手简单,但收费较高。

    咱们今天主要讲 GitHub Actions,因为它用的人多,免费,而且足够满足大部分 Vue 项目的需求。

  • 包管理器:npm/yarn/pnpm (根据你的项目选择,推荐 pnpm,速度快,节省空间)
  • 测试框架:Jest/Mocha/Vitest (根据你的喜好和项目需求选择,推荐 Jest,生态完善)
  • 代码质量检查工具:ESLint/Prettier/Stylelint (保证代码风格一致,减少 bug)
  • 部署工具:

    • 服务器: 各种云服务器 (AWS, Azure, Google Cloud, 阿里云, 腾讯云),或者自己的服务器。
    • 对象存储: 如果是静态网站,可以部署到对象存储 (AWS S3, 阿里云 OSS, 腾讯云 COS)。
    • Vercel/Netlify: 专门为前端项目打造的部署平台,免费额度较高,上手简单。

    咱们今天以部署到服务器为例,演示整个流程。

第二部分:GitHub Actions 入门——YAML 语法和基本概念

GitHub Actions 的核心就是 YAML 文件,它定义了你的 CI/CD 流水线。咱们先来了解一下 YAML 的基本语法和 GitHub Actions 的基本概念。

  • YAML 语法: YAML 是一种人类可读的数据序列化格式。它使用缩进表示层级关系,使用 - 表示列表项。

    name: My Workflow  # 工作流的名称
    
    on:  # 触发工作流的事件
      push:
        branches: [main]
    
    jobs:  # 定义任务
      build:  # 任务名称
        runs-on: ubuntu-latest  # 运行环境
    
        steps:  # 定义步骤
          - name: Checkout code  # 步骤名称
            uses: actions/checkout@v3  # 使用的 Action
    
          - name: Set up Node.js
            uses: actions/setup-node@v3
            with:
              node-version: 16
    
          - name: Install dependencies
            run: npm install
    
          - name: Build project
            run: npm run build
  • GitHub Actions 基本概念:

    • Workflow (工作流): 一个自动化的流程,由一个或多个 jobs 组成。
    • Job (任务): 一个包含多个 steps 的执行单元。
    • Step (步骤): 一个具体的执行动作,可以是运行命令,也可以是使用 Action。
    • Action (动作): 可重用的代码块,封装了常用的操作,比如 checkout 代码,安装依赖等。
    • Runner (运行器): 运行 workflow 的服务器,可以是 GitHub 提供的,也可以是自己搭建的。

第三部分:配置 Vue 项目的 CI/CD 流水线

咱们现在开始配置 Vue 项目的 CI/CD 流水线。假设你的项目已经托管在 GitHub 上,并且使用了 npm 作为包管理器。

  1. 创建 .github/workflows 目录: 在你的项目根目录下创建 .github 目录,然后在 .github 目录下创建 workflows 目录。

  2. 创建 main.yml 文件:.github/workflows 目录下创建一个 main.yml 文件,这个文件就是你的 CI/CD 流程的定义。

  3. 编写 main.yml 文件:

    name: Vue CI/CD  # 工作流的名称
    
    on:  # 触发工作流的事件
      push:
        branches: [main]  # 当 push 到 main 分支时触发
      pull_request:
        branches: [main]  # 当有 pull request 到 main 分支时触发
    
    jobs:  # 定义任务
      build:  # 任务名称
        runs-on: ubuntu-latest  # 运行环境
    
        steps:  # 定义步骤
          - name: Checkout code  # 步骤名称
            uses: actions/checkout@v3  # 使用的 Action
    
          - name: Set up Node.js
            uses: actions/setup-node@v3
            with:
              node-version: 16  # 使用 Node.js 16
    
          - name: Install dependencies
            run: npm install  # 安装依赖
    
          - name: Run tests
            run: npm run test:unit  # 运行单元测试 (根据你的项目配置修改)
    
          - name: Run lint
            run: npm run lint  # 运行 ESLint (根据你的项目配置修改)
    
          - name: Build project
            run: npm run build  # 构建项目
    
          - name: Upload artifacts  # 上传构建产物
            uses: actions/upload-artifact@v3
            with:
              name: dist  # 产物名称
              path: dist  # 产物路径
    
      deploy:  # 部署任务
        needs: build  # 依赖 build 任务
        runs-on: ubuntu-latest  # 运行环境
        if: github.ref == 'refs/heads/main'  # 只在 main 分支上部署
    
        steps:
          - name: Download artifacts  # 下载构建产物
            uses: actions/download-artifact@v3
            with:
              name: dist  # 产物名称
    
          - name: Deploy to server  # 部署到服务器
            uses: appleboy/[email protected]  # 使用 SSH Action
            with:
              host: ${{ secrets.SERVER_HOST }}  # 服务器地址 (从 secrets 中获取)
              username: ${{ secrets.SERVER_USER }}  # 服务器用户名 (从 secrets 中获取)
              key: ${{ secrets.SERVER_PRIVATE_KEY }}  # 服务器私钥 (从 secrets 中获取)
              port: 22  # SSH 端口
              script: |
                cd /var/www/your-project  # 进入项目目录
                rm -rf *  # 删除所有文件
                cp -r dist/* .  # 复制构建产物到项目目录
                # 如果你的项目需要重启服务,可以在这里添加重启命令
                # 例如:sudo systemctl restart your-service
  4. 配置 Secrets: 在 GitHub 仓库的 Settings -> Secrets -> Actions 中添加以下 Secrets:

    • SERVER_HOST: 你的服务器地址。
    • SERVER_USER: 你的服务器用户名。
    • SERVER_PRIVATE_KEY: 你的服务器私钥。 注意:一定不要把私钥直接写在 YAML 文件中,否则会被泄露!
  5. 提交代码:.github/workflows/main.yml 文件提交到 GitHub 仓库。

  6. 观察流水线运行: push 代码到 main 分支,或者创建一个 pull request 到 main 分支,GitHub Actions 就会自动运行你的 CI/CD 流水线。你可以在 GitHub 仓库的 Actions 选项卡中查看流水线的运行状态。

代码解释:

  • name: Vue CI/CD: 定义工作流的名称。
  • on:: 定义触发工作流的事件。这里配置了当 push 到 main 分支或者有 pull request 到 main 分支时触发。
  • jobs:: 定义任务。这里定义了两个任务:builddeploy
  • build:: 构建任务,负责 checkout 代码,安装依赖,运行测试和构建项目。
  • deploy:: 部署任务,负责下载构建产物,然后通过 SSH 连接到服务器,将构建产物复制到服务器上。
  • needs: build: 表示 deploy 任务依赖 build 任务,只有 build 任务成功完成后,deploy 任务才会执行。
  • if: github.ref == 'refs/heads/main': 表示只有当分支是 main 分支时,deploy 任务才会执行。
  • uses: actions/checkout@v3: 使用 actions/checkout@v3 这个 Action 来 checkout 代码。
  • uses: actions/setup-node@v3: 使用 actions/setup-node@v3 这个 Action 来设置 Node.js 环境。
  • uses: actions/upload-artifact@v3: 使用 actions/upload-artifact@v3 这个 Action 来上传构建产物。
  • uses: actions/download-artifact@v3: 使用 actions/download-artifact@v3 这个 Action 来下载构建产物。
  • uses: appleboy/[email protected]: 使用 appleboy/[email protected] 这个 Action 来通过 SSH 连接到服务器。
  • secrets.SERVER_HOST: 从 GitHub 仓库的 Secrets 中获取服务器地址。

第四部分:代码质量检查——ESLint、Prettier 和 Stylelint

代码质量是保证项目稳定性和可维护性的重要因素。咱们可以使用 ESLint、Prettier 和 Stylelint 来进行代码质量检查。

  • ESLint: 用于检查 JavaScript 代码的语法和风格。
  • Prettier: 用于格式化代码,使其符合统一的风格。
  • Stylelint: 用于检查 CSS 代码的语法和风格。
  1. 安装依赖:

    npm install eslint prettier eslint-plugin-vue eslint-config-prettier stylelint stylelint-config-standard stylelint-config-prettier stylelint-order -D
  2. 配置 ESLint: 创建 .eslintrc.js 文件:

    module.exports = {
      root: true,
      env: {
        node: true,
      },
      extends: [
        'plugin:vue/vue3-essential',
        'eslint:recommended',
        '@vue/eslint-config-prettier',
      ],
      parserOptions: {
        ecmaVersion: 2020,
      },
      rules: {
        'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
        'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
        'vue/multi-word-component-names': 'off', // 关闭多单词组件名校验,根据你的需要调整
      },
    };
  3. 配置 Prettier: 创建 .prettierrc.js 文件:

    module.exports = {
      semi: false,
      singleQuote: true,
      trailingComma: 'all',
      printWidth: 100,
      tabWidth: 2,
    };
  4. 配置 Stylelint: 创建 .stylelintrc.js 文件:

    module.exports = {
      extends: [
        'stylelint-config-standard',
        'stylelint-config-prettier',
        'stylelint-order',
      ],
      rules: {
        'order/properties-alphabetical-order': null, // 关闭属性字母顺序校验,根据你的需要调整
      },
    };
  5. 配置 npm scripts:package.json 文件中添加以下 scripts:

    "scripts": {
      "lint": "eslint --ext .vue,.js,.jsx,.ts,.tsx src/",
      "lint:fix": "eslint --fix --ext .vue,.js,.jsx,.ts,.tsx src/",
      "format": "prettier --write src/",
      "stylelint": "stylelint src/**/*.{vue,css,scss}",
      "stylelint:fix": "stylelint --fix src/**/*.{vue,css,scss}"
    }
  6. 修改 main.yml 文件:Run lint 步骤修改为:

    - name: Run lint
      run: npm run lint

现在,每次提交代码,GitHub Actions 都会自动运行 ESLint、Prettier 和 Stylelint,检查代码质量。如果代码质量检查失败,流水线就会停止,防止不符合规范的代码被部署到服务器上。

第五部分:多环境部署——区分开发、测试和生产环境

在实际项目中,通常需要区分开发、测试和生产环境。咱们可以使用不同的分支和不同的配置来支持多环境部署。

  1. 创建不同的分支: 创建 develop (开发) 和 staging (测试) 分支。

  2. 配置不同的 Secrets: 为不同的环境配置不同的 Secrets。例如:

    • SERVER_HOST_DEV: 开发环境服务器地址。

    • SERVER_USER_DEV: 开发环境服务器用户名。

    • SERVER_PRIVATE_KEY_DEV: 开发环境服务器私钥。

    • SERVER_HOST_STAGING: 测试环境服务器地址。

    • SERVER_USER_STAGING: 测试环境服务器用户名。

    • SERVER_PRIVATE_KEY_STAGING: 测试环境服务器私钥。

    • SERVER_HOST_PROD: 生产环境服务器地址。

    • SERVER_USER_PROD: 生产环境服务器用户名。

    • SERVER_PRIVATE_KEY_PROD: 生产环境服务器私钥。

  3. 修改 main.yml 文件:

    
    name: Vue CI/CD
    
    on:
      push:
        branches:
          - main
          - develop
          - staging
      pull_request:
        branches:
          - main
          - develop
          - staging
    
    jobs:
      build:
        runs-on: ubuntu-latest
    
        steps:
          - name: Checkout code
            uses: actions/checkout@v3
    
          - name: Set up Node.js
            uses: actions/setup-node@v3
            with:
              node-version: 16
    
          - name: Install dependencies
            run: npm install
    
          - name: Run tests
            run: npm run test:unit
    
          - name: Run lint
            run: npm run lint
    
          - name: Build project
            run: npm run build
    
          - name: Upload artifacts
            uses: actions/upload-artifact@v3
            with:
              name: dist
              path: dist
    
      deploy:
        needs: build
        runs-on: ubuntu-latest
        strategy:
          matrix:
            environment: [dev, staging, prod]
            # 区分不同的环境
            include:
              - environment: dev
                branch: develop
                SERVER_HOST: ${{ secrets.SERVER_HOST_DEV }}
                SERVER_USER: ${{ secrets.SERVER_USER_DEV }}
                SERVER_PRIVATE_KEY: ${{ secrets.SERVER_PRIVATE_KEY_DEV }}
              - environment: staging
                branch: staging
                SERVER_HOST: ${{ secrets.SERVER_HOST_STAGING }}
                SERVER_USER: ${{ secrets.SERVER_USER_STAGING }}
                SERVER_PRIVATE_KEY: ${{ secrets.SERVER_PRIVATE_KEY_STAGING }}
              - environment: prod
                branch: main
                SERVER_HOST: ${{ secrets.SERVER_HOST_PROD }}
                SERVER_USER: ${{ secrets.SERVER_USER_PROD }}
                SERVER_PRIVATE_KEY: ${{ secrets.SERVER_PRIVATE_KEY_PROD }}
    
        if: github.ref == format('refs/heads/{0}', matrix.branch)
    
        steps:
          - name: Download artifacts
            uses: actions/download-artifact@v3
            with:
              name: dist
    
          - name: Deploy to server
            uses: appleboy/[email protected]
            with:
              host: ${{ matrix.SERVER_HOST }}
              username: ${{ matrix.SERVER_USER }}
              key: ${{ matrix.SERVER_PRIVATE_KEY }}
              port: 22
              script: |
                echo "Deploying to ${{ matrix.environment }}"
                cd /var/www/your-project-${{ matrix.environment }}  # 区分不同环境的项目目录
                rm -rf *
                cp -r dist/* .
                # 根据环境执行不同的重启命令
                # 例如:
                # if [ "${{ matrix.environment }}" == "prod" ]; then
                #   sudo systemctl restart your-service
                # fi

代码解释:

  • strategy: matrix: 使用矩阵策略来并行执行多个部署任务,每个任务对应一个环境。
  • include: 定义了不同环境的配置,包括分支名称、服务器地址、用户名和私钥。
  • if: github.ref == format('refs/heads/{0}', matrix.branch): 只有当分支名称与当前环境配置的分支名称匹配时,才执行部署任务。
  • matrix.environment: 当前环境的名称。
  • matrix.SERVER_HOST: 当前环境的服务器地址。

现在,当你 push 代码到 develop 分支时,GitHub Actions 会自动将代码部署到开发环境服务器上。当你 push 代码到 staging 分支时,GitHub Actions 会自动将代码部署到测试环境服务器上。当你 push 代码到 main 分支时,GitHub Actions 会自动将代码部署到生产环境服务器上。

第六部分:自动化测试——单元测试、集成测试和 E2E 测试

自动化测试是保证项目质量的关键。咱们可以使用 Jest 进行单元测试, Cypress 进行 E2E 测试。

  1. 安装 Jest:

    npm install jest @vue/test-utils -D
  2. 配置 Jest: 创建 jest.config.js 文件:

    module.exports = {
      preset: '@vue/cli-plugin-unit-jest',
    };
  3. 编写单元测试:src/components 目录下创建一个 HelloWorld.spec.js 文件:

    import { shallowMount } from '@vue/test-utils'
    import HelloWorld from '@/components/HelloWorld.vue'
    
    describe('HelloWorld.vue', () => {
      it('renders props.msg when passed', () => {
        const msg = 'new message'
        const wrapper = shallowMount(HelloWorld, {
          props: { msg }
        })
        expect(wrapper.text()).toMatch(msg)
      })
    })
  4. 安装 Cypress:

    npm install cypress -D
  5. 编写 E2E 测试:cypress/integration 目录下创建一个 example.spec.js 文件:

    describe('My First Test', () => {
      it('Visits the app root url', () => {
        cy.visit('/')
        cy.contains('h1', 'You did it!')
      })
    })
  6. 配置 npm scripts:package.json 文件中添加以下 scripts:

    "scripts": {
      "test:unit": "vue-cli-service test:unit",
      "test:e2e": "cypress run"
    }
  7. 修改 main.yml 文件:

    name: Vue CI/CD
    
    on:
      push:
        branches:
          - main
          - develop
          - staging
      pull_request:
        branches:
          - main
          - develop
          - staging
    
    jobs:
      build:
        runs-on: ubuntu-latest
    
        steps:
          - name: Checkout code
            uses: actions/checkout@v3
    
          - name: Set up Node.js
            uses: actions/setup-node@v3
            with:
              node-version: 16
    
          - name: Install dependencies
            run: npm install
    
          - name: Run tests (unit)
            run: npm run test:unit
    
          - name: Run tests (e2e)
            run: npm run test:e2e  # 添加 E2E 测试
    
          - name: Run lint
            run: npm run lint
    
          - name: Build project
            run: npm run build
    
          - name: Upload artifacts
            uses: actions/upload-artifact@v3
            with:
              name: dist
              path: dist
    
      deploy:
        needs: build
        runs-on: ubuntu-latest
        strategy:
          matrix:
            environment: [dev, staging, prod]
            include:
              - environment: dev
                branch: develop
                SERVER_HOST: ${{ secrets.SERVER_HOST_DEV }}
                SERVER_USER: ${{ secrets.SERVER_USER_DEV }}
                SERVER_PRIVATE_KEY: ${{ secrets.SERVER_PRIVATE_KEY_DEV }}
              - environment: staging
                branch: staging
                SERVER_HOST: ${{ secrets.SERVER_HOST_STAGING }}
                SERVER_USER: ${{ secrets.SERVER_USER_STAGING }}
                SERVER_PRIVATE_KEY: ${{ secrets.SERVER_PRIVATE_KEY_STAGING }}
              - environment: prod
                branch: main
                SERVER_HOST: ${{ secrets.SERVER_HOST_PROD }}
                SERVER_USER: ${{ secrets.SERVER_USER_PROD }}
                SERVER_PRIVATE_KEY: ${{ secrets.SERVER_PRIVATE_KEY_PROD }}
    
        if: github.ref == format('refs/heads/{0}', matrix.branch)
    
        steps:
          - name: Download artifacts
            uses: actions/download-artifact@v3
            with:
              name: dist
    
          - name: Deploy to server
            uses: appleboy/[email protected]
            with:
              host: ${{ matrix.SERVER_HOST }}
              username: ${{ matrix.SERVER_USER }}
              key: ${{ matrix.SERVER_PRIVATE_KEY }}
              port: 22
              script: |
                echo "Deploying to ${{ matrix.environment }}"
                cd /var/www/your-project-${{ matrix.environment }}
                rm -rf *
                cp -r dist/* .

现在,每次提交代码,GitHub Actions 都会自动运行单元测试和 E2E 测试,确保代码质量。

第七部分:总结与展望

今天咱们一起打造了一条 Vue 项目的 CI/CD 流水线,包括自动化测试、代码质量检查和多环境部署。虽然配置过程有点繁琐,但是一旦配置完成,就可以大大提高开发效率,保证项目质量。

当然,CI/CD 还有很多高级用法,比如:

  • 代码覆盖率: 使用 Istanbul 或 NYC 来生成代码覆盖率报告。
  • 性能测试: 使用 Lighthouse 或 WebPageTest 来进行性能测试。
  • 安全扫描: 使用 Snyk 或 SonarQube 来进行安全扫描。
  • 自动回滚: 当部署失败时,自动回滚到上一个版本。

希望今天的讲座能对你有所帮助。记住,CI/CD 不是一蹴而就的,需要不断学习和实践,才能打造出最适合你的流水线!下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注