feat: add EXE projects with versioned install configuration
- EXE project kind next to MSIX: formats.exe, new EXE projects, EXE app info (architecture, UpgradeCode), per-kind workspace; EXE builds and signing are still refused explicitly (#9). - Install configuration tabs: registry grid, shortcuts and installer properties with formatted-text checks (#10). - Per-version .isver files: version list, add dialog, lock/unlock, delete with .isver.bak backups, description (#11). - Major/patch versions with cumulative patch checks and automatic kind selection (#12). - 설치화면 tab: setup steps, theme and scripts per version (#13). - Preferences: theme and display language (#14). - 설치파일 tab as an Explorer-style tree: per-row add/remove, Windows folder install locations (FileRule.targetRoot), picked files placed with FileRule.sourceFolder, unsaved-rule file preview (#17). Checks: cargo fmt/clippy/test, 82 frontend tests, Vite build and the msix-studio build pass. npm ci, GUI click tests and real EXE builds were not run. Refs #9 #10 #11 #12 #13 #14 #17 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
905e6d4eaa
commit
366c8fe173
69 files changed
+9295
-441
No files matched your search
@@ -2,6 +2,17 @@
|
||||
|
||||
2026-10-19 실행 범위 변경: JSON 스키마 버전은 v2를 유지한다. 첫 구현은 formats.msix와 direct 대상만 활성화하고 formats.exe는 생략 가능하다. v1 EXE 가져오기·Store 출력은 후속이며 미지원 입력을 명시적으로 거부한다. 현재 예제는 [msix.package-project.json](examples/msix.package-project.json)이다. [현재 설계](12-msix-first-builder.md) 참조.
|
||||
|
||||
2026-11-09 프로젝트 유형 분리: 한 `.isproj`는 `formats.msix`와 `formats.exe` 중 **정확히 하나**를 가진다. 있는 형식이 프로젝트 유형(MSIX/EXE)이며 GUI 시작 화면과 CLI `init --kind`에서 정한다. 둘 다 있거나 둘 다 없는 파일은 `InvalidProjectKind`로 열지 않고, 저장으로 유형을 바꾸면 `ProjectKindChanged`로 거부한다. EXE 대상은 `format: exe`, `delivery: offline-singlefile`, `distribution: direct`만 허용한다. EXE 빌드·서명은 미구현이며 계획 생성에서 `UnsupportedFormat`으로 거부한다.
|
||||
|
||||
2026-11-09 EXE 버전별 메타파일: EXE 프로젝트는 공유 설정과 버전별 내용을 나눈다.
|
||||
|
||||
| 파일 | 내용 |
|
||||
| --- | --- |
|
||||
| `<이름>.isproj` (`formats.exe`) | `architecture`(x64·x86), `upgradeCode`(생성 시 대문자 `{GUID}`, 모든 버전 공유), `installScope`(`perMachine`만), `installRoot`(`[ProgramFiles64Folder]` 또는 x86의 `[ProgramFilesFolder]` + 안전한 상대 경로), `properties`(사용자 속성 `{name, value}`; 기본 속성은 `package_domain::BUILTIN_PROPERTIES`, 해석·치환은 `parse_formatted`·`expand_formatted`). 최상위 `files`·`registry`는 비어 있어야 한다(`VersionedContent`). 템플릿: [`ExeInstallerProject.isproj`](../../src/fixtures/ExeInstallerProject.isproj) |
|
||||
| `<이름>.versions/<버전>.isver` (`ExeVersion`) | `version`(Major.Minor.Patch, 파일 이름과 같아야 함), `description`(제작자 메모, 1000자 이하, 없으면 빈 문자열, 설치 화면에 표시 안 함, `DescriptionTooLong`), `productCode`(대문자 `{GUID}`, UpgradeCode와 달라야 함. 메이저 버전마다 새 값, 패치 버전은 라인 메이저 버전과 같은 값), `updateType`(`major` 기본 / `patch`, [15 EXE 버전과 누적 패치](15-exe-versions-and-patches.md)), `locked`(사용자가 잠금·해제), `files`(파일 규칙), `registry`, `shortcuts`(`{id, location, folder, name, target, arguments}`; `target`은 그 버전의 설치 파일 중 하나, `arguments`는 서식 문자열). 스키마: `schemas/exe-version.schema.json`, 예제: [`exe.1.0.0.isver`](examples/exe.1.0.0.isver) |
|
||||
|
||||
새 EXE 프로젝트는 버전 없이 시작하며, 첫 버전은 사용자가 번호를 지정해 빈 내용으로 만든다. 이후 버전은 선택한 버전을 복사하며, 메이저 버전은 새 ProductCode를 받고 패치 버전은 아래쪽 메이저 버전의 ProductCode를 이어받는다. 검증은 다른 버전과의 관계(`validate_version_line`: ProductCode 재사용, 패치 기준 잠금, 이전 버전 내용 제거·이동 금지)도 검사한다. 저장은 버전 파일마다 읽은 시점의 해시를 비교하고(`RevisionConflict`), 잠긴 버전의 내용 변경(`VersionLocked`)과 패치가 ProductCode를 이어받는 메이저 버전의 삭제(`VersionInUse`)를 거부한다. 화면은 잠긴 버전의 삭제 버튼을 숨기며, 잠금 해제와 삭제를 한 번에 저장하는 것은 허용한다. 바꾼 파일만 `.isver.bak`을 남기고 다시 쓰며, 삭제한 버전 파일은 `.isver.bak`으로 옮긴다. 검증은 공유 설정(`validate`)과 선택한 버전(`validate_version`, 필드 경로 `version.*`)을 함께 검사한다. 이전 단일 버전 EXE 형식(`msiVersion`·`productCode`·`shortcuts`가 `formats.exe`에 있는 파일)은 자동 변환하지 않고 열기를 거부한다. 버전 파일의 `installerUi`(설치 단계·화면 문구·모양·스크립트)는 [14 EXE 설치 UI](14-exe-installer-ui.md#제작기-설치화면-편집-2026-11-09)를 따르며, 없으면 표준 마법사로 연다. 아래 범용 예제의 나머지 EXE 필드(prerequisites, patchPolicy, installationFlow)는 아직 모델에 없으며 알 수 없는 필드로 거부한다.
|
||||
|
||||
**현재 계약은 v2다.** [범용 출력 계약](11-generic-exe-msix-architecture.md)의 `formats`와 `targets`를 사용한다. 아래 MSI 컴포넌트·RegistryEntry·PatchRecord는 EXE 백엔드 전용 타입이며 MSIX 프로젝트의 필수 필드가 아니다.
|
||||
|
||||
[범용 v2 예제](examples/generic.package-project.json)는 두 형식을 요청하는 설계 초안이다. null인 UpgradeCode·Publisher·서명 참조, 실제 payload·로고·런타임을 확정하기 전 빌드할 수 없다. 최소 OS와 템플릿 이름도 예시이며 구현된 JSON Schema는 아직 없다. 아래 EG-BIM 예제는 이전 v1 입력으로 보존하고 백업 후 명시적으로 가져온다.
|
||||
@@ -38,7 +49,7 @@ project/
|
||||
| PackageProject v2 | schemaVersion, product, sources, files, formats, targets; 형식별 입력 분리. 별도 프로젝트 ID 없음 |
|
||||
| InstallationFlow / InstallerUi | 단계 의존성·모드·조건, 테마/페이지/바인딩. [상세 계약](09-install-flow-and-ui.md) 참조 |
|
||||
| ProductSpec | displayName, manufacturer, displayVersion; 제품별 선택 메타데이터. 설치 범위·GUID는 formats.exe, MSIX identity는 formats.msix |
|
||||
| FileRule | 고유 id, source alias, include/exclude 패턴, targetRelativePath, required; v1 destinationDirectory는 형식별 루트와 상대 경로로 변환 |
|
||||
| FileRule | 고유 id, source alias, sourceFolder(선택, 패턴이 시작하는 소스 안 폴더, 빈 값이면 생략), targetRoot(선택, EXE만, 설치 폴더 대신 쓸 Windows 폴더 속성 — `TARGET_ROOTS`, 빈 값이면 생략), include/exclude 패턴, targetRelativePath, required; v1 destinationDirectory는 형식별 루트와 상대 경로로 변환 |
|
||||
| ResolvedFile | source-relative path, target-relative path, 크기, SHA-256, PE 정보, signingPolicy; EXE 전용 확장에 componentId/Guid |
|
||||
| RegistryEntry | id, hive, view, key, name, typed value, component, 설치/제거 정책 |
|
||||
| IdentityLock | 형식별 계열. EXE: ProductCode·컴포넌트 GUID·tombstone; MSIX: Name·Publisher·Architecture·버전 이력 |
|
||||
|
||||
Reference in new issue
Block a user