Skip to content

Commit 99d4b1d

Browse files
feat: one-command bootstrap (bootstrap.ps1 + mur doctor/upgrade + dotnet-tool mur) (#425)
* feat(mur): pack as dotnet global tool + doctor and upgrade verbs Lays the CLI groundwork for one-command bootstrap: - Reactor.Cli.csproj now packs as a dotnet global tool (PackAsTool, ToolCommandName=mur, PackageId=Microsoft.UI.Reactor.Cli), so `dotnet tool install -g` puts `mur` on PATH cross-shell with no per-arch $env:Path edits. The legacy bin/<arch>/ mirror is kept for existing PATH-based installs. - `mur doctor` — verifies .NET 10+ SDK, mur resolvability, local-nupkgs/ feed currency, the reactorapp template registration, and the Claude plugin. Each FAIL prints a copy-pasteable remediation. - `mur upgrade` — re-packs the in-source framework + ProjectTemplates, reinstalls the template (uninstall-first to bypass the template-engine cache), and refreshes the Claude plugin. Does not update `mur` itself (a process can't replace its own binary mid-run); prints the bump-mur command on completion. - pack-local / --regen-api repo-root discovery now prefers CWD before AppContext.BaseDirectory, so a globally-installed `mur` (living under ~/.dotnet/tools) still resolves the source checkout the user is in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(bootstrap): one-command setup script for source checkouts bootstrap.ps1 collapses the six-step Setup in getting-started.md into a single command. It does pre-flight (.NET SDK check), packs `mur` as a global-tool nupkg, runs `dotnet tool install/update -g`, runs `mur pack-local` (framework + ProjectTemplates → local-nupkgs/), installs the `dotnet new reactorapp` template (uninstall-first to dodge the template-engine cache), and drops the Claude Code plugin under ~/.claude/plugins/reactor (symlink when allowed, copy fallback). Idempotent — safe to re-run after `git pull` to refresh everything. For a lighter-weight refresh that leaves `mur` itself in place, run `mur upgrade`. Flags: -SkipPlugin, -SkipMurInstall, -Configuration. Detects host arch from $env:PROCESSOR_ARCHITECTURE so the SignaturesGen apphost build step matches the machine. Prepends ~/.dotnet/tools to $env:Path in-process so the same shell can use `mur` immediately (no "open a new shell" beat). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(getting-started): rewrite Setup around bootstrap.ps1 + mur upgrade Replaces the six-step manual Setup (clone → build → PATH-munge → new shell → pack-local → template install + symlink dance) with two commands: `git clone` + `./bootstrap.ps1`. New sections: - "After `git pull`" — recommends `mur upgrade` (lightweight) vs. re-running `bootstrap.ps1` (also bumps the `mur` global tool). - "Verify the install" — points at `mur doctor`, which is the right first move when `dotnet new reactorapp` misbehaves. Updates the NU1101 caveat to reflect the new flow (re-run bootstrap or `mur upgrade`, not "re-run pack-local"), and notes that `mur upgrade` handles the template-engine cache for you. Regenerated docs/guide/getting-started.md from the edited template. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Quick start to README + Manual setup section to getting-started README "Quick start" was a one-line link to docs/guide; replace it with the actual `git clone` + `./bootstrap.ps1` + `dotnet new reactorapp` sequence so readers can copy-paste from the repo home page, then link to the manual walkthrough for readers who want the no-magic path. getting-started.md.dt gains a "Manual setup" section after "Verify the install" that walks through exactly what bootstrap.ps1 does, step by step: 1. dotnet --list-sdks (prerequisite check) 2. git clone + cd 3. dotnet pack src/Reactor.Cli (as global-tool nupkg) 4. dotnet tool install -g (+ in-process PATH prepend) 5. mur pack-local (framework + ProjectTemplates → local-nupkgs) 6. dotnet new uninstall + install (template, cache-bust) 7. symlink/copy plugins/reactor → ~/.claude/plugins/reactor 8. mur doctor verify Plus a "Refreshing after git pull" note (re-run steps 5 + 6; only do 3 + 4 when the CLI itself changed) and a "Why a global tool vs bin/<arch>/?" explainer pointing at the legacy mirror layout for users who'd rather PATH-mount the build output. Rendered docs/guide/getting-started.md mirrors the template addition. Re-running `mur docs compile` against the rest of the doc currently loses the inline todo/calculator code samples (snippet sources broken locally — pre-existing pipeline state, unrelated to this PR), so the rendered file was edited to add just the new Manual setup section rather than committing a degraded full-recompile. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(bootstrap): docs tier-drift, fail-fast SDK, harden upgrade RunDotnet CI fix: - Adding the "Manual setup" section pushed the existing element-reference table past the rendered page's midpoint, tripping REACTOR_DOC_TIER_005 ("no reference table found in the first half of the page") in both Docs tier-drift and Docs compile (--ci). Adds an 8-row "step → what it produces" summary table at the top of the Manual setup section — sits well before the midpoint and serves as a genuine cross-reference. `mur docs check-tier --topic getting-started` is now clean apart from the pre-existing W001 winui-ref warning. PR review feedback (Copilot): - DoctorCommand header: correct the exit-code contract (non-zero on FAILs only, not on WARNs) and align the "Checks (in order)" list with what the command actually emits (separate framework / template nupkg checks, repo-checkout warn behavior, `dotnet new` template check runs unconditionally). - DoctorCommand "skipping ... template checks" warn message: clarify that only the local-feed nupkg checks are skipped when run outside a source checkout — the `dotnet new` template check still runs. - UpgradeCommand.RunDotnet: wrap Process.Start in try/catch and handle a null return so `mur upgrade` fails gracefully with a clear message + non-zero exit when `dotnet` is missing from PATH, instead of bare null-deref via `Process.Start(psi)!`. - bootstrap.ps1: fail fast when no .NET 10+ SDK is detected, dumping the actual `dotnet --list-sdks` output, instead of printing a yellow warning and continuing into a later, less-actionable pack failure. Not addressed in this commit: - Adding unit tests for the doctor/upgrade verbs. Deferred — out of scope for the CI-fix turn. Captured in a follow-up below. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e476287 commit 99d4b1d

9 files changed

Lines changed: 1223 additions & 130 deletions

File tree

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,21 @@ Many of the experiments in this repo — the charting stack, accessibility valid
6969

7070
## Quick start
7171

72-
See **[docs/guide/getting-started.md](docs/guide/getting-started.md)** for the full walkthrough — prerequisites, building the framework and `mur` CLI from source, scaffolding your first app, and running through hello-world, a todo list, and a calculator with screenshots at each step.
72+
Reactor doesn't ship a signed NuGet yet, so you build the framework, the `mur` CLI, and the project template from source. `bootstrap.ps1` collapses that into a single command — ~3 minutes per machine.
7373

74+
```powershell
75+
git clone https://github.com/microsoft/microsoft-ui-reactor.git
76+
cd microsoft-ui-reactor
77+
./bootstrap.ps1
7478
79+
dotnet new reactorapp -n MyApp
80+
cd MyApp
81+
dotnet run
82+
```
83+
84+
`bootstrap.ps1` packs `mur` as a `dotnet tool` global install (cross-shell PATH, no per-arch `$env:Path` edits), packs the framework + project templates into `local-nupkgs/`, registers the `dotnet new reactorapp` template, and installs the Reactor agent plugin under `~/.claude/plugins/reactor`. Re-run it (or `mur upgrade` for a lighter refresh) after `git pull`. Verify a working install with `mur doctor`.
85+
86+
Prefer to wire it up by hand? **[docs/guide/getting-started.md](docs/guide/getting-started.md#manual-setup)** has a no-magic walkthrough of the exact `dotnet pack` / `dotnet tool install` / `dotnet new install` calls `bootstrap.ps1` makes, plus the full hello-world → todo → calculator tour.
7587

7688
---
7789

bootstrap.ps1

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
#requires -Version 5.1
2+
<#
3+
.SYNOPSIS
4+
One-command setup for a Microsoft.UI.Reactor source checkout.
5+
6+
.DESCRIPTION
7+
Builds the `mur` CLI, installs it as a dotnet global tool, packs the
8+
framework + ProjectTemplates into local-nupkgs/, installs the
9+
`dotnet new reactorapp` template, and (optionally) drops the Claude
10+
Code plugin under ~/.claude/plugins/reactor.
11+
12+
Idempotent — safe to re-run after `git pull` to refresh everything.
13+
For a less heavyweight refresh (mur stays put), run `mur upgrade`.
14+
15+
.PARAMETER SkipPlugin
16+
Skip installing the Claude Code plugin under ~/.claude/plugins.
17+
18+
.PARAMETER SkipMurInstall
19+
Build and pack the CLI but don't run `dotnet tool install/update`.
20+
Useful for CI or for users who manage tool installs externally.
21+
22+
.PARAMETER Configuration
23+
Build configuration for the CLI nupkg. Default: Release.
24+
25+
.EXAMPLE
26+
./bootstrap.ps1
27+
Full bootstrap.
28+
29+
.EXAMPLE
30+
./bootstrap.ps1 -SkipPlugin
31+
Skip the Claude plugin step.
32+
#>
33+
[CmdletBinding()]
34+
param(
35+
[switch]$SkipPlugin,
36+
[switch]$SkipMurInstall,
37+
[string]$Configuration = 'Release'
38+
)
39+
40+
$ErrorActionPreference = 'Stop'
41+
$repoRoot = $PSScriptRoot
42+
Set-Location $repoRoot
43+
44+
function Write-Step($msg) {
45+
Write-Host ''
46+
Write-Host "==> $msg" -ForegroundColor Cyan
47+
}
48+
49+
function Write-Ok($msg) {
50+
Write-Host " [ok] $msg" -ForegroundColor Green
51+
}
52+
53+
function Fail($msg) {
54+
Write-Host ''
55+
Write-Host "ERROR: $msg" -ForegroundColor Red
56+
exit 1
57+
}
58+
59+
# ---------------------------------------------------------------------------
60+
# 1. Pre-flight
61+
# ---------------------------------------------------------------------------
62+
Write-Step 'Pre-flight checks'
63+
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 }
73+
}
74+
}
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+
"@
82+
}
83+
Write-Ok ".NET SDK present"
84+
85+
# ---------------------------------------------------------------------------
86+
# 2. Pack `mur` as a global-tool nupkg
87+
# ---------------------------------------------------------------------------
88+
Write-Step "Packing mur (Reactor CLI) for dotnet global tool install"
89+
90+
$feed = Join-Path $repoRoot 'local-nupkgs'
91+
New-Item -ItemType Directory -Path $feed -Force | Out-Null
92+
93+
# Match host arch so the embed-resource step (which runs the SignaturesGen
94+
# apphost) succeeds. The packed IL itself is platform-portable.
95+
$hostArch = if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { 'ARM64' } else { 'x64' }
96+
97+
& dotnet pack (Join-Path $repoRoot 'src\Reactor.Cli\Reactor.Cli.csproj') `
98+
-c $Configuration `
99+
"-p:Platform=$hostArch" `
100+
-o $feed `
101+
--nologo -v:m
102+
if ($LASTEXITCODE -ne 0) { Fail 'dotnet pack failed for Reactor.Cli' }
103+
Write-Ok "Packed Microsoft.UI.Reactor.Cli -> $feed"
104+
105+
# ---------------------------------------------------------------------------
106+
# 3. Install / update the global tool
107+
# ---------------------------------------------------------------------------
108+
if ($SkipMurInstall) {
109+
Write-Host ''
110+
Write-Host ' Skipping `dotnet tool install` (per -SkipMurInstall).' -ForegroundColor Yellow
111+
} else {
112+
Write-Step 'Installing mur as a dotnet global tool'
113+
114+
$existing = & dotnet tool list -g 2>$null | Select-String -SimpleMatch 'microsoft.ui.reactor.cli'
115+
if ($existing) {
116+
& dotnet tool update -g --add-source $feed Microsoft.UI.Reactor.Cli --no-cache --ignore-failed-sources
117+
} else {
118+
& dotnet tool install -g --add-source $feed Microsoft.UI.Reactor.Cli --no-cache --ignore-failed-sources
119+
}
120+
if ($LASTEXITCODE -ne 0) { Fail '`dotnet tool install/update` failed for Microsoft.UI.Reactor.Cli' }
121+
122+
# Make ~/.dotnet/tools visible to the rest of this script even if this is
123+
# the first global tool the user has ever installed (dotnet adds it to the
124+
# User PATH but not the current process).
125+
$dotnetTools = Join-Path $env:USERPROFILE '.dotnet\tools'
126+
if (Test-Path $dotnetTools) {
127+
$pathParts = $env:Path -split ';'
128+
if ($pathParts -notcontains $dotnetTools) {
129+
$env:Path = "$dotnetTools;$env:Path"
130+
}
131+
}
132+
Write-Ok "mur installed as global tool (also on this shell's PATH)"
133+
}
134+
135+
# ---------------------------------------------------------------------------
136+
# 4. Pack the in-source framework + templates via the freshly-installed mur
137+
# ---------------------------------------------------------------------------
138+
Write-Step 'Packing local Microsoft.UI.Reactor + ProjectTemplates (`mur pack-local`)'
139+
140+
# Use the freshly-installed `mur` if available; otherwise call the source
141+
# project directly (works for -SkipMurInstall too).
142+
$murResolved = Get-Command mur -ErrorAction SilentlyContinue
143+
if ($murResolved) {
144+
& mur pack-local
145+
} else {
146+
& dotnet run --project (Join-Path $repoRoot 'src\Reactor.Cli\Reactor.Cli.csproj') `
147+
-c $Configuration `
148+
"-p:Platform=$hostArch" `
149+
--nologo `
150+
-- pack-local
151+
}
152+
if ($LASTEXITCODE -ne 0) { Fail 'mur pack-local failed' }
153+
154+
# ---------------------------------------------------------------------------
155+
# 5. Install the `dotnet new reactorapp` template
156+
# ---------------------------------------------------------------------------
157+
Write-Step 'Installing `dotnet new reactorapp` template'
158+
159+
$templateNupkg = Join-Path $feed 'Microsoft.UI.Reactor.ProjectTemplates.0.0.0-local.nupkg'
160+
if (-not (Test-Path $templateNupkg)) {
161+
Fail "Template nupkg not produced at $templateNupkg"
162+
}
163+
164+
# Uninstall first so the template engine drops its cached copy by id —
165+
# otherwise the previous install can win against a same-version repack.
166+
& dotnet new uninstall Microsoft.UI.Reactor.ProjectTemplates 2>$null | Out-Null
167+
& dotnet new install $templateNupkg
168+
if ($LASTEXITCODE -ne 0) { Fail '`dotnet new install` failed' }
169+
Write-Ok 'reactorapp template registered'
170+
171+
# ---------------------------------------------------------------------------
172+
# 6. Claude Code plugin (optional)
173+
# ---------------------------------------------------------------------------
174+
if ($SkipPlugin) {
175+
Write-Host ''
176+
Write-Host ' Skipping Claude plugin install (per -SkipPlugin).' -ForegroundColor Yellow
177+
} else {
178+
Write-Step 'Installing Reactor plugin for Claude Code'
179+
180+
$pluginSrc = Join-Path $repoRoot 'plugins\reactor'
181+
$pluginDst = Join-Path $env:USERPROFILE '.claude\plugins\reactor'
182+
183+
if (-not (Test-Path $pluginSrc)) {
184+
Write-Host " [skip] $pluginSrc not present in this checkout" -ForegroundColor Yellow
185+
} else {
186+
New-Item -ItemType Directory -Path (Split-Path $pluginDst) -Force | Out-Null
187+
188+
if (Test-Path $pluginDst) {
189+
Remove-Item $pluginDst -Recurse -Force
190+
}
191+
192+
# Prefer symlink so plugin edits in the checkout are immediately visible.
193+
# Falls back to copy when symlink creation is unprivileged (Developer
194+
# Mode off + non-admin shell).
195+
$linked = $false
196+
try {
197+
New-Item -ItemType SymbolicLink -Path $pluginDst -Target $pluginSrc -ErrorAction Stop | Out-Null
198+
$linked = $true
199+
} catch {
200+
# Fall through to copy.
201+
}
202+
203+
if ($linked) {
204+
Write-Ok "Symlinked $pluginDst -> $pluginSrc"
205+
} else {
206+
Copy-Item $pluginSrc $pluginDst -Recurse -Force
207+
Write-Ok "Copied $pluginSrc -> $pluginDst (re-run bootstrap or `mur upgrade` to refresh)"
208+
}
209+
}
210+
}
211+
212+
# ---------------------------------------------------------------------------
213+
# Done
214+
# ---------------------------------------------------------------------------
215+
Write-Host ''
216+
Write-Host 'Bootstrap complete.' -ForegroundColor Green
217+
Write-Host ''
218+
Write-Host 'Next:'
219+
Write-Host ' dotnet new reactorapp -n MyApp'
220+
Write-Host ' cd MyApp'
221+
Write-Host ' dotnet run'
222+
Write-Host ''
223+
Write-Host 'Other useful commands:'
224+
Write-Host ' mur doctor verify your install'
225+
Write-Host ' mur upgrade refresh local packages + plugin after `git pull`'
226+
Write-Host ' mur --help full command list'

0 commit comments

Comments
 (0)