diff --git a/docs/superpowers/specs/2026-06-11-msix-store-build-design.md b/docs/superpowers/specs/2026-06-11-msix-store-build-design.md new file mode 100644 index 0000000000..372c2734c9 --- /dev/null +++ b/docs/superpowers/specs/2026-06-11-msix-store-build-design.md @@ -0,0 +1,177 @@ +# MSIX Microsoft Store Build — Design + +Date: 2026-06-11. Status: approved (design review with maintainer). +Planning/requirements background: `MSIX_STORE_HANDOFF.md` (repo root, +validated against Microsoft Learn docs and Store Policies v7.19). + +## Goal + +Produce an unsigned `.msix` of OrcaSlicer in CI, built from the same +Windows install tree as the NSIS installer, suitable for manual upload to +Partner Center. The Store re-signs with Microsoft's certificate, which +makes the Store build install cleanly on Smart App Control (SAC) machines +that block the unsigned NSIS installer. + +Everything is additive: existing build outputs (NSIS exe, portable zip) +are untouched, and every runtime behavior change is gated behind a +packaged-context check so classic builds behave exactly as today. + +## Decisions (already made) + +- Packaging path: hand-written `AppxManifest.xml` + `makeappx pack` over + the existing install tree. No CPack MSIX generator exists. +- Minimum Windows: 10 1903 (`MinVersion 10.0.18362.0`), declaring BOTH + virtualization elements (Win11 fine-grained exclusion + Win10 coarse + disable). Each OS honors the one it supports. +- Network plugin: keep the runtime downloader, do NOT bundle Bambu's + closed-source DLLs in the package. +- Config stays at the real `%APPDATA%\OrcaSlicer` via manifest + virtualization exclusions + `unvirtualizedResources` restricted + capability. No `data_dir` code changes. +- Submission is manual via Partner Center (first submission must be); + CI only produces the artifact. + +## Components + +### 1. `scripts/msix/` — packaging files (new) + +Precedent: platform packaging already lives under `scripts/` (flatpak). + +**`AppxManifest.xml`** — template with four substitution tokens: +`@MSIX_VERSION@`, `@MSIX_IDENTITY_NAME@`, `@MSIX_PUBLISHER@`, +`@MSIX_PUBLISHER_DISPLAY_NAME@` (all three identity strings must match +the Partner Center-assigned values exactly). +Key contents (exact schema/namespace details verified against the MSIX +schema reference during implementation; `makeappx`/WACK validate): +- `TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0"`. +- Capabilities: `runFullTrust` + `rescap:unvirtualizedResources`. +- Under `Properties`, both virtualization elements: + - `virtualization:FileSystemWriteVirtualization` with + `ExcludedDirectory = $(KnownFolder:RoamingAppData)\OrcaSlicer` + (Windows 11 fine-grained), and + - `desktop6:FileSystemWriteVirtualization = disabled` + (Windows 10 1903+ coarse fallback). +- `Application Executable="orca-slicer.exe" + EntryPoint="Windows.FullTrustApplication"` (launcher output name set + at `src/CMakeLists.txt:189`). +- Extensions: + - `windows.fileTypeAssociation` for `.3mf`, `.stl`, `.step`/`.stp`, + `.gcode`. Note: `.gcode` is opt-in in the classic build + (`associate_gcode`); manifest declarations are static, so it is + always declared. Declaring an FTA only adds OrcaSlicer to "Open + with" — the user controls defaults via Windows Settings, so this is + not a behavior regression. + - `windows.protocol` for `orcaslicer://`. + - `rescap3:MigrationProgIds` declaring `Orca.Slicer.1`. + KNOWN LIMITATION: the classic build writes its ProgID with a + leading space (`L" Orca.Slicer.1"`, `GUI_App.cpp:9119`), which + MigrationProgIds cannot express. Migration hand-off from existing + NSIS installs may therefore be partial; Windows shows both apps in + "Open with" and the user picks once. The legacy ProgID is NOT fixed + in this change (separate change with its own migration risk). + +**`assets/`** — committed PNGs generated once from +`resources/images/OrcaSlicer.svg`: Square44x44Logo, Square150x150Logo, +StoreLogo (50x50), plus standard scale variants. Committed rather than +generated in CI so the pack step has no extra tool dependencies. + +**`build_msix.ps1`** — single script, runnable in CI and locally (needs +Windows SDK for `makeappx`): + +1. Parse `version.inc` `SoftFever_VERSION` (`2.4.0-dev` → `2.4.0.0`; + MAJOR.MINOR.PATCH from the leading semver triplet, revision fixed + at 0 as the Store requires). +2. Stage: copy the install tree (`build/OrcaSlicer`) + token-substituted + manifest + `assets/` into a temp layout. +3. `makeappx pack` → `OrcaSlicer_Windows_MSIX_.msix`. + +Parameters: `-InstallDir`, `-OutputPath`, `-IdentityName`, `-Publisher`, +`-PublisherDisplayName` (identity defaults are obvious placeholders). NO signing — the Store +strips and re-signs; local testing uses Developer Mode loose-layout +registration instead. + +### 2. CI — `.github/workflows/build_orca.yml` + +One new step in the Windows job, after the install tree exists (adjacent +to the portable-zip step): run `build_msix.ps1`, upload the `.msix` as a +workflow artifact. Identity comes from repo variables +(`vars.ORCA_MSIX_IDENTITY_NAME`, `vars.ORCA_MSIX_PUBLISHER`, +`vars.ORCA_MSIX_PUBLISHER_DISPLAY_NAME`); when unset, +placeholder defaults still produce a valid artifact (not Store-uploadable, +which is fine pre-approval). The step must not fail the job when repo +variables are absent (forks). + +### 3. Runtime changes (Windows-only, packaged-context-gated) + +**New helper** — `bool is_running_in_msix()` in +`src/slic3r/GUI/GUI_Utils.{hpp,cpp}`: cached null-buffer +`GetCurrentPackageFullName` probe (`ERROR_INSUFFICIENT_BUFFER` ⇒ +packaged, `APPMODEL_ERROR_NO_PACKAGE` ⇒ not); constant `false` on +non-Windows. + +**Updater suppression (R4)** — Store apps must not self-update: + +- Skip the startup auto-check (`check_new_version_sf()` call at + `GUI_App.cpp:934`) when packaged. +- The manual "Check for updates" menu action (`MainFrame.cpp:2580`) + opens the Store listing (`ms-windows-store://pdp/?ProductId=...`, + ProductId from a build-time define fed by repo variable + `ORCA_MSIX_STORE_PRODUCT_ID`) instead of the download flow. If the + ProductId is not configured (pre-reservation builds), the menu action + shows a message that updates are delivered through the Microsoft + Store — it never falls back to the classic download flow. + +**Association suppression (R3)** — the manifest owns shell integration; +runtime registry writes are virtualized and invisible: + +- Early-return in `associate_files`, `disassociate_files`, + `associate_url`, `disassociate_url` when packaged. +- Hide the file-association checkboxes in Preferences + (`Preferences.cpp:982+`) in packaged context. +- Suppress any prompt/check that offers to (re)register associations + (e.g. `check_url_association` consumers) in packaged context — + implementation enumerates call sites. + +## What does NOT change + +- `data_dir` / config location code — `%APPDATA%\OrcaSlicer` everywhere. +- NSIS/CPack packaging, portable zip, all non-Windows builds. +- Bambu network plugin download flow (works against the real AppData + path; SAC behavior verified per the test plan). +- Classic-build association/updater behavior (gates are packaged-only). + +## Verification + +No meaningful unit-test surface (Windows shell + packaging behavior); +the PR documents manual verification per repo review guidelines: + +1. Local loose-layout install (Developer Mode, + `Add-AppxPackage -Register AppxManifest.xml` over the staged layout): + app launches; `.3mf` open-with and `orcaslicer://` activation work; + single-instance hand-off works when launched via the package alias. +2. Config interop: profiles created by a classic install are visible in + the packaged app and vice versa (Win11 machine); profiles survive + packaged-app uninstall. +3. Updater: no startup check; menu action opens Store listing. +4. Network plugin: download + load succeeds in packaged context. +5. WACK run against the CI artifact passes (or failures triaged). +6. CI: Windows job produces the `.msix` artifact with correct version; + NSIS and portable artifacts byte-identical in content to before + (spot-check). +7. Post-certification only: SAC end-to-end test via private-audience + listing (cannot be done locally; Store signature is the variable + under test). + +## Sequencing / rollout + +The PR lands before Partner Center account approval — placeholder +identity builds a valid artifact. Once the account clears and the app +name is reserved, set the three repo variables (identity name, publisher +`CN=`, ProductId); the next CI run produces the uploadable +package. First Store submission is manual (listing, IARC, +`runFullTrust` + `unvirtualizedResources` justifications) per the +handoff doc's "Submission process" section. + +Main external risk (tracked in handoff doc R2.5): Store approval of +`unvirtualizedResources` for a non-game app is not guaranteed; fallback +is documented there and does not change this design's code shape.