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
This commit is contained in:
1 parent
24b624301c
commit
388e1e3327
103 files changed
+13328
-5
No files matched your search
+162
@@ -0,0 +1,162 @@
|
||||
# Installer Studio — 구현 시작 버전
|
||||
|
||||
기존 MSIX 우선 설계를 실행 가능한 Rust/Tauri 프로그램으로 옮긴 0.1 개발 버전이다. 모든 소스와 잠금 파일은 이 디렉터리에 있다. **미서명 또는 개발자 자체 서명 MSIX를 생성하며 일반 배포 준비 상태를 부여하지 않는다.**
|
||||
|
||||
## 실행
|
||||
|
||||
Windows, Rust 1.94.0/MSVC 빌드 도구, Node.js 22.12 이상, WebView2가 필요하다. 실제 패키징에는 Windows SDK의 MakeAppx.exe가 필요하다. SDK·인증서는 프로그램이 자동 설치하지 않는다.
|
||||
|
||||
```powershell
|
||||
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의 파일 선택 대화상자와 현재 프로젝트 세션을 사용한다.
|
||||
|
||||
실행 파일을 빌드하려면:
|
||||
|
||||
```powershell
|
||||
# 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 공식 디버깅 안내](https://v2.tauri.app/develop/debug/vscode/)를 따른다.
|
||||
|
||||
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 샘플에 레지스트리 설정을 자동 추가하지 않았으며, 새 기능을 사용한 프로젝트는 다시 빌드해야 한다. [설정 예시와 실제 실행 시험 기록](../docs/changes/2026-10-22-msix-registry-editor.md)
|
||||
|
||||
## CLI
|
||||
|
||||
아래 명령은 `src`에서 실행한다. stdout은 JSON, 오류는 stderr JSON이다. 개발자 서명 모드는 실제 MSIX 서명을 수행한다. 앱 설치와 PC의 인증서 신뢰 등록은 자동 수행하지 않는다.
|
||||
|
||||
```powershell
|
||||
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`으로 보관한다.
|
||||
|
||||
```json
|
||||
{
|
||||
"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 개별 서명, 공인 서명 서비스, 타임스탬프, 자동 설치 시험은 아직 제공하지 않는다. [구현 및 검증 기록](../docs/changes/2026-10-23-developer-signing.md)
|
||||
|
||||
## EG-BIM 샘플의 개발용 서명 후처리
|
||||
|
||||
기존 미서명 릴리스에는 `0x800B010A` 설치 오류에 대응하여 추가했던 `scripts/Sign-DevelopmentMsix.ps1`을 계속 사용할 수 있다. **제작기로 만든 검증된 미서명 릴리스**의 해시를 확인하고 복사본에 Windows SDK SignTool로 서명한다. 새 빌드에는 위의 통합 개발자 서명 모드를 사용하며, 이미 서명된 릴리스에 후처리를 중복 적용하지 않는다.
|
||||
|
||||
```powershell
|
||||
.\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의 기본 검증](https://learn.microsoft.com/en-us/windows/msix/package/create-app-package-with-makeappx-tool)을 끄지 않고 검사한다. Tauri 명령은 [AppManifest와 capability 권한](https://v2.tauri.app/security/capabilities/)으로 main 창에 한정한다.
|
||||
|
||||
## 검사와 시험 앱
|
||||
|
||||
```powershell
|
||||
# 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 검증 기록](tests/verification-2026-10-19.json)에 실제 도구 해시, 시험 결과, 로컬 생성물 해시와 미실행 항목을 보관한다. 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를 완료로 표시하지 않는다.
|
||||
Reference in new issue
Block a user