Files
2026-07-13 21:36:47 +08:00

419 lines
13 KiB
Markdown
Raw Permalink 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.
# CIGitLab CI/CD
> **使用场景**:在 GitLab 流水线中针对合并请求、推送到主分支或定时流水线运行 Playwright 测试。
## 快速参考
```bash
# GitLab 流水线中使用的关键命令
npx playwright install --with-deps # 安装浏览器 + 操作系统依赖
npx playwright test --shard=1/4 # 运行 4 个分片中的第 1 个
npx playwright merge-reports ./blob-report # 合并分片结果
npx playwright test --reporter=dot # CI 日志的最小化输出
```
## 模式
### 模式 1:生产就绪流水线(复制粘贴即用)
**使用场景**:任何包含 Playwright 测试的 GitLab 项目。这是完整且推荐的配置。
```yaml
# .gitlab-ci.yml
image: mcr.microsoft.com/playwright:v1.52.0-noble
stages:
- install
- test
- report
variables:
CI: "true"
npm_config_cache: "$CI_PROJECT_DIR/.npm"
# 跨流水线缓存 node_modules 和 npm 缓存
cache:
key:
files:
- package-lock.json
paths:
- .npm/
- node_modules/
install:
stage: install
script:
- npm ci
artifacts:
paths:
- node_modules/
expire_in: 1 hour
test:
stage: test
needs: [install]
script:
- npx playwright test
artifacts:
when: always
paths:
- playwright-report/
- test-results/
expire_in: 14 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
```
### 模式 2:并行分片执行
**使用场景**:测试套件运行超过 10 分钟。使用 GitLab 的 `parallel` 关键字在多个作业之间自动拆分。
**避免场景**:测试套件运行时间少于 5 分钟。
```yaml
# .gitlab-ci.yml
image: mcr.microsoft.com/playwright:v1.52.0-noble
stages:
- install
- test
- report
variables:
CI: "true"
npm_config_cache: "$CI_PROJECT_DIR/.npm"
cache:
key:
files:
- package-lock.json
paths:
- .npm/
- node_modules/
install:
stage: install
script:
- npm ci
artifacts:
paths:
- node_modules/
expire_in: 1 hour
test:
stage: test
needs: [install]
parallel: 4
script:
- npx playwright test --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL
artifacts:
when: always
paths:
- blob-report/
expire_in: 1 hour
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
merge-report:
stage: report
needs: [test]
when: always
script:
- npx playwright merge-reports --reporter=html ./blob-report
artifacts:
when: always
paths:
- playwright-report/
expire_in: 14 days
```
**分片流水线的配置:**
```ts
// playwright.config.ts
import { defineConfig } from "@playwright/test"
export default defineConfig({
reporter: process.env.CI ? [["blob"], ["dot"]] : [["html", { open: "on-failure" }]],
})
```
### 模式 3:含环境变量的合并请求流水线
**使用场景**:测试需要密钥(API 密钥、密码),且应仅在合并请求或默认分支上运行。
**避免场景**:测试完全自包含,无外部依赖。
```yaml
# .gitlab-ci.yml
image: mcr.microsoft.com/playwright:v1.52.0-noble
stages:
- test
variables:
CI: "true"
test:e2e:
stage: test
variables:
BASE_URL: $STAGING_URL
TEST_PASSWORD: $TEST_PASSWORD
API_KEY: $API_KEY
before_script:
- npm ci
script:
- npx playwright test
artifacts:
when: always
paths:
- playwright-report/
- test-results/
expire_in: 14 days
rules:
# 在合并请求上运行
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# 在推送到默认分支时运行
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# 允许手动触发
- when: manual
allow_failure: true
```
**在 GitLab 中设置变量:**
导航至 **Settings > CI/CD > Variables** 并添加:
- `STAGING_URL` —— 不脱敏,不保护
- `TEST_PASSWORD` —— 脱敏,受保护
- `API_KEY` —— 脱敏,受保护
### 模式 4:使用子流水线进行多浏览器测试
**使用场景**:在合并请求上运行 Chromium,在默认分支上运行所有浏览器。
**避免场景**:你只测试一个浏览器。
```yaml
# .gitlab-ci.yml
image: mcr.microsoft.com/playwright:v1.52.0-noble
stages:
- install
- test
variables:
CI: "true"
install:
stage: install
script:
- npm ci
artifacts:
paths:
- node_modules/
expire_in: 1 hour
# 合并请求上仅运行 Chromium(快速反馈)
test:chromium:
stage: test
needs: [install]
script:
- npx playwright test --project=chromium
artifacts:
when: always
paths:
- playwright-report/
- test-results/
expire_in: 14 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# 默认分支上运行所有浏览器
test:all-browsers:
stage: test
needs: [install]
parallel:
matrix:
- PROJECT: [chromium, firefox, webkit]
script:
- npx playwright test --project=$PROJECT
artifacts:
when: always
paths:
- playwright-report/
- test-results/
expire_in: 14 days
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
```
### 模式 5:包含应用的自定义 Docker 镜像
**使用场景**:测试需要与 Playwright 一起运行应用程序,或者你需要自定义系统依赖。
**避免场景**:官方的 Playwright 镜像加上配置中的 `webServer` 已能满足你的使用场景。
```yaml
# .gitlab-ci.yml
stages:
- test
test:e2e:
stage: test
image: mcr.microsoft.com/playwright:v1.52.0-noble
services:
- name: postgres:16-alpine
alias: db
- name: redis:7-alpine
alias: cache
variables:
CI: "true"
DATABASE_URL: "postgresql://postgres:postgres@db:5432/test"
REDIS_URL: "redis://cache:6379"
POSTGRES_PASSWORD: "postgres"
POSTGRES_DB: "test"
before_script:
- npm ci
- npx prisma db push
- npx prisma db seed
script:
- npx playwright test
artifacts:
when: always
paths:
- playwright-report/
- test-results/
expire_in: 14 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
```
### 模式 6:定时夜间回归测试
**使用场景**:完整的回归测试对每个合并请求来说太慢了。按计划运行它。
```yaml
# .gitlab-ci.yml(添加到现有配置)
test:nightly:
stage: test
image: mcr.microsoft.com/playwright:v1.52.0-noble
before_script:
- npm ci
script:
- npx playwright test --grep @regression
artifacts:
when: always
paths:
- playwright-report/
expire_in: 30 days
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
```
**CI/CD > Schedules** 中设置定时任务:`0 3 * * 1-5`(UTC 时间凌晨 3 点,工作日)。
## 决策指南
| 场景 | 方案 | 原因 |
| ------------------------------------ | ------------------------------------------------------ | --------------------------------------------------- |
| 简单项目,测试套件 < 5 分钟 | 使用 Playwright Docker 镜像的单一 `test` 作业 | 无需分片开销;构建产物捕获报告 |
| 测试套件 > 10 分钟 | 使用 `--shard``parallel: N` | GitLab 自动分配 `CI_NODE_INDEX`/`CI_NODE_TOTAL` |
| 合并请求快速反馈 | 合并请求上仅运行 Chromium;主分支上运行所有浏览器 | 合并请求的流水线耗时减少 3 倍 |
| 需要外部服务(数据库、Redis | 使用 Postgres/Redis 镜像的 `services:` 关键字 | GitLab 管理服务生命周期 |
| 预发环境的密钥 | GitLab CI/CD 变量(脱敏 + 受保护) | 永远不要将密钥硬编码在 `.gitlab-ci.yml` 中 |
| 完整的夜间回归测试 | 流水线定时任务(`CI_PIPELINE_SOURCE == "schedule"`) | 避免阻塞合并请求流水线 |
| 报告浏览 | 使用 `paths: [playwright-report/]``artifacts:` | 直接在 GitLab 作业构建产物 UI 中浏览 |
## 反模式
| 反模式 | 问题 | 应改为 |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| 未使用 Playwright Docker 镜像 | 每次运行都安装浏览器,增加 1-2 分钟 | 使用 `mcr.microsoft.com/playwright:v1.52.0-noble` 作为基础镜像 |
| 仅设置 `artifacts: when: on_failure` | 测试通过时无报告;无法验证结果 | 使用 `when: always` 来始终捕获报告 |
| 构建产物没有设置 `expire_in` | 构建产物不断累积,消耗存储空间 | 为报告设置 `expire_in: 14 days`,为中间构建产物设置 `1 hour` |
| 使用 `parallel:` 但没有等价的 `fail-fast: false` | GitLab 默认不会取消同级作业(好),但 `allow_failure: false` 意味着流水线会快速失败 | 可接受的默认行为;无需更改 |
| 在分片标志中硬编码 `CI_NODE_TOTAL` | 当更改 `parallel:` 值时就会失效 | 使用 `--shard=$CI_NODE_INDEX/$CI_NODE_TOTAL` |
| 在阶段之间跳过 `needs:` | 作业会等待前一阶段的所有作业,而不仅仅是其依赖项 | 使用 `needs:` 实现精确的依赖图 |
| 包含 `node_modules/``cache:` 没有 `key` | 过期的缓存导致版本冲突 | 将缓存键设置为基于 `package-lock.json` 的哈希 |
## 故障排查
### 浏览器启动失败:"Failed to launch browser"
**原因**:未使用 Playwright Docker 镜像,或使用的版本与你的 `@playwright/test` 版本不匹配。
**修复**:将 Docker 镜像标签与你的 Playwright 版本匹配:
```yaml
# 检查你的版本
# npm ls @playwright/test -> @playwright/test@1.52.0
image: mcr.microsoft.com/playwright:v1.52.0-noble
```
### 测试在 GitLab runner 中挂起:"Navigation timeout exceeded"
**原因**GitLab 共享 runner 的资源可能有限。默认超时时间设置过紧。
**修复**:减少 workers 并增加超时时间:
```ts
// playwright.config.ts
import { defineConfig } from "@playwright/test"
export default defineConfig({
workers: process.env.CI ? 2 : undefined,
use: {
navigationTimeout: process.env.CI ? 30_000 : 15_000,
},
})
```
### 流水线在每次推送时都运行,而不仅仅是合并请求
**原因**:缺少 `rules:` 配置。GitLab 的默认行为是在每次推送时都运行。
**修复**:添加明确的规则:
```yaml
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
```
### 测试无法访问服务(Postgres/Redis
**原因**:使用了 `localhost` 而不是服务别名。
**修复**:使用服务别名作为主机名:
```yaml
services:
- name: postgres:16-alpine
alias: db # <-- 使用 "db" 作为主机名
variables:
DATABASE_URL: "postgresql://postgres:postgres@db:5432/test" # 不是 localhost
```
### 分片运行后合并报告为空
**原因**:每个分片作业需要 `blob` 报告器,而不是 `html`。合并步骤会创建 HTML 报告。
**修复**:为 CI 配置 blob 报告器:
```ts
// playwright.config.ts
import { defineConfig } from "@playwright/test"
export default defineConfig({
reporter: process.env.CI ? [["blob"], ["dot"]] : [["html", { open: "on-failure" }]],
})
```
## 相关文档
- [ci/ci-github-actions.md](ci-github-actions.md) —— GitHub Actions 的等效配置
- [ci/ci-other.md](ci-other.md) —— CircleCI、Azure DevOps、Jenkins
- [ci/parallel-and-sharding.md](parallel-and-sharding.md) —— 分片策略
- [ci/docker-and-containers.md](docker-and-containers.md) —— Docker 镜像详情
- [ci/reporting-and-artifacts.md](reporting-and-artifacts.md) —— 报告器配置