Files
live-app-translator/docs/WINDOWS-TESTING.md
EJClaw 88b9d931eb
Some checks failed
Windows / verify (push) Has been cancelled
ci: GPU 러너 동시 실행 차단 + 티어 검증을 실제 로드로 교체
러너를 여러 프로젝트가 공유한다는 전제에서 두 가지 구멍이 있었다.

1) 동시 실행 — GPU 는 나눠 쓸 수 없다. 두 작업이 같이 VRAM 을 잡으면
   둘 다 OOM 이거나, 더 나쁘게는 하나가 조용히 느려져 원인을 못 찾는다.
   concurrency(같은 저장소) + 러너 capacity:1(저장소 무관, 진짜 보장)을
   두 겹으로 두고, 문서에 capacity 확인을 필수 절차로 넣었다.
   한 대에 러너 프로세스를 여러 개 띄우면 안 된다는 것도 명시.

2) 티어 검증이 주장보다 얕았다 — "4·5티어 검증"이라 해놓고 실제로는
   tier_availability() 만 봤고, 그 함수는 torch 유무만 본다. 즉

     - 4티어(Seed-X AWQ Int4)는 autoawq 가 없어도 "사용 가능"
     - 5티어(Qwen3-8B)는 VRAM 11GB 가 필요한데 8GB 카드에서도 "사용 가능"

   게이트가 통과인데 실제로는 못 올리는 상태를 CI 가 못 잡고 있었다.
   그래서 게이트 검사는 얕은 검사임을 이름과 주석에 명시하고, 그 옆에
   **진짜로 모델을 올려 번역까지 하는** 항목을 새로 뒀다. 러너 GPU 가
   무엇일지 모르므로 여유 VRAM 과 autoawq 유무로 후보를 거른 뒤 올릴 수
   있는 것 중 가장 무거운 티어를 고르고, 건너뛴 티어와 그 이유를 결과에
   남긴다. 하나도 못 올리면 실패다 (조용히 통과하지 않는다).

   게이트는 통과인데 실제 로드가 실패하면 그게 찾아야 할 버그다 — 앱이
   사용자에게 "쓸 수 있다"고 하고선 못 올리는 상황. 러너가 붙으면 실제
   증거를 보고 tier_availability() 를 고칠지 판단한다.

- autoawq 설치 단계 추가(4티어 전용, Windows 휠이 없을 때가 있어 선택)
- 1~3티어 CUDA 경로 번역 검사는 유지하고 unload 를 finally 로 보장

검증: pytest 189개 통과, ruff clean, 워크플로 파싱·concurrency 확인,
      --gpu 를 torch 없는 환경에서 실행해 6개 중 5개가 의도대로 실패

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 03:47:15 +09:00

12 KiB

Windows에서 자동 검증 붙이기

개발은 리눅스에서 하고 있어서 Windows 전용 경로는 테스트가 불가능합니다. 오디오 캡처, 전역 단축키, 모니터 열거, exe 빌드가 전부 여기에 해당합니다. 그래서 "리눅스에서 다 통과했는데 윈도우에서 안 되더라"가 나올 수 있습니다.

Windows 러너를 하나 붙이면 main 에 push할 때(또는 Actions 탭에서 수동 실행할 때) 이 경로들이 자동으로 검증됩니다.

어디에 올릴까 — 서버 Windows VM 권장

개인 PC를 안 쓰고 서버에 올려 완전 자동화하는 게 가장 깔끔합니다. GPU가 없어도 됩니다. 실제로 확인해봤습니다.

검증 항목 GPU 필요? GPU 없는 서버 VM
단위 테스트 189개 ❌ ✅
WASAPI 장치·프로그램 열거 ❌ ✅
전역 단축키 ❌ ✅
모니터 열거·자막 배치 ❌ ✅
포터블 exe 빌드·기동 ❌ ✅
오디오 → 한국어 자막 (전 과정) ❌ CPU로 됨 ✅
CUDA 인식·VRAM 상한 ✅ ⚠️ 경고만 (빌드 통과)

GPU가 필요한 테스트는 어떻게 하나

GPU는 한 VM만 독점합니다. 컨슈머 NVIDIA는 vGPU/SR-IOV를 지원하지 않아서 쪼개 쓸 방법이 없습니다. 지금 RTX 5050은 .9(리눅스)에 물려 있습니다.

그래서 러너를 라벨로 둘로 나눠뒀습니다. 지금은 앞의 것만 있으면 되고, 나중에 GPU 머신이 생기면 뒤의 것을 붙이기만 하면 됩니다. 워크플로는 이미 그렇게 짜여 있습니다.

러너 라벨 어디에 언제 도나 무엇을
windows:host GPU 없는 서버 VM main push마다 자동 테스트·Windows 경로·오디오→자막·exe 빌드
windows-gpu:host GPU 달린 머신 수동 실행만 CUDA 인식·VRAM 상한·4·5티어·GPU 번역

GPU 러너를 상시로 띄울 필요는 없습니다. 필요할 때만 daemon을 켜고 Actions 탭에서 Windows GPU → Run workflow 하면 됩니다. 러너가 없으면 큐에 쌓이기만 하고 아무 일도 일어나지 않습니다.

GPU 워크플로는 GPU가 없으면 분명히 실패합니다. GPU를 확인하겠다고 부른 작업이 조용히 통과하면 안 되니까요. nvidia-smi가 없으면 그 자리에서 멈춥니다.

여러 프로젝트가 GPU 러너를 같이 쓸 때 — 동시에 돌리면 안 됩니다

이게 가장 중요합니다. GPU는 나눠 쓸 수 없어서, 두 작업이 동시에 VRAM을 잡으면 둘 다 OOM으로 죽거나 하나가 조용히 느려져 원인을 못 찾습니다. CPU 러너와 달리 GPU 러너는 반드시 한 번에 한 작업만 돌아야 합니다.

보장 장치가 두 겹입니다.

  1. 러너 capacity: 1 — 진짜 보장입니다. 저장소가 달라도 막힙니다. act_runner 설정 파일(config.yaml)에서 확인하세요. 기본값이 1이지만 GPU 러너에서는 반드시 확인하고 넘어가세요.

    runner:
      capacity: 1   # GPU 러너는 절대 올리지 말 것
    

    설정 파일이 없으면 만들고 --config config.yaml 로 띄웁니다.

    .\act_runner.exe generate-config > config.yaml
    .\act_runner.exe daemon --config config.yaml
    
  2. 워크플로 concurrency — 같은 저장소 안에서 겹치는 것만 막습니다. windows-gpu.yml 에 이미 들어 있습니다. 다른 저장소에는 효력이 없으니 1번을 대신하지 못합니다.

한 대에 러너 프로세스를 여러 개 띄우지도 마세요. capacity 1짜리 러너가 둘이면 결국 동시에 두 작업이 돕니다.

GPU 머신을 어디서 구하나

다른 프로젝트도 이 러너에서 GPU 테스트를 돌리실 거라면 셋 중 하나입니다.

  1. 서버에 GPU 한 장 더 — 가장 깔끔합니다. PCIe 슬롯과 파워 여유만 있으면 그 GPU를 Windows VM에 패스스루하고 windows-gpu 라벨로 등록하면 끝입니다. 이후로는 두 러너가 각자 계속 돕니다.
  2. RTX 5050을 Windows VM으로 옮기기 — .9가 GPU를 포기해야 합니다. .9에서 하던 GPU 작업(이미지 생성 등)을 못 하게 되니 득실을 따져보세요.
  3. 필요할 때만 실물 PC에 GPU 러너 — 평소엔 꺼두고 GPU 검증이 필요할 때만 켭니다. 수동 실행이라 게임 중에 갑자기 도는 일은 없습니다.

1번이 가능한지는 서버에서 이걸로 확인할 수 있습니다.

lspci | grep -i vga          # GPU가 몇 장 꽂혀 있나
dmidecode -t slot | grep -i "in use"   # 빈 PCIe 슬롯이 있나

Proxmox에 Windows VM 만들기

# Proxmox 호스트에서 (VMID 는 비어 있는 번호로)
qm create 920 --name win-ci --memory 8192 --cores 4   --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-single   --scsi0 local-lvm:80 --ostype win11 --machine q35 --bios ovmf   --efidisk0 local-lvm:1,efitype=4m,pre-enrolled-keys=1 --tpmstate0 local-lvm:1,version=v2.0   --ide2 local:iso/Win11.iso,media=cdrom --ide0 local:iso/virtio-win.iso,media=cdrom   --audio0 device=ich9-intel-hda,driver=none   --vga std

RAM 8GB / 디스크 80GB면 충분합니다(모델 캐시 포함). 아래 둘을 꼭 넣으세요.

  • --audio0 ... — 없으면 오디오 장치가 하나도 안 잡혀 WASAPI 점검이 실패합니다
  • --vga std — 화면이 있어야 자막 오버레이를 그리고 스크린샷을 찍습니다

VM 안에서 꼭 해야 하는 두 가지

1. 자동 로그온 — 러너가 서비스로 돌면 데스크톱 세션이 없어 GUI 테스트와 스크린샷이 전부 실패합니다. 자동 로그온 + 로그온 시 실행이어야 합니다.

# 관리자 PowerShell (VM 안이므로 부담 적음)
$k = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
Set-ItemProperty $k AutoAdminLogon 1
Set-ItemProperty $k DefaultUserName "ci"
Set-ItemProperty $k DefaultPassword "<비밀번호>"

2. 화면 꺼짐·잠금 끄기 — 잠기면 GUI 테스트가 실패합니다.

powercfg /change monitor-timeout-ac 0
powercfg /change standby-timeout-ac 0

그 다음은 아래 일반 설치 절차와 같습니다. 러너는 서비스가 아니라 로그온 예약 작업으로 등록하세요(아래 "항상 켜두고 싶다면" 참고).


무엇이 자동으로 확인되나

항목 리눅스에서 Windows 러너에서
단위 테스트 189개 ✅ ✅
WASAPI 루프백 장치 열거 ❌ ✅
소리 내는 프로그램 열거 ❌ ✅
전역 단축키 등록/해제 ❌ ✅
모니터 열거·자막 배치 가상 화면만 ✅ 실제 모니터
포터블 exe 빌드 ❌ ✅
exe가 실제로 뜨는지 ❌ ✅
GPU 인식 (CUDA) 이 서버 GPU ✅ 실제 게임 PC GPU
실제 화면 스크린샷 offscreen ✅ 진짜 화면

결과물(exe, 스크린샷, 로그)은 Gitea Actions의 artifact로 올라오므로 원격에서도 눈으로 확인할 수 있습니다.


설치 (약 10분, 한 번만)

1. 필요한 것 설치

winget install Git.Git                                  # 소스 받기 (checkout) 에 필요
winget install Microsoft.PowerShell                     # 워크플로가 pwsh 7 을 씁니다
winget install Python.Python.3.12
winget install Kitware.CMake
winget install Microsoft.VisualStudio.2022.BuildTools   # C++ 데스크톱 워크로드 선택

Git.Git 과 Microsoft.PowerShell 은 빠뜨리기 쉬운데 둘 다 필수입니다. Git 이 없으면 소스를 못 받고, Windows 에 기본으로 깔린 PowerShell 은 5.1 이라 워크플로가 쓰는 pwsh(7.x) 가 없으면 단계마다 실패합니다. 설치 후 새 PowerShell 창을 열어 git --version, pwsh --version 이 나오는지 확인하세요.

2. Gitea 러너 내려받기

https://gitea.com/gitea/act_runner/releases 에서 act_runner-<버전>-windows-amd64.exe 를 받아 C:\gitea-runner\act_runner.exe 로 둡니다.

3. 등록 토큰 받기

Gitea 웹에서 저장소 → 설정 → Actions → 러너 → 등록 토큰 생성 (주소: https://git.tkrmagid.kr/tkrmagid/live-app-translator/settings/actions/runners)

4. 등록하고 실행

cd C:\gitea-runner
.\act_runner.exe register --no-interactive `
  --instance https://git.tkrmagid.kr `
  --token <위에서 받은 토큰> `
  --name windows-test `
  --labels windows:host

.\act_runner.exe daemon

--labels windows:host 가 중요합니다. 워크플로의 runs-on: windows 와 이름이 맞아야 하고, :host 는 컨테이너가 아니라 그 PC에서 직접 돌리라는 뜻입니다 (생략해도 기본값이 host 지만 명시하는 편이 낫습니다).

이름이 안 맞으면 Gitea 는 작업을 실패시키지 않고 기본 우분투 컨테이너에서 돌립니다. Windows 검증이 조용히 리눅스에서 돌아가는 셈이라, 워크플로 첫 단계에서 그걸 막아 뒀습니다.

5. 확인

워크플로는 main 에 push될 때 또는 Actions 탭에서 수동 실행할 때 돕니다. (.gitea/workflows/windows.yml 의 on: 항목)

저장소 Actions 탭 → Windows → Run workflow 로 바로 한 번 돌려보세요.


항상 켜두고 싶다면 (선택)

서비스(sc.exe create)로 등록하지 마세요. 그렇게 하면 SYSTEM 권한으로 돌아가 러너가 PC 전체를 건드릴 수 있게 됩니다. 로그인할 때 그 계정 권한으로 뜨게 하는 편이 안전합니다.

# 관리자 아님 — 그냥 평소 PowerShell
$action  = New-ScheduledTaskAction -Execute "C:\gitea-runner\act_runner.exe" `
                                   -Argument "daemon" -WorkingDirectory "C:\gitea-runner"
$trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -TaskName "gitea-runner" -Action $action -Trigger $trigger

게임할 때 방해되면 그냥 꺼두고, 확인이 필요할 때만 daemon 을 켜도 됩니다. 러너가 꺼져 있으면 워크플로는 대기만 하고 아무 일도 하지 않습니다.


안전에 대해 — 정확히 어떤 권한인가

솔직하게 말하면, 이건 "그 PC에서 명령을 실행할 수 있는 권한"입니다.

러너는 저장소의 워크플로 파일을 실행합니다. 그런데 저(Claude)는 이 저장소의 main에 push할 수 있으므로, 워크플로 파일을 고쳐서 커밋하면 결과적으로 그 PC에서 원하는 명령을 돌릴 수 있습니다. "커밋된 파일만 실행하니 안전하다"는 말은 정확하지 않습니다.

실제로 보장되는 것은 이것뿐입니다.

  • 숨길 수 없습니다. 실행되는 모든 것은 .gitea/workflows/ 안에 있고 git 기록에 남습니다. 몰래 뭘 했는지 나중에 전부 확인할 수 있습니다.
  • 러너를 끄면 즉시 멈춥니다. daemon 을 종료하면 그만입니다.
  • 러너는 자기 계정 권한으로 돕니다. 관리자로 띄우지 마세요.

그래서 권하는 방식은 이렇습니다.

방법 위험 얻는 것
Windows VM (Proxmox 등) 거의 없음 빌드·단축키·모니터·UI·exe 실행
전용 Windows 계정 낮음 — 그 계정 파일만 노출 위 전부 + 실제 GPU
평소 쓰는 계정 개인 파일·브라우저 세션 노출 위 전부 + 실제 게임 소리

VM이나 전용 계정을 권합니다. GPU와 실제 게임 소리 확인은 못 하지만, 지금 미검증인 항목의 대부분(빌드·단축키·모니터·UI·exe 기동)은 거기서도 그대로 검증됩니다. GPU/오디오는 필요할 때만 평소 계정에서 한 번씩 확인하는 편이 낫습니다.

부담스러우면 러너를 붙이지 않고 아래 "러너 없이 직접 돌려보기"만 하셔도 충분합니다.


러너 없이 직접 돌려보기

러너를 안 붙이고 그때그때 확인만 하고 싶다면 이것만 실행하고 결과를 보내주셔도 됩니다.

git clone https://git.tkrmagid.kr/tkrmagid/live-app-translator
cd live-app-translator
py -3.12 -m venv .venv
.\.venv\Scripts\python -m pip install -r packaging\requirements-portable.txt pytest
$env:PYTHONPATH="src"
.\.venv\Scripts\python -m pytest tests -q
.\.venv\Scripts\python packaging\windows_smoke.py

artifacts\windows-smoke.txt 와 artifacts\*.png 를 보내주시면 됩니다.