Community코딩 & 개발github.com

playwright-ci-caching

CI/CD 파이프라인(GitHub Actions, Azure DevOps)에서 Playwright 브라우저 바이너리를 캐싱하여 매 빌드마다 발생하는 1-2분의 다운로드 오버헤드를 방지합니다.

playwright-ci-caching란 무엇인가요?

이 스킬은 CI/CD 파이프라인에서 Playwright E2E 테스트를 실행할 때 브라우저 바이너리 다운로드로 인한 빌드 시간 지연을 해결하는 방법을 제공합니다. Playwright 브라우저(~400MB)는 기본적으로 CI 실행 시마다 다운로드되어 1-2분의 추가 시간, 대역폭 낭비, 일시적 네트워크 오류로 인한 실패를 유발합니다. 이 스킬은 `Directory.Packages.props`에서 Playwright 버전을 추출하여 캐시 키로 사용하고, OS별 적절한 경로에 브라우저 바이너리를 캐싱하며, 캐시 미스 시에만 다운로드하도록 조건부 설치를 구성합니다. GitHub Actions와 Azure DevOps 파이프라인을 모두 지원하며, 버전 변경 시 자동으로 캐시를 무효화하는 패턴을 사용합니다. 또한 Playwright CLI 위치를 추상화하는 `build/playwright.ps1` 헬퍼 스크립트를 제공하여 프로젝트 구조에 관계없이 일관된 방식으로 브라우저 설치를 수행할 수 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/playwright-ci-caching

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

CI/CD에서 Playwright 브라우저 캐싱

이 스킬을 사용하는 경우

다음 상황에서 이 스킬을 사용하세요:

  • Playwright E2E 테스트가 포함된 프로젝트의 CI/CD 설정 시
  • 브라우저 다운로드(~400MB, 1-2분)로 빌드 시간이 느린 경우
  • Playwright 버전 변경 시 자동으로 캐시를 무효화하려는 경우
  • GitHub Actions 또는 Azure DevOps 파이프라인을 사용하는 경우

문제

Playwright 브라우저(~400MB)는 기본적으로 모든 CI 실행 시 다운로드되어야 합니다. 이로 인해:

  • 모든 빌드에 1-2분이 추가됨
  • 대역폭 낭비
  • 일시적인 네트워크 문제로 실패 가능
  • PR 피드백 루프 지연

핵심 패턴

  1. Playwright 버전 추출 - Directory.Packages.props(CPM)에서 캐시 키로 사용할 버전 추출
  2. 브라우저 바이너리 캐싱 - 플랫폼에 적합한 경로 사용
  3. 조건부 설치 - 캐시 미스 시에만 다운로드
  4. 자동 캐시 무효화 - 키에 버전이 포함되어 패키지 업그레이드 시 캐시가 무효화됨

OS별 캐시 경로

OS경로
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

멀티 OS 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: 'Playwright 버전 가져오기'
  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: 'Playwright 브라우저 캐싱'
  inputs:
    key: 'playwright | "$(Agent.OS)" | $(PlaywrightVersion)'
    path: '$(HOME)/.cache/ms-playwright'
    cacheHitVar: 'PlaywrightCacheHit'

- task: PowerShell@2
  displayName: 'Playwright 브라우저 설치'
  condition: ne(variables['PlaywrightCacheHit'], 'true')
  inputs:
    filePath: 'build/playwright.ps1'
    arguments: 'install --with-deps'

도우미 스크립트: playwright.ps1

Playwright CLI를 찾아 실행하는 build/playwright.ps1 스크립트를 만듭니다. 이는 프로젝트 구조에 따라 달라지는 Playwright CLI 위치를 추상화합니다.

# build/playwright.ps1
# Microsoft.Playwright.dll을 찾아 번들된 Playwright CLI를 실행합니다

param(
    [Parameter(ValueFromRemainingArguments = $true)]
    [string[]]$Arguments
)

# Playwright DLL 찾기 (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을 찾을 수 없습니다. 먼저 'dotnet build'를 실행하세요."
    exit 1
}

$playwrightDir = $playwrightDll.DirectoryName

# Playwright CLI 찾기 (경로는 OS와 node 버전에 따라 다름)
$playwrightCmd = Get-ChildItem -Path "$playwrightDir/.playwright/node" -Recurse -Filter "playwright.cmd" -ErrorAction SilentlyContinue |
    Select-Object -First 1

if (-not $playwrightCmd) {
    # Unix 실행 파일 시도
    $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 "$playwrightDir/.playwright/node에서 Playwright CLI를 찾을 수 없습니다."
    exit 1
}

Write-Host "사용 중인 Playwright CLI: $($playwrightCmd.FullName)"
& $playwrightCmd.FullName @Arguments

사용법:

# 브라우저 설치
./build/playwright.ps1 install --with-deps

# 특정 브라우저 설치
./build/playwright.ps1 install chromium

# 설치된 브라우저 확인
./build/playwright.ps1 install --dry-run

전제 조건

이 패턴은 다음을 가정합니다:

  1. **중앙 패키지 관리(CPM)**와 Directory.Packages.props:

    <Project>
      <ItemGroup>
        <PackageVersion Include="Microsoft.Playwright" Version="1.40.0" />
      </ItemGroup>
    </Project>
    
  2. playwright.ps1 실행 전에 프로젝트가 빌드되어 있어야 함 (DLL이 존재해야 함)

  3. CI 에이전트에 PowerShell 사용 가능 (GitHub Actions 및 Azure DevOps에 사전 설치됨)

버전 기반 캐시 키가 중요한 이유

캐시 키에 Playwright 버전을 사용하면 다음이 보장됩니다:

  • Playwright 업그레이드 시 자동 무효화
  • SDK 버전과 일치하지 않는 오래된 브라우저 바이너리 없음
  • 버전 업데이트 후 수동 캐시 삭제 불필요

캐시 키를 하드코딩하면(예: playwright-browsers-v1) Playwright 업그레이드 시마다 수동으로 키를 변경해야 하거나, 버전 불일치로 인한 오류 메시지가 발생할 수 있습니다.

문제 해결

캐시가 사용되지 않는 경우

  1. 버전 추출 단계가 올바른 버전을 출력하는지 확인
  2. 캐시 경로가 OS와 일치하는지 확인
  3. Directory.Packages.props가 존재하고 Playwright 패키지가 포함되어 있는지 확인

캐시 히트 후 "브라우저를 찾을 수 없음" 오류

캐시된 브라우저가 Playwright SDK 버전과 일치하지 않는 경우입니다. 다음과 같은 상황에서 발생합니다:

  • 캐시 키에 버전이 포함되지 않은 경우
  • 버전 추출이 무시되었을 경우

해결 방법: 캐시 키에 Playwright 버전이 포함되도록 합니다.

playwright.ps1이 DLL을 찾을 수 없는 경우

스크립트를 실행하기 전에 dotnet build 또는 dotnet restore를 실행하세요. Playwright DLL은 NuGet 복원 후에만 존재합니다.

참고 자료

이 패턴은 실제 프로젝트에서 검증되었습니다:

관련 스킬

  • dotnet-skills:playwright-blazor - Blazor 애플리케이션용 Playwright 테스트 작성
  • dotnet-skills:project-structure - 중앙 패키지 관리 설정

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

관련 스킬