Files
networkSetup/src/README.md
T
jychoi4 388e1e3327 feat: implement Installer Studio MSIX authoring workflow
Add the shared Rust engine, Tauri UI and CLI, registry authoring, developer signing, branded start screen and .isproj persistence. Remove the unused project ID.

Validation: frontend build and tests, Rust workspace tests, native build and real Windows SDK package generation passed.

Closes #2
Closes #3
Closes #4
Closes #5
Closes #6
Closes #7
2026-11-02 15:12:00 +09:00

17 KiB
Raw Blame History

Installer Studio — 구현 시작 버전

기존 MSIX 우선 설계를 실행 가능한 Rust/Tauri 프로그램으로 옮긴 0.1 개발 버전이다. 모든 소스와 잠금 파일은 이 디렉터리에 있다. 미서명 또는 개발자 자체 서명 MSIX를 생성하며 일반 배포 준비 상태를 부여하지 않는다.

실행

Windows, Rust 1.94.0/MSVC 빌드 도구, Node.js 22.12 이상, WebView2가 필요하다. 실제 패키징에는 Windows SDK의 MakeAppx.exe가 필요하다. SDK·인증서는 프로그램이 자동 설치하지 않는다.

cd D:\networkSetup\src\apps\desktop
npm.cmd ci
npm.cmd run tauri dev

새 프로젝트를 만들거나 .isproj 파일을 연 뒤 앱 정보 → 파일 구성에서 소스 폴더 연결 → 패키지 설정 → 검증·빌드에서 SDK 도구 선택 → 입력 검사 → 빌드 계획 생성 → MSIX 빌드 순서로 사용한다. 포함/제외 규칙과 의존성은 프로젝트 JSON 편집에서 수정한다. 기본 프로젝트의 Publisher는 예시이며 자신의 패키지 identity에 맞게 바꿔야 한다.

시작 화면은 왼쪽 최근 프로젝트 목록, 오른쪽 프로젝트 열기 / 새 프로젝트 만들기 버튼으로 구성한다. 최근 목록은 이 버전의 GUI에서 열거나 만든 프로젝트를 최대 20개 보관하며 앱을 다시 실행해도 유지된다. 이름·경로 검색과 오늘/이번 주/이번 달/이전 그룹을 제공한다. 목록의 ×는 기록만 제거하며 프로젝트 파일을 삭제하지 않는다. 이동·삭제된 파일은 위치 확인 안내를 표시한다.

편집 화면 상단의 프로젝트 닫기로 시작 화면에 돌아간다. 미저장 변경이 있으면 저장 후 닫기 / 저장 안 함 / 취소를 선택하며, 저장 오류가 발생하면 프로젝트와 편집 내용을 유지한다. 다른 프로젝트 열기·새로 만들기에도 같은 보호 절차를 적용한다. 빌드 중에는 프로젝트를 닫을 수 없다.

최근 목록은 Tauri의 앱 데이터 폴더(기본 Windows 경로 %APPDATA%\dev.networksetup.msixstudio\recent-projects.json)에 저장한다. 프로젝트 경로·표시명·최근 열기 시각만 기록한다. 목록 저장 실패가 프로젝트 열기 자체를 차단하지는 않는다.

프런트엔드만 npm.cmd run dev로 실행하면 UI를 볼 수 있지만 파일 작업은 할 수 없다. 데스크톱은 임의 경로를 받는 파일/셸 플러그인 없이 Rust의 파일 선택 대화상자와 현재 프로젝트 세션을 사용한다.

실행 파일을 빌드하려면:

# src/apps/desktop
npm.cmd run build
cd ../..
cargo build --locked -p msix-studio --features custom-protocol
# src/target/debug/msix-studio.exe

VS Code 실행·디버깅

저장소 최상위 D:\networkSetup 폴더를 VS Code로 연다. 루트 .vscode 설정은 src의 Rust workspace를 연결한다. 확장 탭의 권장 확장인 **C/C++ (ms-vscode.cpptools)**와 rust-analyzer를 설치한다. Windows/MSVC용 cppvsdbg 구성은 Tauri 공식 디버깅 안내를 따른다.

  1. 실행 및 디버그 (Ctrl+Shift+D)에서 **Installer Studio: Desktop (Rust)**를 선택한다.
  2. src/apps/desktop/src-tauri/src/main.rs 또는 src/crates의 Rust 코드에 중단점을 설정하고 F5를 누른다.
  3. 프런트엔드 의존성 설치 → TypeScript/Vite 빌드 → Rust debug 빌드 후 앱이 디버거에서 실행된다. 빌드된 UI를 포함하므로 Vite 서버는 시작하지 않는다. UI 변경도 F5로 다시 빌드하여 반영한다.

데스크톱 F5는 잠금 파일 기준으로 npm ci를 실행하므로 기존 Vite/Tauri 개발 서버를 먼저 종료한다. Rust 코드를 변경하고 재실행할 때도 이전 디버그 세션을 종료한다. Ctrl+F5는 디버거 없이 실행하고, Ctrl+Shift+B는 데스크톱 빌드만 수행한다. 이 구성의 중단점 대상은 Rust이며 TypeScript/WebView 디버깅은 별도다.

VS Code 빌드/시험 산출물은 src/target/vscode에 분리한다. 기존 src/target/debug/msix-studio.exe를 실행 중이어도 디버그 빌드와 충돌하지 않는다. 최초 VS Code 빌드는 의존성을 별도로 컴파일하므로 시간이 더 걸릴 수 있다.

실행 구성 동작
Installer Studio: Desktop (Rust) MSIX 제작기 GUI와 Rust 공통 엔진 디버깅
MSIX CLI: Schema 추가 입력 없이 JSON Schema 출력; CLI 중단점 확인용
MSIX CLI: Validate project 입력한 JSON 프로젝트 검사
MSIX CLI: Plan release 프로젝트 경로와 대상 ID를 받아 계획 생성
MSIX CLI: Build plan 프로젝트 경로와 새 계획 UUID를 받아 실제 MSIX 생성

CLI 입력 경로는 절대 경로나 src 기준 상대 경로를 따옴표 없이 입력한다. 기본값 fixtures/InstallerProject.isproj은 Publisher가 미확정인 초안이므로 검증 오류를 확인하는 용도다. 실제 빌드는 소스/SDK가 연결된 프로젝트를 사용한다. 시험 앱을 생성했다면 .work/demo/FixtureAlpha/InstallerProject.isproj을 입력할 수 있다. 계획 ID는 Plan release 결과의 id를 사용한다.

명령 팔레트의 Tasks: Run Task에서 MSIX: test Rust를 실행할 수 있다. MakeAppx 통합 시험은 기존 scripts/Check.ps1 -MakeAppx ...로 명시 실행한다.

레지스트리 설정

레지스트리 탭에서 값을 추가·수정·삭제한 뒤 저장하고 새 빌드 계획을 생성한다. HKLM\Software 아래 앱 전용 설정, 기본값, 32/64비트 뷰, REG_SZ/REG_EXPAND_SZ/REG_DWORD/REG_QWORD/REG_MULTI_SZ/REG_BINARY를 지원한다. QWORD를 포함한 모든 데이터는 프로젝트 JSON의 문자열로 보관한다.

빌드 엔진은 Windows의 offreg.dll로 실제 registry.dat를 생성하고, 패키지 역추출 후 해시와 각 값을 다시 검증한다. 빌드 PC의 일반 레지스트리는 변경하지 않는다. 릴리스와 내보내기에 registry-report.json을 포함한다. 이전 v2 프로젝트에 registry 필드가 없어도 빈 설정으로 열린다.

HKCU 초기화·COM/파일 연결·ISM 가져오기와 MSI 속성 치환은 아직 지원하지 않는다. 기존 EG-BIM 샘플에 레지스트리 설정을 자동 추가하지 않았으며, 새 기능을 사용한 프로젝트는 다시 빌드해야 한다. 설정 예시와 실제 실행 시험 기록

CLI

아래 명령은 src에서 실행한다. stdout은 JSON, 오류는 stderr JSON이다. 개발자 서명 모드는 실제 MSIX 서명을 수행한다. 앱 설치와 PC의 인증서 신뢰 등록은 자동 수행하지 않는다.

cargo run -p package-builder -- init --project D:\packages\demo\InstallerProject.isproj
cargo run -p package-builder -- validate --project D:\packages\demo\InstallerProject.isproj
cargo run -p package-builder -- plan release --project D:\packages\demo\InstallerProject.isproj --target msix-direct
cargo run -p package-builder -- build --project D:\packages\demo\InstallerProject.isproj --plan-id <계획의-id>
cargo run -p package-builder -- job status --project D:\packages\demo\InstallerProject.isproj --id <계획의-id>
cargo run -p package-builder -- export --project D:\packages\demo\InstallerProject.isproj --release <계획의-id> --output D:\exports\new-folder
cargo run -p package-builder -- schema

프로젝트는 .isproj만 지원하며 내부 내용은 UTF-8 JSON v2다. 새 프로젝트의 기본 이름은 InstallerProject.isproj이고, 확장자를 생략하면 .isproj를 붙인다. 기존 .json 프로젝트의 열기·호환·변환은 제공하지 않는다. 기존 JSON 항목은 최근 목록에서도 숨긴다.

별도의 프로젝트 ID는 사용하지 않는다. MSIX 앱 계열은 패키지 설정의 Identity Name과 Publisher로 관리한다. 기존 projectId 필드가 포함된 개발용 파일은 자동 변환하지 않고 알 수 없는 필드로 거부한다. 현재 fixture와 샘플 생성 스크립트는 해당 필드 없이 프로젝트를 만든다.

프로젝트 옆 <프로젝트 파일명>.local.json에서 소스 alias와 SDK를 연결한다. 예를 들어 InstallerProject.isproj.local.json이다. 상대 소스 경로는 프로젝트 폴더 기준이다. 기존 package-project.local.json은 읽지 않으며 local 파일은 Git에 넣지 않는다. 저장 시 이전 프로젝트는 .isproj.bak, 이전 로컬 설정은 .isproj.local.json.bak으로 보관한다.

{
  "sources": { "appPayload": "payload" },
  "makeAppx": "C:/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0/x64/makeappx.exe"
}

소스는 .work, releases를 포함하거나 그 아래에 위치할 수 없다. 예를 들어 프로젝트 폴더 전체 대신 그 안의 payload 폴더를 연결한다. 실행 항목은 x86/x64/arm64 PE이며 패키지 아키텍처와 같아야 한다. 이 버전의 로고는 scale-100 기준 44×44, 150×150 PNG만 지원한다.

생성물은 .work/plans/<id>.json, .work/jobs/<id>/, releases/<id>/에 보존된다. 계획에는 프로젝트/local revision, 소스별 SHA-256, MakeAppx 경로·해시를 저장하고 실행 전에 재검사한다. 같은 계획은 다시 실행하지 않으며 실패 후에는 새 계획을 만든다. Ctrl+C로 CLI 빌드를 취소할 수 있다. 종료된 작업의 Running 상태는 job status에서 잠금 소유자가 없을 때 Interrupted로 복구한다.

종료 코드: 0 명령 성공, 2 입력/계획 오류, 4 도구 오류, 5 아티팩트/identity 오류, 6 잠금 충돌, 130 취소. 빌드 성공은 배포 가능 판정이 아니다.

개발자 서명 모드

서명 → 개발자 서명 · 테스트용 자체 서명을 선택하고 테스트 인증서 생성을 누른다. 기존 인증서를 사용할 때는 인증서 목록 새로고침 → 인증서 선택 → 선택한 인증서 사용 순서로 진행한다. Publisher와 정확히 일치하는 Subject, 유효 기간, 코드 서명 용도, 개인 키를 검사한다. MakeAppx.exe와 같은 폴더의 SignTool.exe가 필요하다.

프로젝트에는 "signing": { "mode": "development" }, 로컬 설정에는 developerCertificateThumbprint를 저장한다. 기존 프로젝트의 기본값은 unsigned다. 인증서를 선택하지 않은 개발자 서명 빌드는 오류로 중단하며 미서명으로 대체하지 않는다. CLI에서는 certificate --project <경로> --create, 기존 인증서 선택은 --thumbprint <지문>, 목록은 추가 옵션 없이 실행한다.

인증서는 현재 사용자 개인 저장소에 생성하고 3개월간 유효하다. 개인 키는 내보내지 않으며 다음 빌드에서 선택한 인증서를 재사용한다. 패키징 → MSIX 서명 → 서명 및 신뢰 검사 → 역추출·내용 검사 순서로 처리한다. 결과에 공개 인증서 development-signing.cer, signing-report.json, sign.log, 관리자용 Install-TestCertificate.ps1을 함께 출력한다. 설치할 테스트 PC에서 공개 인증서를 확인한 뒤 스크립트를 관리자 권한으로 실행하면 해당 인증서만 LocalMachine\TrustedPeople에 등록한다. 제작기는 이 스크립트를 자동 실행하지 않는다.

서명 성공과 PC 신뢰 여부는 별도로 표시한다. 자체 서명은 Microsoft의 게시자 신원 보증이 아니며 Windows 개발자 모드와도 별개다. 내부 EXE/DLL 개별 서명, 공인 서명 서비스, 타임스탬프, 자동 설치 시험은 아직 제공하지 않는다. 구현 및 검증 기록

EG-BIM 샘플의 개발용 서명 후처리

기존 미서명 릴리스에는 0x800B010A 설치 오류에 대응하여 추가했던 scripts/Sign-DevelopmentMsix.ps1을 계속 사용할 수 있다. 제작기로 만든 검증된 미서명 릴리스의 해시를 확인하고 복사본에 Windows SDK SignTool로 서명한다. 새 빌드에는 위의 통합 개발자 서명 모드를 사용하며, 이미 서명된 릴리스에 후처리를 중복 적용하지 않는다.

.\scripts\Sign-DevelopmentMsix.ps1 -Release '<프로젝트 폴더>\releases\<빌드 ID>\release.json' -Output '<새 서명 출력 폴더>'
# 기존 개발용 인증서를 재사용하려면 -CertificateThumbprint <지문> 추가
# 새 빌드부터 이어서 서명하려면 Build-EgbimMsixSample.ps1에 -SignDevelopment 추가

Publisher와 같은 Subject의 단기 개발용 인증서를 현재 사용자의 개인 저장소에 생성한다. 개인 키는 내보내지 않고 공개 CER만 출력한다. 출력에는 서명본, SignTool 로그, CMS 서명 검사 및 신뢰 검사 결과를 기록한다. 신뢰되지 않은 자체 서명 인증서는 SignTool verify가 실패하며 설치 가능한 상태로 표시하지 않는다. 설치하려면 테스트 PC의 관리자가 공개 CER를 LocalMachine\TrustedPeople에 등록해야 한다. 스크립트는 인증서 신뢰 등록이나 앱 설치를 수행하지 않는다.

구조

경로 역할
crates/package-domain 엄격한 v2 DTO, 진단, 경로/버전/대상 검증, manifest 생성
crates/package-engine 검증/계획 계약, 백엔드 포트, 파일·도구 스냅샷
crates/package-infra 스트림 해시/복사, 파일 수집, OS 잠금, 원자적 JSON 저장, MakeAppx, 패키지 재검사, 결과 내보내기
apps/cli 공통 엔진 CLI
apps/desktop TypeScript UI와 Tauri 명령·capabilities
fixtures, scripts 기본 프로젝트, 두 Win32 시험 앱 생성, 재현 가능한 검증 명령

Rust DTO에서 생성하는 JSON Schema는 구조와 알 수 없는 필드를 검증하는 용도다. 버전·대상·경로·파일 등의 의미 검증은 공통 엔진이 추가 수행한다. 실제 manifest XML은 MakeAppx의 기본 검증을 끄지 않고 검사한다. Tauri 명령은 AppManifest와 capability 권한으로 main 창에 한정한다.

검사와 시험 앱

# src에서 실행. 기존 폴더는 덮어쓰지 않는다.
.\scripts\New-Fixtures.ps1 -MakeAppx 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\makeappx.exe'
# .work/demo/FixtureAlpha와 FixtureBeta의 프로젝트를 앱에서 열 수 있다.

.\scripts\Check.ps1 -MakeAppx 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\makeappx.exe'

검사 스크립트는 npm ci로 의존성을 재설치하므로 먼저 실행 중인 Vite/Tauri 개발 서버를 종료한다. Windows에서 실행 중인 esbuild 파일은 교체할 수 없다.

cargo test는 일반 단위/통합 테스트를 수행한다. SDK 통합 테스트는 기본적으로 ignored이며 MSIX_TEST_MAKEAPPX를 지정하고 --ignored로 명시 실행한다. 이 테스트는 두 identity, 실제 MakeAppx pack/unpack, 원본 변경 후 계획 거부, 중복 빌드, 내보내기 무결성 및 취소를 확인한다. 설치 시험으로 취급하지 않는다.

프로젝트 닫기와 최근 목록 저장 테스트는 cargo test --workspace에 포함된다. 시작 화면의 날짜 그룹·검색·HTML 이스케이프와 미저장 변경 처리 테스트는 apps/desktop에서 npm.cmd test로 실행한다. scripts/Check.ps1도 두 검사를 모두 수행한다.

2026-10-19 검증 기록에 실제 도구 해시, 시험 결과, 로컬 생성물 해시와 미실행 항목을 보관한다. GUI 컴파일은 통과했지만 현재 자동화 연결을 사용할 수 없어 화면 클릭·시각 검사는 notRun이다.

현재 구현 범위와 다음 작업

  • 구현: workspace/공통 엔진/CLI/GUI, v2 열기·저장·백업·revision 충돌, 파일 포함/제외·정렬·충돌·위험 경로/reparse 차단, PE 아키텍처/PNG 확인, XML 이스케이프, 계획 고정, OS 잠금, Windows Job Object, 도구 타임아웃·취소, MSIX pack/unpack 및 identity/파일 해시 대조, 덮어쓰기 없는 개발 결과와 보고서 내보내기.
  • 다음: 운영 서명 프로필·타임스탬프, App Installer, 승인 SDK 버전 정책, 다중 대상 일괄 작업, 작업 이력/재연결 화면, 파일 목록 서버 페이지 처리, 접근성/DPI 전체 시험, Windows VM 설치·실행·업데이트·제거·사용자 데이터 보존 증빙.
  • EXE/MSI/MSP, Store, bundle, extension은 명시적으로 거부한다. 운영 서명 프로필/App Installer 요청도 구현 완료 전에는 차단한다.
  • 로컬 검증을 통과한 아키텍처/OS 값은 지원 인증을 의미하지 않는다. 도구 해시는 계획 시 고정하지만 승인된 SDK 목록/도구 버전 메타데이터 정책은 아직 없다.
  • 프로젝트와 local JSON은 각각 원자적으로 저장하고 이전 파일을 .bak으로 보관한다. 두 파일을 한 번에 교체하는 트랜잭션은 아니다. 작업 로그에는 로컬 경로가 포함되므로 결과 공유 시 검토한다.
  • 결과는 애플리케이션에서 덮어쓰지 않지만 OS 파일 권한으로 불변성을 강제하지는 않는다. 내보내기 전 MSIX 해시를 다시 검증한다.

백로그 BL-09/10/11/14/16/29/47/48/49/50의 일부를 구현한 상태다. 서명·VM 증빙을 포함한 원래 인수 기준이 남아 있으므로 해당 백로그와 MS6를 완료로 표시하지 않는다.