419 lines
13 KiB
Markdown
419 lines
13 KiB
Markdown
# CI:GitLab 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) —— 报告器配置
|