Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_NaiveVue/zh/deploy/ci-cd.md
T
2026-10-08 17:50:29 +08:00

345 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 11.11 CI/CD 流水线
:::tip 概述
本节介绍项目的持续集成/持续部署(CI/CD)方案,包括 GitHub Actions 和 GitLab CI 的配置示例,以及自动化测试、构建和部署流程。
:::
:::info 模板说明
本项目仓库当前未内置 `.github/workflows` 或 `.gitee/workflows`,以下配置为**通用模板**:按实际代码托管平台(GitHub / Gitee / GitLab)调整后放入对应目录即可使用。
:::
## 流水线架构
```text
代码提交 → 代码检查 → 单元测试 → 构建打包 → 部署上线
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
Git Push Pint PHPUnit Docker 生产环境
ESLint (sqlite) Image
```
## GitHub Actions
### 后端 CI 配置
```yaml
# =============================================================================
# .github/workflows/backend-ci.yml — 后端持续集成
# =============================================================================
# 触发条件:push 到 main/develop 分支,或向 main 发起 PR
# 执行环境:Ubuntu 最新版
# 流程:代码风格检查 → 单元测试 → 覆盖率上传
# 说明:phpunit.xml 已配置 sqlite :memory: 测试库,无需外部数据库服务
# =============================================================================
name: Backend CI
on:
push:
branches: [main, develop] # push 到这些分支时触发
pull_request:
branches: [main] # 向 main 发起 PR 时触发
jobs:
test:
runs-on: ubuntu-latest # 使用 GitHub 托管的最新 Ubuntu 运行器
steps:
# 拉取代码(checkout@v4 是 GitHub 官方 Action)
- uses: actions/checkout@v4
# 安装 PHP 8.3 及所需扩展
- name: Setup PHP
uses: shivammathur/setup-php@v2 # 第三方 Action,自动安装 PHP
with:
php-version: '8.3' # PHP 版本(Laravel 13 要求)
extensions: pdo_mysql, mbstring, gd, bcmath, zip, redis
coverage: xdebug # 代码覆盖率工具
# 安装 Composer 依赖(--no-progress 隐藏进度条,--prefer-dist 下载压缩包更快)
- name: Install Dependencies
run: composer install --no-progress --prefer-dist
# 准备运行环境
- name: Prepare Environment
run: |
cp .env.example .env # 复制环境配置模板
php artisan key:generate # 生成应用密钥
# 代码风格检查(Laravel Pint,PSR-12 + Laravel 风格)
- name: Code Style Check
run: vendor/bin/pint --test
# 运行单元测试(composer test = config:clear + artisan test,
# phpunit.xml 使用 sqlite :memory:,不依赖 MySQL)
- name: Run Tests
run: composer test -- --coverage-text
# 上传覆盖率报告到 Codecov(免费的覆盖率追踪平台)
- name: Upload Coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml # 覆盖率报告文件
```
:::tip 无外部依赖的测试
`phpunit.xml` 将 `DB_CONNECTION` 设为 `sqlite`、`DB_DATABASE` 设为 `:memory:`,单元测试在内存库中运行,CI 无需启动 MySQL 容器。如需对真实 MySQL 做集成测试,再参考下文 GitLab CI 的 services 配置。
:::
### 前端 CI 配置
```yaml
# =============================================================================
# .github/workflows/frontend-ci.yml — 前端持续集成
# =============================================================================
# 触发条件:仅当 ui/ 目录下的文件变更时触发(避免后端改动触发前端 CI)
# 流程:Lint → 类型检查 → 构建
# =============================================================================
name: Frontend CI
on:
push:
branches: [main, develop]
paths:
- 'ui/**' # 仅 ui/ 目录变更时触发
pull_request:
branches: [main]
paths:
- 'ui/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 安装 Node.js 22 并配置 pnpm 缓存
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22' # Node.js 版本(22 LTS;根目录 Vite 7 要求 20.19+/22.12+)
cache: 'pnpm' # 启用 pnpm 缓存(加速依赖安装)
cache-dependency-path: ui/pnpm-lock.yaml # 缓存依赖的 lockfile 路径
# 全局安装 pnpm 包管理器
- name: Install pnpm
run: npm install -g pnpm
# 安装前端依赖(--frozen-lockfile 确保版本与 lockfile 一致)
- name: Install Dependencies
working-directory: ui # 在 ui/ 目录下执行
run: pnpm install --frozen-lockfile
# ESLint 代码风格检查(脚本自带 --fix,会自动修复可修复问题)
- name: Lint
working-directory: ui
run: pnpm lint:eslint
# TypeScript 类型检查(发现类型错误)
- name: Type Check
working-directory: ui
run: pnpm type:check
# 生产环境构建(验证构建是否成功)
- name: Build
working-directory: ui
run: pnpm build
```
:::info 前端暂无单元测试脚本
`ui/package.json` 未提供 `test` 脚本(如后续引入 Vitest,再补 `pnpm test` 步骤)。
:::
### 自动部署配置
```yaml
# =============================================================================
# .github/workflows/deploy.yml — 自动部署
# =============================================================================
# 触发条件:push 到 main 分支,或打 v* 标签
# 流程:构建 Docker 镜像 → 推送到镜像仓库 → SSH 部署到服务器
# =============================================================================
name: Deploy
on:
push:
branches: [main]
tags:
- 'v*' # 匹配 v1.0.0、v2.1.3 等标签
jobs:
deploy:
runs-on: ubuntu-latest
needs: [test] # 依赖 test job 通过后才执行
if: github.ref == 'refs/heads/main' # 仅 main 分支触发部署
steps:
- uses: actions/checkout@v4
# 构建 Docker 镜像(用 git commit SHA 作为版本标签)
# 项目自带 Dockerfile 与 docker-compose.yml(含 docker/.env.docker)
- name: Build Docker Image
run: |
docker build -t myapp:${{ github.sha }} . # 用 commit SHA 标记
docker tag myapp:${{ github.sha }} myapp:latest # 同时打 latest 标签
# 推送镜像到 Docker Hub(secrets 在 GitHub 仓库设置中配置)
- name: Push to Registry
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push myapp:${{ github.sha }} # 推送版本标签
docker push myapp:latest # 推送 latest 标签
# 通过 SSH 连接服务器执行部署命令
- name: Deploy to Server
uses: appleboy/ssh-action@v1 # 第三方 SSH Action
with:
host: ${{ secrets.SERVER_HOST }} # 服务器地址
username: ${{ secrets.SERVER_USER }} # SSH 用户名
key: ${{ secrets.SSH_KEY }} # SSH 私钥
script: |
cd /opt/myapp # 进入项目目录
docker compose --env-file docker/.env.docker pull # 拉取最新镜像
docker compose --env-file docker/.env.docker up -d --build
# 数据库结构变更:手动执行 document/mysql 下的 SQL 脚本(本项目无 migration)
```
## GitLab CI
```yaml
# =============================================================================
# .gitlab-ci.yml — GitLab CI/CD 配置
# =============================================================================
# 阶段:test → build → deploy
# 缓存:vendor/ 和 node_modules/ 按分支缓存,加速后续构建
# =============================================================================
stages:
- test # 测试阶段
- build # 构建阶段
- deploy # 部署阶段
# 全局变量(所有 job 共享)
variables:
MYSQL_ROOT_PASSWORD: root # 测试数据库密码(集成测试用)
MYSQL_DATABASE: test_db # 测试数据库名
# -----------------------------------------------------------------------------
# 后端测试
# -----------------------------------------------------------------------------
backend:test:
stage: test
image: php:8.3-cli # PHP 8.3 CLI 镜像
services:
- mysql:8.0 # MySQL 服务容器(集成测试用;单元测试走 sqlite 内存库)
script:
# 安装系统依赖(PHP 扩展编译需要)
- apt-get update && apt-get install -y libpq-dev libzip-dev
# 安装 PHP 扩展
- docker-php-ext-install pdo_mysql zip
- pecl install redis && docker-php-ext-enable redis
# 安装 Composer 依赖
- composer install --no-progress
# 准备环境并执行测试
- cp .env.example .env
- php artisan key:generate
- vendor/bin/pint --test # 代码风格检查
- composer test # 运行测试(phpunit.xml 用 sqlite :memory:)
cache:
key: ${CI_COMMIT_REF_SLUG} # 按分支名缓存
paths:
- vendor/ # 缓存 Composer 依赖
# -----------------------------------------------------------------------------
# 前端测试
# -----------------------------------------------------------------------------
frontend:test:
stage: test
image: node:22 # Node.js 22 镜像
script:
- cd ui
- npm install -g pnpm # 全局安装 pnpm
- pnpm install --frozen-lockfile # 安装依赖
- pnpm lint:eslint # 代码风格检查
- pnpm type:check # TypeScript 类型检查
- pnpm build # 生产构建
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- ui/node_modules/ # 缓存前端依赖
# -----------------------------------------------------------------------------
# 生产部署(仅 main 分支)
# -----------------------------------------------------------------------------
deploy:production:
stage: deploy
only:
- main # 仅 main 分支触发
script:
- docker build -t myapp:$CI_COMMIT_SHA . # 构建镜像
- docker push myapp:$CI_COMMIT_SHA # 推送到仓库
# SSH 到服务器执行部署
- ssh $SERVER_USER@$SERVER_HOST "cd /opt/myapp && docker compose --env-file docker/.env.docker up -d --build"
environment:
name: production
url: https://api.example.com
```
## 部署策略
### 蓝绿部署
```text
当前版本(蓝) → 新版本(绿)
│ │
▼ ▼
Nginx 切换流量 ← 验证通过
```
### 滚动更新
```bash
# Docker Swarm 滚动更新
# --update-parallelism 1 → 每次只更新 1 个容器实例
# --update-delay 30s → 每批更新间隔 30 秒(观察健康状态)
# --image myapp:v2.0 → 新版本镜像
# myapp_web → 服务名称
docker service update \
--update-parallelism 1 \
--update-delay 30s \
--image myapp:v2.0 \
myapp_web
```
## 通知集成
### 部署通知(钉钉/飞书)
```yaml
# GitHub Actions 部署成功通知(发送到钉钉机器人)
- name: Notify on Success
if: success() # 仅在部署成功时执行
run: |
# curl 发送 POST 请求到钉钉 Webhook
# -H → 设置请求头为 JSON 格式
# -d → 发送 JSON 消息体(Markdown 格式)
curl -X POST "${{ secrets.DINGTALK_WEBHOOK }}" \
-H 'Content-Type: application/json' \
-d '{
"msgtype": "markdown",
"markdown": {
"title": "部署成功",
"text": "## ✅ 部署成功\n- **项目**: myapp\n- **分支**: ${{ github.ref_name }}\n- **提交**: ${{ github.sha }}\n- **时间**: $(date)"
}
}'
```
## 相关文档
- [Docker 生产部署](/zh/deploy/docker.html)
- [后端部署](/zh/deploy/backend.html)
- [前端部署](/zh/deploy/frontend.html)