2222. PARAMETER Configuration
2323 Build configuration for the CLI nupkg. Default: Release.
2424
25+ . PARAMETER InstallWinAppSdk
26+ Tri-state Windows App Runtime 2.0 install. When unspecified (default),
27+ prompt interactively (default no). Pass -InstallWinAppSdk to force-install
28+ non-interactively (useful for CI / one-shot dev-box setup); pass
29+ -InstallWinAppSdk:$false to skip the prompt silently. The framework
30+ defaults to self-contained, so the runtime is only required for
31+ framework-dependent deployment.
32+
2533. EXAMPLE
2634 ./bootstrap.ps1
27- Full bootstrap.
35+ Full bootstrap (prompts before installing WindowsAppRuntime) .
2836
2937. EXAMPLE
3038 ./bootstrap.ps1 -SkipPlugin
3139 Skip the Claude plugin step.
40+
41+ . EXAMPLE
42+ ./bootstrap.ps1 -InstallWinAppSdk -SkipPlugin
43+ Non-interactive: install everything (incl. WindowsAppRuntime) and
44+ skip the agent plugin. Suitable for CI / fresh-dev-box automation.
3245#>
3346[CmdletBinding ()]
3447param (
3548 [switch ]$SkipPlugin ,
3649 [switch ]$SkipMurInstall ,
37- [string ]$Configuration = ' Release'
50+ [string ]$Configuration = ' Release' ,
51+ # Windows App SDK runtime install: tri-state. When unspecified, prompt
52+ # interactively (default no) since the framework defaults to
53+ # WindowsAppSDKSelfContained=true and the machine runtime is only needed
54+ # for framework-dependent deployment. Pass -InstallWinAppSdk to force-
55+ # install non-interactively; pass -InstallWinAppSdk:$false to skip the
56+ # prompt and continue.
57+ [Nullable [bool ]]$InstallWinAppSdk = $null
3858)
3959
4060$ErrorActionPreference = ' Stop'
@@ -56,32 +76,107 @@ function Fail($msg) {
5676 exit 1
5777}
5878
79+ # Install a winget package and refresh $env:Path so the freshly-installed tool
80+ # is resolvable in this same shell. Hard-fails if winget itself is missing —
81+ # that's an OS-level prerequisite this script doesn't try to repair.
82+ function Install-WithWinget {
83+ param (
84+ [Parameter (Mandatory )][string ]$Id ,
85+ [string ]$Reason = $Id
86+ )
87+ if (-not (Get-Command winget - ErrorAction SilentlyContinue)) {
88+ Fail " Need to install '$Reason ' but winget is not on PATH. Install App Installer from the Microsoft Store, then re-run ./bootstrap.ps1."
89+ }
90+ Write-Host " Installing $Reason via winget ($Id )..." - ForegroundColor Yellow
91+ & winget install -- id $Id -- accept- source- agreements -- accept- package- agreements -- silent -- disable-interactivity
92+ if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne -1978335189 ) {
93+ # -1978335189 = APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE (already installed / up-to-date)
94+ Fail " winget install $Id failed (exit $LASTEXITCODE ). Install $Reason manually and re-run ./bootstrap.ps1."
95+ }
96+ # winget edits the Machine + User PATH but the current process keeps its
97+ # original. Rebuild $env:Path from the registry so subsequent commands in
98+ # this script can find the freshly-installed binaries.
99+ $env: Path = (
100+ [Environment ]::GetEnvironmentVariable(' Path' , ' Machine' ),
101+ [Environment ]::GetEnvironmentVariable(' Path' , ' User' )
102+ ) -join ' ;'
103+ }
104+
59105# ---------------------------------------------------------------------------
60106# 1. Pre-flight
61107# ---------------------------------------------------------------------------
62108Write-Step ' Pre-flight checks'
63109
64- $dotnetCmd = Get-Command dotnet - ErrorAction SilentlyContinue
65- if (-not $dotnetCmd ) {
66- Fail ' `dotnet` not found on PATH. Install the .NET 10+ SDK: https://dotnet.microsoft.com/download'
67- }
68- $sdkOutput = & dotnet -- list- sdks
69- $has10OrLater = $false
70- foreach ($line in $sdkOutput ) {
71- if ($line -match ' ^(\d+)\.' ) {
72- if ([int ]$Matches [1 ] -ge 10 ) { $has10OrLater = $true ; break }
110+ function Test-DotnetSdk10 {
111+ if (-not (Get-Command dotnet - ErrorAction SilentlyContinue)) { return $false }
112+ foreach ($line in (& dotnet -- list- sdks)) {
113+ if ($line -match ' ^(\d+)\.' -and [int ]$Matches [1 ] -ge 10 ) { return $true }
73114 }
115+ return $false
74116}
75- if (-not $has10OrLater ) {
76- Fail @"
77- .NET 10+ SDK not detected — Reactor requires 10 or later.
78- Installed SDKs:
79- $ ( $sdkOutput -join " `n " )
80- Install the latest .NET SDK from https://dotnet.microsoft.com/download and re-run ./bootstrap.ps1.
81- "@
117+
118+ if (-not (Test-DotnetSdk10 )) {
119+ if (-not (Get-Command dotnet - ErrorAction SilentlyContinue)) {
120+ Write-Host " [info] dotnet not found on PATH." - ForegroundColor Yellow
121+ } else {
122+ Write-Host " [info] dotnet present but no .NET 10+ SDK detected. Installed:" - ForegroundColor Yellow
123+ & dotnet -- list- sdks | ForEach-Object { Write-Host " $_ " - ForegroundColor Yellow }
124+ }
125+ Install-WithWinget - Id ' Microsoft.DotNet.SDK.10' - Reason ' .NET 10 SDK'
126+ if (-not (Test-DotnetSdk10 )) {
127+ Fail ' .NET 10 SDK install reported success but `dotnet --list-sdks` still does not show a 10.x entry. Open a new shell and re-run ./bootstrap.ps1.'
128+ }
82129}
83130Write-Ok " .NET SDK present"
84131
132+ # Windows App SDK runtime — optional but recommended.
133+ #
134+ # The framework defaults to WindowsAppSDKSelfContained=true (see
135+ # Directory.Build.props), so builds and scaffolded apps work *without* the
136+ # machine-wide runtime — every app's bin/ output ships its own copy of WAS
137+ # native binaries from NuGet restore.
138+ #
139+ # But many devs prefer framework-dependent deployment: smaller per-app
140+ # output, faster incremental builds, and the runtime installed once on the
141+ # machine. For that path the user needs the WindowsAppRuntime 2.0 install
142+ # matching our WindowsAppSDKVersion=2.0.1.
143+ #
144+ # So we prompt by default. `-InstallWinAppSdk` to force-install,
145+ # `-InstallWinAppSdk:$false` to skip the prompt non-interactively.
146+
147+ function Test-WindowsAppRuntime20 {
148+ if (-not (Get-Command winget - ErrorAction SilentlyContinue)) { return $true } # nothing we can check without winget
149+ & winget list -- id Microsoft.WindowsAppRuntime.2.0 -- exact 2> $null | Out-Null
150+ return ($LASTEXITCODE -eq 0 )
151+ }
152+
153+ if (-not (Test-WindowsAppRuntime20 )) {
154+ $shouldInstall = $false
155+ if ($null -ne $InstallWinAppSdk ) {
156+ $shouldInstall = [bool ]$InstallWinAppSdk
157+ if (-not $shouldInstall ) {
158+ Write-Host ' [skip] Windows App Runtime 2.0 not installed (skipped per -InstallWinAppSdk:$false).' - ForegroundColor Yellow
159+ }
160+ } else {
161+ Write-Host ' '
162+ Write-Host ' Windows App Runtime 2.0 is not installed on this machine.' - ForegroundColor Yellow
163+ Write-Host ' Reactor builds default to WindowsAppSDKSelfContained=true, so this is optional —'
164+ Write-Host ' your apps will work either way. Installing it enables framework-dependent'
165+ Write-Host ' deployment (smaller per-app output, faster builds) when you override'
166+ Write-Host ' WindowsAppSDKSelfContained=false in a consuming project.'
167+ $answer = Read-Host ' Install Windows App Runtime 2.0 via winget now? [y/N]'
168+ $shouldInstall = ($answer -match ' ^[Yy]' )
169+ if (-not $shouldInstall ) {
170+ Write-Host " Skipped. Re-run later with: winget install Microsoft.WindowsAppRuntime.2.0" - ForegroundColor Cyan
171+ }
172+ }
173+ if ($shouldInstall ) {
174+ Install-WithWinget - Id ' Microsoft.WindowsAppRuntime.2.0' - Reason ' Windows App Runtime 2.0'
175+ }
176+ } else {
177+ Write-Ok ' Windows App Runtime 2.0 installed'
178+ }
179+
85180# ---------------------------------------------------------------------------
86181# 2. Pack `mur` as a global-tool nupkg
87182# ---------------------------------------------------------------------------
0 commit comments