用途
该技能用于在 CI/CD 流水线中缓存 Playwright 浏览器二进制文件,避免每次构建都重新下载。默认情况下,Playwright 浏览器(约 400MB)必须在每次 CI 运行时下载,这会为每次构建增加 1-2 分钟、浪费带宽、可能因瞬时网络问题失败,并拖慢 PR 反馈循环。
适用场景
- 为带有 Playwright E2E 测试的项目设置 CI/CD
- 构建时间因浏览器下载(约 400MB,1-2 分钟)而变慢
- 希望在 Playwright 版本变化时自动使缓存失效
- 使用 GitHub Actions 或 Azure DevOps 流水线
核心模式
- 从 Directory.Packages.props(CPM)中提取 Playwright 版本,用作缓存键
- 使用与平台匹配的路径缓存浏览器二进制文件
- 条件安装:仅在缓存未命中时下载
- 自动缓存失效:缓存键包含版本号,因此包升级会使缓存失效
各操作系统缓存路径
- Linux:
~/.cache/ms-playwright - macOS:
~/Library/Caches/ms-playwright - Windows:
%USERPROFILE%\AppData\Local\ms-playwright
GitHub Actions 配置
- name: Get Playwright Version
shell: pwsh
run: |
$propsPath = "Directory.Packages.props"
[xml]$props = Get-Content $propsPath
$version = $props.Project.ItemGroup.PackageVersion |
Where-Object { $_.Include -eq "Microsoft.Playwright" } |
Select-Object -ExpandProperty Version
echo "PlaywrightVersion=$version" >> $env:GITHUB_ENV
- name: Cache Playwright Browsers
id: playwright-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ env.PlaywrightVersion }}
- name: Install Playwright Browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
shell: pwsh
run: ./build/playwright.ps1 install --with-deps
多操作系统 GitHub Actions 工作流:
- name: Cache Playwright Browsers
id: playwright-cache
uses: actions/cache@v4
with:
path: |
~/.cache/ms-playwright
~/Library/Caches/ms-playwright
~/AppData/Local/ms-playwright
key: ${{ runner.os }}-playwright-${{ env.PlaywrightVersion }}
Azure DevOps 配置
- task: PowerShell@2
displayName: 'Get Playwright Version'
inputs:
targetType: 'inline'
script: |
[xml]$props = Get-Content "Directory.Packages.props"
$version = $props.Project.ItemGroup.PackageVersion |
Where-Object { $_.Include -eq "Microsoft.Playwright" } |
Select-Object -ExpandProperty Version
Write-Host "##vso[task.setvariable variable=PlaywrightVersion]$version"
- task: Cache@2
displayName: 'Cache Playwright Browsers'
inputs:
key: 'playwright | "$(Agent.OS)" | $(PlaywrightVersion)'
path: '$(HOME)/.cache/ms-playwright'
cacheHitVar: 'PlaywrightCacheHit'
- task: PowerShell@2
displayName: 'Install Playwright Browsers'
condition: ne(variables['PlaywrightCacheHit'], 'true')
inputs:
filePath: 'build/playwright.ps1'
arguments: 'install --with-deps'
辅助脚本 playwright.ps1
创建 build/playwright.ps1 脚本,用于发现并运行 Playwright CLI,从而抽象掉因项目结构不同而变化的 Playwright CLI 位置。
# build/playwright.ps1
# Discovers Microsoft.Playwright.dll and runs the bundled Playwright CLI
param(
[Parameter(ValueFromRemainingArguments = $true)]
[string[]]$Arguments
)
# Find the Playwright DLL (after dotnet build/restore)
$playwrightDll = Get-ChildItem -Path . -Recurse -Filter "Microsoft.Playwright.dll" -ErrorAction SilentlyContinue |
Select-Object -First 1
if (-not $playwrightDll) {
Write-Error "Microsoft.Playwright.dll not found. Run 'dotnet build' first."
exit 1
}
$playwrightDir = $playwrightDll.DirectoryName
# Find the playwright CLI (path varies by OS and node version)
$playwrightCmd = Get-ChildItem -Path "$playwrightDir/.playwright/node" -Recurse -Filter "playwright.cmd" -ErrorAction SilentlyContinue |
Select-Object -First 1
if (-not $playwrightCmd) {
# Try Unix executable
$playwrightCmd = Get-ChildItem -Path "$playwrightDir/.playwright/node" -Recurse -Filter "playwright" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -eq "playwright" } |
Select-Object -First 1
}
if (-not $playwrightCmd) {
Write-Error "Playwright CLI not found in $playwrightDir/.playwright/node"
exit 1
}
Write-Host "Using Playwright CLI: $($playwrightCmd.FullName)"
& $playwrightCmd.FullName @Arguments
用法:
# Install browsers
./build/playwright.ps1 install --with-deps
# Install specific browser
./build/playwright.ps1 install chromium
# Show installed browsers
./build/playwright.ps1 install --dry-run
前提条件
该模式假设:
- 使用中央包管理(CPM)与 Directory.Packages.props:
<Project>
<ItemGroup>
<PackageVersion Include="Microsoft.Playwright" Version="1.40.0" />
</ItemGroup>
</Project>
- 项目在运行 playwright.ps1 之前已构建(以便 DLL 存在)
- CI 代理上可用 PowerShell(GitHub Actions 和 Azure DevOps 已预装)
为什么基于版本的缓存键很重要
在缓存键中使用 Playwright 版本可确保:
- 升级 Playwright 时自动失效
- 不会出现与 SDK 版本不匹配的过期浏览器二进制文件
- 版本升级后无需手动清除缓存
如果硬编码缓存键(例如 playwright-browsers-v1),则每次升级 Playwright 时都需要手动更新它,否则会遇到难以理解的版本不匹配错误。
故障排查
缓存未被使用:
- 验证版本提取步骤输出了正确的版本
- 检查缓存路径是否与操作系统匹配
- 确保 Directory.Packages.props 存在且包含 Playwright 包
缓存命中后出现“Browser not found”:
- 缓存的浏览器与 Playwright SDK 版本不匹配。发生在:缓存键不包含版本;版本提取静默失败。
- 修复:确保 Playwright 版本位于缓存键中。
playwright.ps1 找不到 DLL:
- 在运行脚本前执行
dotnet build或dotnet restore。Playwright DLL 仅在 NuGet 还原后存在。
参考
该模式已在生产项目中经过实战检验:
- petabridge/geekedin
- petabridge/DrawTogether.NET
相关技能
- dotnet-skills:playwright-blazor - 为 Blazor 应用程序编写 Playwright 测试
- dotnet-skills:project-structure - 中央包管理设置