- 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>
4.9 KiB
4.9 KiB
오프라인 설치기 덮어쓰기
2026-11-13. 오프라인용 설치기 생성이 같은 이름의 기존 파일을 사용자가 동의했을 때 새 설치기로 바꾸게 했다. 기존에는 저장 창에서 기존 파일을 고르면 OutputExists로 멈췄다. 같은 날 "결과는 덮어쓰지 않는다" 원칙을 "결과는 사용자가 판단해 동의할 때만 덮어쓴다"로 바꿨다. 이 변경은 MSIX 빌드 결과와 내보내기에 동의 절차를 만들지 않았고, 프로젝트·소스 파일의 덮어쓰기를 허용하지 않는다.
결정
- 사용자 요청: "덮어 쓰기 가능하게 해줘"(
OutputExists오류 화면과 함께). - 원칙 변경: "결과는 사용자 판단에 의해 덮어쓰기 가능하게 해"(사용자 결정).
CLAUDE.md의 "결과는 덮어쓰지 않는다"를 "결과는 사용자가 판단해 동의할 때만 덮어쓴다"로 바꿨다. 동의가 없으면 기존 결과를 거부하고, 동의해도 새 결과가 완성된 뒤에 한 번에 바꾼다. 지금 동의 절차가 있는 것은 오프라인 설치기뿐이다. - 동의는 사용자가 직접 한다. GUI는 Windows 저장 창이 기존 파일을 바꿀지 묻고(
FOS_OVERWRITEPROMPT를 명시적으로 켬), 동의해야 그 경로를 돌려준다. CLI는--replace를 줄 때만 바꾸고, 없으면 지금처럼OutputExists로 거부한다. - 교체는 새 설치기가 완성된 뒤 한 번에 한다. 같은 폴더의 임시 파일에 쓰고 다시 읽어 SHA-256을 확인한 다음, 한 번의 이름 바꾸기(
MoveFileEx교체)로 기존 파일 자리에 놓는다. 입력 검사 거부, 설치 프로그램 변경·누락, 취소, 다시 읽기 실패에서는 기존 파일이 그대로 남는다. - 동의해도 바꾸지 않는 것: 같은 이름의 폴더(
InvalidSetupOutput), 이번 설치기를 만드는 데 쓰는 파일인 설치기 원본PrerequisiteSetup.exe와 담을 설치 프로그램 파일(OutputIsInput). 바꿀 파일이 실행 중이거나 읽기 전용이면OutputNotWritten으로 멈춘다.
고친 오류
- 같은 날 사용자 화면에서 오프라인용 설치기 생성이 "매개 변수가 틀립니다. (0x80070057)"로 멈췄다. 열린 프로젝트 경로는
canonicalize때문에\\?\D:\...형식인데, 저장 창의 시작 폴더를 만드는SHCreateItemFromParsingName은 이 형식을E_INVALIDARG로 거부한다(이 PC에서D:\networkSetup은0x00000000,\\?\D:\networkSetup은0x80070057로 확인).save_dialog.rs의shell_path가\\?\D:\…를D:\…로,\\?\UNC\server\share\…를\\server\share\…로 바꿔 넘기고, 실패하면SaveDialogFailed와 폴더를 알린다. 고친 뒤 실제 앱에서 저장 창을 여는 시험은 수행하지 않았다.
모델과 실행
- 프로젝트·로컬·버전 파일: 바뀌지 않는다.
- 엔진:
package_infra::build_offline_setup(doc, version, inputs, output, replace, cancel, progress).replace가 새 인수다.check_output은 기존 파일을replace일 때만 받고 폴더는 거부한다. 설치 프로그램 위치를 정한 뒤check_not_input이 출력과 원본·설치 프로그램의 정규 경로를 비교한다. 마지막 저장은replace면persist, 아니면persist_noclobber이다. - GUI:
build_offline_setupTauri 명령은 저장 창이 물은 뒤 받은 경로이므로replace = true로 호출한다. 명령 인자와 권한은 바뀌지 않는다. - CLI:
package-builder offline-setup ... --replace. - 새 오류 코드:
OutputIsInput.OutputExists와OutputNotWritten은 조치 문구를 바꿨다. - 범위 밖: 백업 파일(
.bak)을 남기지 않는다. 바뀐 기존 파일은 되돌릴 수 없다.
확인 결과
cargo fmt --all --check,cargo clippy --locked --workspace --all-targets -- -D warnings통과.- Rust 테스트 133개 통과, 7개 무시(
#[ignore]). 오프라인 설치기 통합 테스트 9개 통과, 1개 무시(네트워크 다운로드). 새 테스트:an_existing_output_is_refused_unless_the_user_agreed_to_replace_itan_agreed_replacement_happens_only_once_the_new_setup_is_completea_folder_the_setup_program_or_a_carried_installer_is_never_replaced
- 프런트엔드 테스트 149개 통과. TypeScript 검사와 Vite 빌드 통과.
- GUI에서 저장 창의 덮어쓰기 확인을 실제로 누르는 시험, CLI
--replace실제 실행, 실행 중인 파일을 바꾸는 시험은 수행하지 않았다. 만든 설치기는 설치 시험을 하지 않았다(installationTest: notRun).
증빙: 시험 결과 JSON