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:
jychoi4 committed 2026-11-02 15:12:00 +09:00
1 parent 24b624301c
commit 388e1e3327
103 files changed
+13328 -5

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
param(
[Parameter(Mandatory = $true)][string]$Project,
[string]$Cli = (Join-Path $PSScriptRoot '..\target\debug\package-builder.exe'),
[switch]$Export,
[switch]$SignDevelopment
)
$ErrorActionPreference = 'Stop'
$projectPath = (Resolve-Path -LiteralPath $Project).Path
$cliPath = (Resolve-Path -LiteralPath $Cli).Path
$sampleRoot = Split-Path -Parent $projectPath
$reports = Join-Path $sampleRoot 'reports'
if (-not (Test-Path -LiteralPath $reports)) { New-Item -ItemType Directory -Path $reports | Out-Null }
$utf8 = New-Object System.Text.UTF8Encoding($false)
function Invoke-BuilderJson([string[]]$BuilderArguments, [string]$ReportPath) {
$result = & $cliPath @BuilderArguments
$code = $LASTEXITCODE
$json = $result -join "`n"
[System.IO.File]::WriteAllText($ReportPath, $json, $utf8)
if ($code -ne 0) { throw "package-builder failed ($code); report: $ReportPath" }
return ($json | ConvertFrom-Json)
}
# Use the existing Rust engine for planning, packaging, unpack verification and export.
# Packaging stays in the Rust engine. Optional signing is a separate post-build step.
$plan = Invoke-BuilderJson -BuilderArguments @('plan','release','--project',$projectPath,'--target','msix-direct') -ReportPath (Join-Path $reports 'plan.json')
Write-Output "Plan: $($plan.id); files: $($plan.files.Count)"
$mappingPath = Join-Path $reports 'file-mapping.json'
if (Test-Path -LiteralPath $mappingPath) {
$expected = Get-Content -LiteralPath $mappingPath -Raw -Encoding UTF8 | ConvertFrom-Json
$actualTargets = @{}
foreach ($file in $plan.files) { $actualTargets[$file.target] = $file }
foreach ($file in $expected) {
if (-not $actualTargets.ContainsKey($file.target)) { throw "ISM target missing from actual plan: $($file.target)" }
$actual = $actualTargets[$file.target]
# Compare normalized source paths too; filename matching alone is insufficient.
$actualSource = $actual.source -replace '^\\\\\?\\', ''
if ($actualSource -ine $file.preparedSource -or [uint64]$actual.bytes -ne [uint64]$file.bytes -or $actual.sha256 -ne $file.sha256) {
throw "ISM source/size mismatch: $($file.ismFileId)"
}
}
if ($plan.files.Count -ne ($expected.Count + 2)) { throw 'Unexpected payload count; expected ISM files plus two logo assets.' }
}
$result = Invoke-BuilderJson -BuilderArguments @('build','--project',$projectPath,'--plan-id',$plan.id) -ReportPath (Join-Path $reports 'build-result.json')
Write-Output "Build: $($result.status); signing: $($result.signing)"
Write-Output "Artifact: $($result.artifact)"
if ($SignDevelopment) {
& (Join-Path $PSScriptRoot 'Sign-DevelopmentMsix.ps1') `
-Release (Join-Path $sampleRoot ("releases\" + $result.buildId + '\release.json')) `
-Output (Join-Path $sampleRoot ('signed-development-' + $result.buildId))
}
if ($Export) {
$exportRoot = Join-Path $sampleRoot ('export-' + $plan.id)
$exportedPath = Invoke-BuilderJson -BuilderArguments @('export','--project',$projectPath,'--release',$plan.id,'--output',$exportRoot) -ReportPath (Join-Path $reports 'export-result.json')
Write-Output "Export: $exportedPath"
}