Files
networkSetup/docs/changes/2026-11-12-prerequisite-offline-setup.md
T
jychoi4andClaude Opus 5.5 4964f2ced4 feat: add prerequisites, offline prerequisite setup and version tabs
- EXE versions: prerequisite list with the built-in catalog, custom
  installers, download link checks and install file analysis; entries
  are added by the user (no automatic adding), one "추가" dialog with
  기본 목록 / 직접 추가 tabs
- offline prerequisite setup: single exe made from PrerequisiteSetup.exe
  (Windows task dialog, no prerequisites) plus every installer, hash
  checked before, while and after appending; optional WebView2 Runtime
  chosen in the save dialog; replaces an existing file only after the
  user agrees (CLI --replace)
- desktop: progress popup with 생성위치 열기, save dialog fix for
  verbatim paths, EXE 서명 and 설치파일 생성 as version tabs, start
  screen 도구 › 서명하기 placeholder
- CLAUDE.md: results are overwritten only with the user's consent
- test kit and scripts for Windows Sandbox and Hyper-V checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 16:26:24 +09:00

10 KiB

선행 설치 오프라인 설치기

2026-11-12. EXE 버전의 선행 설치 탭에 오프라인용 설치기 생성을 추가했다. 선행 설치 목록의 설치 프로그램을 모두 담은 실행 파일 하나를 만들고, 그 파일을 대상 PC에서 실행하면 목록을 위에서부터 감지·설치하며 진행 과정을 HTML 화면으로 보여 준다. 제품 파일을 포함한 EXE 빌드, 서명, 대상 PC 설치 시험은 범위 밖이다.

사용

  1. 설치 구성에서 버전을 열고 선행 설치 탭에서 오프라인용 설치기 생성을 누른다. 버전을 읽기만 하므로 잠긴 버전에서도 된다.
  2. 저장 위치를 고른다. 기본 이름은 <제품>-<버전>-Prerequisites.exe이고 같은 이름의 파일이 있으면 만들지 않는다.
  3. 팝업 창이 열려 전체 진행(설치 프로그램 확인 → 설치기에 넣기 → 다시 읽어 확인)과 항목마다의 단계를 보여 준다. 실행 중에는 취소 요청만 누를 수 있다.
  4. 끝나면 같은 팝업에 경로, 크기, SHA-256, 항목별 출처(지정한 파일·제작기 기본 제공 파일·이번에 다운로드)와 설치 시험: notRun, 또는 실패·취소 이유가 표시되고 닫기로 닫는다(2026-11-13, 처음의 탭 아래 패널을 사용자 요청으로 팝업으로 바꿈).
  5. 대상 PC에서 실행하면 관리자 권한을 요청하고, 항목별 상태(설치 필요·이미 설치됨·해당 없음)를 보여 준 뒤 설치로 차례로 설치한다.

CLI: cargo run -p package-builder -- offline-setup --project <.isproj> --version <버전> --output <새 .exe>.

결정

  • 사용자 결정(2026-11-12): 설치기 UI는 Rust + Tauri(WebView2)의 HTML 화면으로 만들고 WebView2 Runtime은 대상 PC에 있다고 가정한다. 없으면 설치기가 MissingWebView2 안내를 띄우고 종료하며 WebView2를 대신 설치하지 않는다.
  • 결과물은 단일 파일 설치 결정(4GB 미만)을 따른다. 설치기 원본 PrerequisiteSetup.exe 뒤에 설치 프로그램을 붙이는 형식이며, 폴더나 여러 파일로 나누지 않는다.
  • 오프라인용이므로 download 방식 항목도 설치기 안에 넣는다. 설치 프로그램은 지정한 파일 → 제작기의 SetupPrerequisites → 다운로드 주소 순으로 찾는다. bundled 항목의 파일이 없을 때 다운로드로 대신하지 않는다.
  • 실행 중인 설치 프로그램은 중단하지 않는다(절반 설치 방지). 중단 요청은 다음 항목 전에 적용하고, 실패한 항목 뒤의 항목은 설치하지 않는다. 재부팅은 안내만 한다.
  • 대상 PC의 감지는 레지스트리 값만 읽는다. 파일 버전 감지(fileVersion)는 설치기를 만들 때 UnsupportedPrerequisiteDetection으로 거부한다.
  • 설치기의 종료 코드는 Windows Installer 관례를 따른다: 0 성공(설치할 항목 없음 포함), 3010 재시작 필요, 1603 실패·시작 불가, 1602 사용자 취소·중단.

모델과 실행

  • 형식(package-domain::setup_bundle): [설치기 원본][설치 프로그램 1..n][manifest JSON][0 채움][꼬리 32바이트]. 꼬리는 원본 끝(설치 프로그램 시작), manifest 위치, manifest 길이(각 u64 LE)와 ISPRQ001이다. 파일은 8바이트 경계로 끝나며, 서명된 파일은 PE 보안 디렉터리의 인증서 표 앞까지 읽는다. manifest는 {format: 1, product: {name, version, manufacturer}, items: [{id, name, architecture, fileName, arguments, detect, rebootCodes, sha256, bytes, offset}]}이고 알 수 없는 필드를 거부한다. 항목은 원본 바로 뒤부터 빈틈없이 이어져 manifest 앞에서 끝나야 한다(DamagedSetup).
  • 만들기(package-infra::build_offline_setup): 입력 검사(NoPrerequisites, InvalidPrerequisites, UnsupportedPrerequisiteDetection) → 출력 검사(InvalidSetupOutput, OutputExists, MissingOutputFolder) → 원본 검사(MissingSetupStub, InvalidSetupStub: MZ가 아님·서명됨·이미 목록 있음) → 4GB 검사(SetupTooLarge) → 설치 프로그램 확보와 SHA-256·크기 확인(MissingPrerequisiteFile, PrerequisiteChanged, DownloadFailed, DownloadMismatch) → 출력 폴더의 임시 파일에 원본·설치 프로그램을 스트림 해시하며 쓰기(InputChanged) → manifest·꼬리 → 같은 리더로 다시 읽어 manifest와 원본·항목 해시 확인(ArtifactMismatch) → persist_noclobber. 다운로드는 시스템 임시 폴더에 받고 끝나면 지운다. 진행은 OfflineSetupProgress {stage, index, name, done, total}(바이트 수는 10진 문자열)로 알린다.
  • 다운로드(download_installer): check_download_link와 같은 WinHTTP 요청을 함수로 나눠 본문까지 받는다. HTTPS 이외로 이동하면 거부하고, Content-Length가 기록과 다르거나 받은 크기가 넘치면 즉시 멈추며, 실패·취소 시 받던 파일을 지운다. 기존 파일은 덮어쓰지 않는다.
  • 설치기(apps/prerequisite-setup, 실행 파일 PrerequisiteSetup.exe): 매니페스트 requireAdministrator, withGlobalTauri, 명령 setup_overview·start_install·cancel_install·finish_setup, 이벤트 setup-item·setup-finished·setup-close-blocked. 시작할 때 WebView2(HKLM 32비트 보기 또는 HKCU의 EdgeUpdate\Clients\{F3017226-…} pv) → 자기 파일의 목록 → Windows 아키텍처(IsWow64Process2)를 확인하고 실패하면 메시지 상자 후 1603으로 끝낸다. 작업 폴더(%TEMP%\PrerequisiteSetup-*)에 항목마다 <번호>\<파일 이름>으로 꺼내고, 공유 읽기만 허용한 핸들로 SHA-256을 다시 확인해 실행이 끝날 때까지 쓰기·삭제를 막는다. .exe는 직접, .msi는 System32\msiexec.exe /i, .msu는 System32\wusa.exe로 실행하며 실행 인수는 셸 없이 그대로 넘긴다. WebView2 데이터도 작업 폴더에 두고 종료할 때 지운다. 로그는 %TEMP%\PrerequisiteSetup-<UNIX 초>.log.
  • 데스크톱: Tauri 명령 build_offline_setup(version)(저장 대화상자 → session.busy → 진행 이벤트 offline-setup-progress, 단계·항목·퍼센트가 바뀔 때만 보냄), 취소는 기존 cancel_build. 화면 모듈 offline-setup.ts(팝업 renderOfflineSetupDialog, 전체 진행 offlineOverall).
  • 빌드: apps/prerequisite-setup은 컴파일할 때 ui/dist를 넣으므로 npm.cmd run build(새 build:setup 포함)가 cargo보다 먼저다. Check.ps1과 check 스킬의 순서를 fmt → 프런트엔드 → test → clippy → 실행 파일 빌드로 바꿨다. 관리자 권한 프로그램이라 테스트 빌드를 만들지 않는다(test = false). Publish-Desktop.ps1은 PrerequisiteSetup.exe를 InstallerStudio.exe 옆에 복사한다. 제작기는 원본을 실행 파일 옆에서만 찾는다(다른 폴더로 대신 찾지 않음). 개발 빌드는 제작기와 같은 --target-dir에 있어야 하므로 VS Code 빌드 작업(target/vscode)도 prerequisite-setup을 함께 빌드한다(2026-11-13, VS Code에서 띄운 제작기가 MissingSetupStub을 낸 문제 수정).

확인 결과

  • scripts/Check.ps1 전체 통과(fmt → npm ci·test·build → cargo test → clippy → 두 실행 파일 빌드). Rust 테스트 122개 통과, 6개 무시. 새 테스트는 도메인 8개(꼬리 왕복·손상 거부, 8바이트 채움, 인증서 표 위치, 항목 배치 검사, 파일 이름, 아키텍처 적용, 버전 비교, 종료 코드 판정), 인프라 단위 8개(추출·잠금·변조·손상·취소, 설치 프로그램 실행 방식, 감지 입력 거부, 다운로드 주소 거부와 기존 파일 보존, 기본 파일 이름), 통합 6개(지정 파일+기본 제공 파일로 만들고 다시 읽어 추출, 기존 출력 보존, 변경·누락 파일, 거부되는 목록, 원본 검사, 취소 시 잔여물 없음).
  • 무시 테스트 직접 실행: 레지스트리 읽기(이 PC의 CurrentBuild·DWORD·없는 키/값, 쓰기 없음), 기본 목록에서 가장 작은 설치 프로그램 실제 다운로드와 해시 일치·틀린 해시 거부, 다운로드 항목을 받아 설치기에 넣기. 모두 통과.
  • 프런트엔드 테스트 154개 통과(설치기 화면 8개, 오프라인 설치기 팝업 8개 포함). 팝업은 브라우저에서 앱 스타일로 진행·완료·실패 상태를 렌더링해 확인했다(실제 Tauri 창에서 버튼으로 연 시험은 아님). 데스크톱과 설치기 화면의 tsc·Vite 빌드 통과.
  • 실제 프로젝트로 생성: D:\egbim-setup\egbimExe.isproj 1.0.0(6개 항목, 모두 기본 제공 파일)을 CLI로 임시 폴더에 만들었다. 243,683,008바이트, SHA-256 22a9a63e5c8a3d82efb24af843785c98d45bec1683763df9e69971c61e8535a6, 약 23초. 프로젝트와 버전 파일의 해시는 생성 전후로 같았다. 시험 파일은 지웠다.
  • 관리자 권한 요구: 원본과 만든 설치기 모두 일반 권한 셸에서 시작하면 "requires elevation"으로 거부되어 매니페스트와 PE가 유효함을 확인했다. 서명 상태는 NotSigned.
  • 설치기 화면: 브라우저에서 Tauri 연결을 흉내 내 대기·진행·재시작 필요·실패 상태를 렌더링해 확인했고, 목록이 창 안에서 스크롤되도록 고쳤다. 실제 WebView2 창은 띄우지 않았다.
  • 실행하지 않음: 만든 설치기를 관리자 권한으로 실행(실제 설치), 대상 PC·VM 설치 시험, WebView2가 없는 PC, Arm64·x86 Windows, 데스크톱 앱에서 버튼을 눌러 보는 GUI 시험, 배포본(Publish-Desktop.ps1)으로 만든 설치기, 서명한 설치기 읽기.

증빙: 시험 결과 JSON

근거