실제 설치에서 드러난 것들이다. 셋 다 증상이 원인과 전혀 안 닮아서 기록해 둔다. 1) provision.ps1 이 1행부터 파싱 실패 - 'Missing closing }' Windows PowerShell 5.1 은 BOM 없는 .ps1 을 ANSI(CP1252)로 읽는다. 한글 주석의 UTF-8 바이트가 CP1252 에서 스마트따옴표(0x93/0x94 = " ")로 변하고, 파서가 그걸 문자열 시작으로 보고 뒤를 전부 삼킨다. -> build.sh 가 ISO 에 넣을 때 UTF-8 BOM 을 붙인다. 2) stage2.ps1 의 1행이 주석으로 인식되지 않음 agent 가 받은 본문에 BOM 이 있으면 Set-Content -Encoding UTF8 이 BOM 을 한 번 더 붙여 BOM 이 두 개가 된다. PowerShell 은 첫 개만 벗겨내고 남은 U+FEFF 때문에 '#' 이 줄 맨 앞이 아니게 된다. -> agent 가 TrimStart(U+FEFF), 서버는 charset=utf-8 을 명시한다. 3) Git 설치가 'Cannot convert System.Object[] to System.String' 로 실패 Log 가 Write-Output 이라 함수 안에서 부르면 그 함수의 반환값에 로그가 섞인다. Get-File 이 배열을 돌려주니 Start-Process 가 죽었다. -> Log 를 Write-Host 로 바꿨다. 덤으로 시리얼/HTTP 로그의 한글이 '?' 로 깨지던 것도 고쳤다 (SerialPort 기본 인코딩이 ASCII). 그리고 러너 토큰이 없어도 워크플로와 같은 단계를 VM 안에서 직접 돌리는 검증 절차를 stage2 에 붙였다.
65 lines
2.9 KiB
Bash
Executable File
65 lines
2.9 KiB
Bash
Executable File
#!/bin/bash
|
||
# win-ci Windows VM 을 처음부터 만든다 (.9 안 중첩 KVM, 무인 설치).
|
||
#
|
||
# ./build.sh ISO 내려받기 + 무인설치 ISO 굽기 + 디스크 생성
|
||
# ./start-vm.sh VM + 제어 서버 기동 (systemd 트랜지언트 유닛)
|
||
# ./boot-cd.sh reset "Press any key to boot from CD" 넘기기 (최초 1회만)
|
||
# ./status.sh 진행 상황
|
||
# ./shot.sh out.png 화면 스냅샷
|
||
#
|
||
# 사람이 클릭할 일은 없다. autounattend.xml 이 설치를 끝내고, provision.ps1 이
|
||
# virtio 드라이버를 넣고, agent.ps1 이 호스트에서 stage2.ps1 을 받아 개발도구와
|
||
# act_runner 를 깐다.
|
||
#
|
||
# 필요 패키지: qemu-system-x86 ovmf swtpm swtpm-tools xorriso
|
||
# 그리고 실행 계정이 kvm 그룹에 있어야 한다 (없으면 sg kvm -c '...' 로 감싼다).
|
||
set -euo pipefail
|
||
|
||
D="${WIN_CI_DIR:-/home/claude/win-ci}"
|
||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||
|
||
WIN_ISO_URL='https://go.microsoft.com/fwlink/p/?linkid=2289029' # Win11 Ent LTSC 2024 평가판(90일, 키 불필요)
|
||
VIRTIO_URL='https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso'
|
||
|
||
mkdir -p "$D/iso" "$D/serve" "$D/swtpm" "$D/unattend-root/setup"
|
||
|
||
fetch() { # url dest 최소크기MB
|
||
if [ -f "$2" ] && [ "$(stat -c%s "$2")" -gt $(( $3 * 1024 * 1024 )) ]; then
|
||
echo "이미 있음: $2"; return
|
||
fi
|
||
echo "다운로드: $2"
|
||
curl -fL --retry 3 -o "$2" "$1"
|
||
}
|
||
|
||
fetch "$WIN_ISO_URL" "$D/iso/win11-ltsc.iso" 4000
|
||
fetch "$VIRTIO_URL" "$D/iso/virtio-win.iso" 500
|
||
|
||
# 무인 설치 미디어. Windows Setup 이 붙어 있는 모든 드라이브 루트에서
|
||
# autounattend.xml 을 찾으므로 CD 로 물려주면 된다.
|
||
cp "$HERE/unattend/autounattend.xml" "$D/unattend-root/"
|
||
|
||
# ⚠️ .ps1 은 반드시 UTF-8 BOM 으로 넣는다.
|
||
# Windows PowerShell 5.1 은 BOM 이 없으면 현재 ANSI 코드페이지(en-US -> CP1252)로
|
||
# 읽는다. 한글 주석의 UTF-8 바이트가 CP1252 에서 스마트따옴표(“ ” ‘)로 변하고,
|
||
# PowerShell 은 그걸 문자열 시작으로 보고 스크립트 뒤를 전부 삼킨다. 증상은
|
||
# 엉뚱하게도 "Missing closing '}' in statement block" 이다. 실제로 이것 때문에
|
||
# 첫 설치의 provision.ps1 이 통째로 안 돌았다.
|
||
for f in "$HERE/unattend/setup/"*.ps1; do
|
||
out="$D/unattend-root/setup/$(basename "$f")"
|
||
printf '\xef\xbb\xbf' > "$out"
|
||
cat "$f" >> "$out"
|
||
done
|
||
xorriso -as mkisofs -V UNATTEND -J -joliet-long -R -o "$D/unattend.iso" "$D/unattend-root/"
|
||
|
||
# 호스트 제어 채널이 내려줄 스크립트
|
||
cp "$HERE/serve/stage2.ps1" "$D/serve/"
|
||
cp "$HERE"/*.sh "$HERE"/serve-host.py "$D/"
|
||
chmod +x "$D"/*.sh
|
||
|
||
[ -f "$D/win-ci.qcow2" ] || qemu-img create -f qcow2 "$D/win-ci.qcow2" 80G
|
||
# SecureBoot 용 - MS 키가 미리 들어 있는 변수 이미지여야 Windows 부트로더가 통과한다
|
||
[ -f "$D/OVMF_VARS.fd" ] || cp /usr/share/OVMF/OVMF_VARS_4M.ms.fd "$D/OVMF_VARS.fd"
|
||
|
||
echo
|
||
echo "준비 끝. 다음: $D/start-vm.sh 그리고 $D/boot-cd.sh reset"
|