[Console]::OutputEncoding 을 UTF-8 로 두지 않으면 PowerShell 이 자식 프로세스 stdout 을 콘솔 코드페이지(LTSC en-US = 437)로 읽는다. python 쪽은 PYTHONUTF8=1 로 UTF-8 을 내보내니 한글이 전부 깨지고, 회수한 증거 파일의 실패 메시지를 읽을 수 없었다(실제로 첫 수집본의 한글 트레이스백이 그랬다). docs/WINDOWS-TESTING.md 에 증거 채널(POST /artifact -> results/)과 sync.sh <sha> 재검증 절차를 적었다.
26 KiB
Windows에서 자동 검증 붙이기
개발은 리눅스에서 하고 있어서 Windows 전용 경로는 테스트가 불가능합니다. 오디오 캡처, 전역 단축키, 모니터 열거, exe 빌드가 전부 여기에 해당합니다. 그래서 "리눅스에서 다 통과했는데 윈도우에서 안 되더라"가 나올 수 있습니다.
Windows 러너를 하나 붙이면 main 에 push할 때(또는 Actions 탭에서 수동
실행할 때) 이 경로들이 자동으로 검증됩니다.
어디에 올릴까 — 서버 Windows VM 권장
개인 PC를 안 쓰고 서버에 올려 완전 자동화하는 게 가장 깔끔합니다. GPU가 없어도 됩니다. 실제로 확인해봤습니다.
| 검증 항목 | GPU 필요? | GPU 없는 서버 VM |
|---|---|---|
| 단위 테스트 189개 | ❌ | ✅ |
| WASAPI 장치·프로그램 열거 | ❌ | ✅ |
| 전역 단축키 | ❌ | ✅ |
| 모니터 열거·자막 배치 | ❌ | ✅ |
| 포터블 exe 빌드·기동 | ❌ | ✅ |
| 오디오 → 한국어 자막 (전 과정) | ❌ CPU로 됨 | ✅ |
| CUDA 인식·VRAM 상한 | ✅ | ⚠️ 경고만 (빌드 통과) |
GPU 검증은 .9(리눅스)에서 — 이미 돌아갑니다
GPU는 .9에 고정입니다. 그래서 GPU 검증은 Windows가 아니라 여기서 합니다.
한 줄이면 됩니다.
scripts/gpu-check.sh
RTX 5050 실측 결과입니다.
[OK] CUDA 사용 가능 RTX 5050 · 7.5GB
[OK] VRAM 상한이 실제로 걸리는지 35% 상한 적용됨
[OK] GPU 로 실제 번역 '적 왼쪽에서 오는'
[OK] 티어 게이트가 뭐라고 하는지 정밀=autoawq 없음 / 극한=VRAM 부족(16GB 필요, 7.5GB)
6/6 통과
이 GPU로는 4·5티어를 쓸 수 없습니다. 정밀은 autoawq가 필요하고
(pip install autoawq), 극한은 16GB가 필요한데 카드가 7.5GB입니다.
극한은 카드를 바꾸지 않는 한 영영 불가능합니다. 1~3티어는 정상 동작합니다.
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 러너는 반드시 한 번에 한 작업만 돌아야 합니다.
보장 장치가 두 겹입니다.
-
러너
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 -
워크플로
concurrency— 같은 저장소 안에서 겹치는 것만 막습니다.windows-gpu.yml에 이미 들어 있습니다. 다른 저장소에는 효력이 없으니 1번을 대신하지 못합니다.
한 대에 러너 프로세스를 여러 개 띄우지도 마세요. capacity 1짜리 러너가 둘이면 결국 동시에 두 작업이 돕니다.
GPU 머신을 어디서 구하나
다른 프로젝트도 이 러너에서 GPU 테스트를 돌리실 거라면 셋 중 하나입니다.
- 서버에 GPU 한 장 더 — 가장 깔끔합니다. PCIe 슬롯과 파워 여유만 있으면
그 GPU를 Windows VM에 패스스루하고
windows-gpu라벨로 등록하면 끝입니다. 이후로는 두 러너가 각자 계속 돕니다. - RTX 5050을 Windows VM으로 옮기기 —
.9가 GPU를 포기해야 합니다..9에서 하던 GPU 작업(이미지 생성 등)을 못 하게 되니 득실을 따져보세요. - 필요할 때만 실물 PC에 GPU 러너 — 평소엔 꺼두고 GPU 검증이 필요할 때만 켭니다. 수동 실행이라 게임 중에 갑자기 도는 일은 없습니다.
1번이 가능한지는 서버에서 이걸로 확인할 수 있습니다.
lspci | grep -i vga # GPU가 몇 장 꽂혀 있나
dmidecode -t slot | grep -i "in use" # 빈 PCIe 슬롯이 있나
선택지 A — .9 안에 Windows VM (중첩 가상화)
Proxmox를 건드리지 않고 .9(우분투) 안에서 바로 Windows VM을 돌릴 수 있습니다.
실제로 확인했습니다.
/dev/kvm 존재
kvm_intel nested Y
qemu -enable-kvm kvm support: enabled ← 실측
여유 RAM / 디스크 25GB / 294GB
가장 큰 장점은 사람 손이 안 간다는 것입니다. .9 안이라 에이전트가 VM
생성부터 러너 등록까지 직접 합니다. Proxmox 자격증명도 필요 없습니다.
단, GPU는 여전히 안 됩니다. 중첩 패스스루가 불가능합니다.
IOMMU 그룹 수 0개
커널 cmdline iommu 옵션 없음
되게 하려면 Proxmox가 .9에 vIOMMU를 열어주고 .9 커널에 intel_iommu=on을
넣은 뒤 GPU를 vfio-pci에 묶어야 하는데, 그러면 .9가 GPU를 잃습니다.
결국 "GPU를 누가 가질 것인가" 문제로 되돌아오므로 중첩으로는 해결되지 않습니다.
만드는 법 — 스크립트 한 줄, 사람 손 없음
scripts/win-ci/ 에 전부 들어 있습니다. Windows 설치 화면을 사람이 클릭할
방법이 없으므로 무인 설치로 짰습니다.
scripts/win-ci/build.sh # ISO 받기 + 무인설치 ISO 굽기 + 디스크 생성
/home/claude/win-ci/start-vm.sh # VM + 제어 서버 기동
/home/claude/win-ci/boot-cd.sh reset # CD 부팅 프롬프트 넘기기 (최초 1회)
/home/claude/win-ci/status.sh # 진행 상황
/home/claude/win-ci/shot.sh a.png # 화면 스냅샷
네 부분이 이어달리기를 합니다.
| 파일 | 어디서 도나 | 하는 일 |
|---|---|---|
unattend/autounattend.xml |
Windows Setup | 디스크 분할·설치·OOBE 건너뛰기·ci 자동 로그온 |
unattend/setup/provision.ps1 |
첫 로그온 1회 | virtio 드라이버(네트워크·화면), 화면꺼짐/잠금 해제 |
unattend/setup/agent.ps1 |
로그온마다 상주 | 호스트에서 stage2.ps1 을 받아 실행 |
serve/stage2.ps1 |
호스트가 HTTP 로 제공 | Git·pwsh7·Python·Node·CMake·act_runner 설치 |
왜 설치 내용을 ISO 에 안 넣었나 — ISO 는 한 번 구우면 못 고칩니다. 설치
스크립트를 ISO 에 박으면 한 줄 고칠 때마다 Windows 를 다시 깔아야 합니다.
그래서 ISO 에는 껍데기(agent)만 넣고, 실제 설치 내용은 호스트의 HTTP 로
가져옵니다. serve/stage2.ps1 을 고치면 게스트가 20초 안에 알아서 새로
받아 실행합니다.
호스트와 게스트를 잇는 통로는 두 개입니다.
| 방향 | 통로 | 왜 |
|---|---|---|
| 호스트 → 게스트 | HTTP 10.0.2.2:8099 (QEMU user networking) |
스크립트·러너 토큰 전달 |
| 게스트 → 호스트 | COM1 → serial.log, HTTP POST /log → guest.log |
네트워크가 없는 드라이버 설치 단계도 봐야 하므로 시리얼이 필수 |
| 게스트 → 호스트 | HTTP POST /artifact/<이름> → results/<이름>.log |
검증 출력 전문. 바로 아래 참고 |
검증 결과는 results/ 에 파일로 남습니다 — 그게 기준선입니다
stage2.ps1 의 Step 이 각 단계 출력을 게스트 C:\win-ci\out\ 에 한 줄씩
기록하면서 호스트 /home/claude/win-ci/results/ 로 올립니다. 판정 요약은
results/summary.log 이고 검증한 리비전이 박혀 있습니다.
처음에는 로그 채널로 마지막 몇 줄만 흘렸는데, serial.log 는 VM 이 뜰 때마다
truncate 되고 guest.log 는 stage2 가 다시 돌 때만 채워집니다. 그래서 pytest
실패 트레이스백이 호스트에 한 줄도 남지 않아, 화면을 읽고 옮겨 적는 수밖에
없었습니다. 고친 뒤에 고쳐졌다고 말할 근거가 없는 상태였습니다.
재검증은 리비전으로 요청합니다.
cd scripts/win-ci && ./sync.sh <sha> # stage2 의 $TargetRev 를 그 커밋으로 박아 배포
tail -f /home/claude/win-ci/guest.log # 약 10분, 끝나면 results/summary.log 확인
게스트 agent 는 stage2.ps1 내용 해시가 바뀔 때만 다시 돕니다. 커밋만
푸시하면 재검증이 걸리지 않으니 sha 를 넘기세요. build.sh 를 다시 돌리면
안 됩니다 — 지워둔 Windows ISO 4.8GB 를 다시 내려받습니다.
접속 정보: 계정 ci / 비밀번호는 ~/.config/ejclaw/secrets.json 의
win-ci.9 항목. RDP 는 호스트 127.0.0.1:13389, VNC 는 127.0.0.1:5919.
재부팅해도 살아납니다. ~/.config/systemd/user/win-ci{,-http}.service 로
등록돼 있고 linger=yes 라 로그인 없이 default.target 이 끌어올립니다.
systemd-run 트랜지언트로 띄우면 /run 에만 남아 재부팅에 사라지니,
디버깅으로 한 번 띄웠더라도 마지막에는 반드시 아래로 되돌려 놓으세요.
systemctl --user start default.target # 부팅과 같은 경로로 기동
systemctl --user is-enabled win-ci.service # enabled 여야 한다. transient 면 안 된다
systemctl --user show win-ci.service -p FragmentPath # ~/.config/... 를 가리켜야 한다
설치가 끝난 뒤 무엇을 지우나 — 기준
지우는 기준은 "VM 이 도는 데 계속 필요한가" 하나입니다.
| 파일 | 설치 후 | 왜 |
|---|---|---|
iso/win11-ltsc.iso (4.8GB) |
지운다 | 설치 미디어. 다시 깔 때만 필요하고 build.sh 가 받아온다 |
iso/virtio-win.iso (837MB) |
남긴다 | VM 에 계속 물려 있다. 드라이버 재설치·재프로비저닝이 여기서 읽는다 |
unattend.iso (0.4MB) |
남긴다 | VM 에 계속 물려 있다 |
win-ci.qcow2 (14GB) |
남긴다 | VM 그 자체 |
ISO 를 지우면 -drive 가 없는 파일을 가리켜 qemu 가 기동 자체에 실패합니다.
재부팅 후 러너 호스트가 조용히 사라지므로, run-vm.sh 는 cdrom 을 있을 때만
붙입니다. 그리고 같은 ISO 를 버전별로 여러 개 남겨두지 마세요. 어느 것이
물려 있는지 헷갈리면 고친 줄 알았던 스크립트가 반영되지 않습니다. 현재
물려 있는 것은 query-block 으로 확인합니다.
무인 설치에서 실제로 걸린 것들
Press any key to boot from CD— UEFI 는 이걸 5초 안에 안 누르면 CD 를 포기합니다. 무인인데 누를 사람이 없어서 QMPsend-key로 밀어 넣습니다. 단, 설치가 시작된 뒤에는 절대 키를 더 보내면 안 됩니다. 남는 Enter 가 설치 화면의Cancel을 눌러 "Are you sure you want to quit?" 를 띄웁니다. 반대로 설치가 끝난 뒤 재부팅 때는 아무 키도 안 누르는 게 정답입니다. 그래야 CD 를 건너뛰고 디스크로 부팅합니다.- 시스템 디스크는 SATA(AHCI) 로 둡니다. virtio-blk/scsi 로 하면 Windows Setup 이 디스크 자체를 못 봐서 WinPE 단계 드라이버 주입이 필요해집니다. 네트워크·화면만 virtio 로 가고, 그 드라이버는 설치 후 ISO 에서 오프라인으로 넣습니다.
- Windows ISO 는 UDF 입니다. ISO9660 층에는
README.TXT하나뿐이라xorriso로 열면 비어 보입니다.mount -t udf로 봐야 합니다. install.wim의 인덱스 1 이Windows 11 Enterprise LTSC 2024 Evaluation입니다(이미지 2개).wiminfo로 확인하고 박았습니다.- LTSC 에는 winget 이 없습니다. 스토어가 빠져 있어서 설치 파일을 직접 받습니다.
- Node.js 가 필요합니다.
act_runner의 host 모드는actions/checkout,upload-artifact같은 JS 액션을 PATH 의node로 돌립니다. 빠뜨리면 그 단계들만 조용히 실패합니다. - 러너는 서비스가 아니라 로그온 예약 작업이어야 합니다. 서비스로 돌리면 데스크톱 세션이 없어 GUI 테스트와 스크린샷이 전부 실패합니다.
VM 에 얼마를 줄까 — 실측 근거
vCPU 4개 / RAM 8GB / 디스크 80GB. 추측이 아니라 재보고 정한 값입니다.
CPU 는 4개에서 포화합니다. 더 줘도 빨라지지 않습니다.
파이프라인(음성→자막) 2코어 13초 · 4코어 10초 · 8코어 11초
테스트 190개 4코어 5.9초 · 8코어 5.8초
CI 작업 대부분이 단일 스레드라 코어를 늘려도 효과가 없습니다. 음성인식만 병렬인데 그것도 4개에서 멈춥니다.
RAM 8GB 는 Windows 11 이 쾌적하게 도는 최소선이고, 그 안에서 도는 음성인식+번역이 2~3GB 를 씁니다. 디스크 80GB 는 Windows 30GB + 모델 캐시
- 빌드 산출물 기준입니다.
VM 의 Windows 는 진짜 Windows 인가
맞습니다. 같은 Windows 이고 같은 바이너리입니다. 우리가 검증하려는 것들은 코드 경로가 동일합니다.
| VM 에서 | 비고 | |
|---|---|---|
| WASAPI 오디오 캡처 API | 동일 | 에뮬레이트된 사운드 장치를 잡을 뿐 API 는 같음 |
전역 단축키(RegisterHotKey) |
동일 | |
| 모니터 열거 API | 동일 | max_outputs=2 로 화면 2개를 줄 수 있음 |
| PyInstaller exe 빌드·실행 | 동일 | |
| CPU 명령어 집합 | 동일 | 중첩 KVM 이 호스트 CPU 기능을 그대로 노출 |
다른 점은 셋입니다.
- GPU 없음 — CUDA·VRAM 검증은
.9(리눅스)에서 합니다. 위 참고. - 실제 게임 소리 없음 — 게임을 띄워 진짜 캡처하는 건 못 합니다. 대신 음성 파일을 넣어 자막까지 나오는지 자동 검증합니다.
- 속도/지연이 다름 — 체감 지연 측정값은 실제 PC 기준이 아닙니다.
모니터 선택을 검증하려면 화면을 2개 줘야 합니다. --vga std 는 1개뿐입니다.
-device virtio-vga,max_outputs=2 # KVM 기동 확인 완료
Windows 쪽에 virtio-gpu 드라이버(virtio-win ISO)가 깔려 있어야 2개로 잡힙니다.
100% 부하에서도 괜찮은가 — 재봤습니다
VM 4 vCPU 까지는 안전하고, .9 의 CPU 를 늘리면 오히려 해롭습니다.
.9 안에서 VM 이 4코어를 100% 써도 영향은 작습니다.
부하 없음 VM 4코어 100%
테스트 5.9초 -> 6.1초 (+3%)
파이프라인 9.8초 -> 11.0초 (+12%)
문제는 호스트입니다. 물리 호스트는 i7-11700(8코어/16스레드)뿐인데
.5 에서 운영 컨테이너 19개가 돌고 있습니다. .5 의 응답 속도를 재봤습니다.
.9 가 한가할 때 616~628ms 기준
.9 가 4코어 사용 657~678ms +7% ← VM 이 100% 인 실제 상황
.9 가 8코어 전부 사용 1487~1491ms +140% ← 봇까지 동시에 바쁠 때
그래서 .9 의 vCPU 는 늘리면 안 됩니다. 늘릴수록 호스트 지분이 커져
.5 의 운영 서비스가 느려집니다. 지금 8개가 적정 상한입니다.
최악(8코어 전부)을 아예 막으려면 VM 을 CPU 상한과 함께 띄웁니다.
systemd-run --user --scope -p CPUQuota=400% -p CPUWeight=50 --unit win-ci qemu-system-x86_64 -enable-kvm ...
CPUWeight=50 은 봇과 경합할 때 VM 이 양보한다는 뜻입니다.
CPUQuota=200% 로 재봤을 때 .5 응답이 649~653ms 로 기준선과 같았습니다.
RAM 은 최악을 다 더해도 여유가 있습니다.
ejclaw 봇 (실측 최대) 7.2 GB
Windows VM 게스트 8.0 GB
QEMU 오버헤드 0.4 GB
GPU 작업 동시 실행 3.0 GB
tmpfs 워치독 경보선 3.0 GB
─────────────────────────────────────
최악 합계 21.6 GB / 30 GB (여유 8.4 GB)
결론: CPU·RAM 모두 증설 없이 진행하면 됩니다. RAM 만 여유를 두고 싶다면 30 → 38GB 까지는 호스트가 감당합니다(호스트 62.6GB, 여유 약 24GB). CPU 는 늘리지 마세요.
⚠️ 메모리 cgroup 함정 — 반드시 systemd-run --scope
에이전트 턴에서 그냥 qemu를 띄우면 VM이 봇의 cgroup 안에서 돕니다.
ejclaw.service는 MemoryHigh=16G이고 평상시 5G를 쓰므로, 8G짜리 VM이
그 안에 들어가면 상한에 부딪혀 봇이 OOM으로 죽습니다(전례 있음).
# 봇 cgroup 밖(app.slice 형제)에 VM 을 띄운다 — 실측으로 확인됨
systemd-run --user --scope -p MemoryMax=10G --unit win-ci qemu-system-x86_64 -enable-kvm -m 8192 ...
A vs B 비교
A: .9 안 중첩 VM |
B: Proxmox VM | |
|---|---|---|
| 만드는 사람 | 에이전트가 직접 | 사용자 |
| GPU | ❌ | ❌ (별도 GPU 추가 시 ⭕) |
| 속도 | 중첩이라 다소 느림 | 네이티브 |
.9 재부팅 시 |
같이 죽음 | 영향 없음 |
| RAM | .9의 30GB를 나눠 씀 |
독립 |
CI는 타임아웃이 30분인데 파이프라인이 27초라 속도 여유가 충분합니다. 먼저 A로 띄워보고, 부족하면 B로 옮기는 편을 권합니다.
선택지 B — 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 를 보내주시면 됩니다.