Add winapp run --aot - #817
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Build Metrics ReportBinary Sizes
Test Results✅ 7222 passed, 37 skipped out of 7259 tests in 1094.2s (+44 tests, -252.4s vs. baseline) Test Coverage✅ 85.9% line coverage, 80% branch coverage · ✅ no change vs. baseline CLI Startup Time67ms median (x64, Try This BuildInstalls the MSIX for your architecture, replacing any previously installed build. Needs the GitHub CLI — the command offers to install it and sign you in if it is missing. & ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) 817Switching between builds often?Put the tool on your PATH once: & ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) -AddToPathThen this build is just: winapp-pr 817Run Updated 2026-09-18 00:17:41 UTC · commit |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a9a4773d-85be-4cc0-9cba-b5cbd7c7fa3b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
There was a problem hiding this comment.
🟡 Changes recommended
Toolchain readiness is not actually validated, exact publish inputs are lost during runtime dependency resolution, and verification/output can report incorrect success-state details.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds project-mode publishing and Native AOT verification to winapp run.
Changes:
- Adds
--publish,--verify-native-aot, and--dry-run. - Adds publish resolution, payload/runtime provenance checks, and packaged staging support.
- Updates tests, npm bindings, documentation, schema, and acceptance automation.
File summaries
| File | Description |
|---|---|
src/winapp-npm/test/winapp-commands-run.test.ts |
Tests new wrapper flags. |
src/winapp-npm/src/winapp-commands.ts |
Exposes publish/AOT options. |
src/winapp-CLI/WinApp.Cli/Services/ProjectRunService.Publish.cs |
Implements publish preparation and readiness. |
src/winapp-CLI/WinApp.Cli/Services/ProjectRunService.Platform.cs |
Supports concrete RID selection. |
src/winapp-CLI/WinApp.Cli/Services/ProjectRunService.cs |
Integrates operation-aware preparation. |
src/winapp-CLI/WinApp.Cli/Services/ProjectRunService.Arguments.cs |
Builds publish and restore arguments. |
src/winapp-CLI/WinApp.Cli/Services/PeHelper.cs |
Detects .NET single-file bundles. |
src/winapp-CLI/WinApp.Cli/Services/NativeAotVerifier.cs |
Verifies payload and process provenance. |
src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs |
Stages publishes and resolves runtime dependencies. |
src/winapp-CLI/WinApp.Cli/Services/IProjectRunService.cs |
Adds operation-aware service contract. |
src/winapp-CLI/WinApp.Cli/Services/IncrementalCopyHelper.cs |
Supports filtered staging copies. |
src/winapp-CLI/WinApp.Cli/Services/INativeAotVerifier.cs |
Defines verifier contract. |
src/winapp-CLI/WinApp.Cli/Services/IMsixService.cs |
Extends deployment options. |
src/winapp-CLI/WinApp.Cli/Models/ProjectRunModels.cs |
Adds preparation and publish models. |
src/winapp-CLI/WinApp.Cli/Models/NativeAotModels.cs |
Adds verification result models. |
src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs |
Registers the verifier service. |
src/winapp-CLI/WinApp.Cli/Commands/RunCommand.ProjectMode.cs |
Orchestrates publish and verification. |
src/winapp-CLI/WinApp.Cli/Commands/RunCommand.cs |
Adds CLI options and JSON output. |
src/winapp-CLI/WinApp.Cli.Tests/RunCommandTests.cs |
Preserves folder-mode JSON coverage. |
src/winapp-CLI/WinApp.Cli.Tests/RunCommandProjectModeTests.cs |
Tests new project workflows. |
src/winapp-CLI/WinApp.Cli.Tests/ProjectRunServiceTests.cs |
Updates build-resolution coverage. |
src/winapp-CLI/WinApp.Cli.Tests/PeHelperTests.cs |
Tests bundle detection. |
src/winapp-CLI/WinApp.Cli.Tests/NativeAotVerifierTests.cs |
Tests static and runtime verification. |
src/winapp-CLI/WinApp.Cli.Tests/MsixServiceIdentityTests.cs |
Tests staging and dependency handling. |
src/winapp-CLI/WinApp.Cli.Tests/FakeProjectRunService.cs |
Supports preparation test scenarios. |
src/winapp-CLI/WinApp.Cli.Tests/FakeNativeAotVerifier.cs |
Provides verifier test double. |
src/winapp-CLI/WinApp.Cli.Tests/FakeMsixService.cs |
Captures deployment options in tests. |
scripts/test-native-aot-run.ps1 |
Adds real-device acceptance automation. |
plugins/winapp/skills/winapp-setup/SKILL.md |
Documents the new workflow. |
plugins/winapp/com.github.copilot/agents/winapp.agent.md |
Updates agent command guidance. |
docs/usage.md |
Documents publish and verification. |
docs/npm-usage.md |
Updates generated npm API documentation. |
docs/guides/dotnet.md |
Adds Native AOT guidance. |
docs/cli-schema.json |
Adds generated option schema. |
Review details
- Files reviewed: 35/35 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
There was a problem hiding this comment.
🟡 Changes recommended
Recipe reconciliation can delete files from the still-registered layout before replacement staging succeeds.
Get a fresh assessment by requesting another Copilot review.
Review details
- Files reviewed: 29/29 changed files
- Comments generated: 1
- Review effort level: Balanced
Read ownership before staging, retain both old and completed copies on failure, and reconcile obsolete files only after all replacement mappings copy successfully. Extend the failure regression to preserve retired payload until a successful retry. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Packaged staging and ARM64 runtime behavior still require final human validation despite passing builds, focused tests, and an x64 AOT run.
Review details
- Files reviewed: 29/29 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Runtime follow-up for the latest review, against e7ac126: rebuilt the native CLI and exercised real authored-manifest AOT package staging, registration, and launch on this ARM64 Windows machine using both the ARM64 and x64 binaries. The launched apps themselves reported the expected Package.Current family identity, NativeAOT=True, and Architecture=Arm64 / X64 respectively. The registration used the staged publish\AppX layout. Temporary registrations were removed afterward. The 491 focused project, AOT, identity, path-safety, and copy tests also passed, including failed-copy ownership recovery, deferred stale deletion, and source-junction protection. This runtime exercise covers authored-manifest identity packages; it is not a new generated-WinUI runtime test. |
Zach Teutsch (zateutsch)
left a comment
There was a problem hiding this comment.
The only issue that surfaced during my review was from conflict with main. Not sure which implementation you want going forward here:
🔴 Must Fix
Rebase onto current main and reconcile the recipe-reconciliation designs
What is wrong
This branch forked from a Sept 11 commit. Since then main rewrote MsixService.CopyFilesFromRecipeAsync into an ownership-based model (LayoutReconciliation.Exact / PruneLayout / ReadAndValidateRecipe, with the MSIX and bundle callers now passing LayoutReconciliation.None). This PR independently rewrote the same method to use a .winapp-recipe-files state file with a different signature. The two designs cannot coexist.
Show me
git merge origin/main into the PR branch → 10 conflicts, including MsixService.Identity.cs, IncrementalCopyHelper.cs, RunCommand.cs, RunCommand.ProjectMode.cs, and MsixServiceIdentityTests.cs. In MsixService.Identity.cs, keeping the PR side as-is drops the LayoutReconciliation parameter the bundle/MSIX callers on main now rely on → those callers lose main's stale-file pruning behavior.
Why it matters
A mechanical "keep ours" conflict resolution regresses a reconciler that non-AOT packaging already depends on, and leaves winapp with two competing recipe-copy behaviors.
Smallest fix
Rebase onto current origin/main. In the recipe conflict, adopt main's LayoutReconciliation.Exact / PruneLayout ownership model as the base and thread the AOT-specific appxRecipe + NativeBinary entry-point check into it, rather than reintroducing .winapp-recipe-files.
Location — src/winapp-CLI/WinApp.Cli/Services/MsixService.Identity.cs (recipe copy), plus the other 9 conflicting files.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
…ne' may silently drop its earlier arguments' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Alexandre Zollinger Chohfi (azchohfi)
left a comment
There was a problem hiding this comment.
🤖 AI-generated review (winappcli pr-review skill) — verify before acting.
Reviewed at b146d531, i.e. after the merge with main, against merge-base 6ad70619.
This reads as close to ready. The --aot surface is coherent, the rejection paths behave correctly under --json and --quiet, and the solution builds clean at this commit. Three things worth a look — only the first is really a "should fix before merge":
PathSafety.HasReparsePointOnExistingPathhas no production caller. Main's layout-staging rework removed the call sites it was written for, so this PR now adds ~50 lines of dead code plus a test to maintain.- The AOT publish reads the first
{"Properties": ...}object on stdout. Unlike the evaluate pass,publishruns targets that can print before MSBuild's envelope, which can produce a false "Native AOT is not enabled" and hide the diagnostics that would explain it. - Shipped plugin guidance omits one
--aotrestriction (--manifest), so an agent can confidently suggest a command that always fails.
Details inline.
Exercised: dotnet build winapp.sln -c Debug at this commit (clean, 0 warnings); AOT unit tests (39/39 pass); --aot rejection paths (--no-build, --arch x86, --manifest, .cs input, folder input) under --json and --quiet, with stdout confirmed clean.
Not exercised: a real Native AOT publish of a packaged WinUI app — the recipe staging and staged-entry-point validation are covered here only statically and by their unit tests, so that path is still unproven on real hardware.
Zach Teutsch (zateutsch)
left a comment
There was a problem hiding this comment.
🤖 AI-generated review (winappcli pr-review skill) — verify before acting.
Decision: mergeable
Nice work. This builds clean (Release, warnings-as-errors, 0 warnings) and the core feature works end to end. I published winapp.exe and ran winapp run <proj> --aot against a throwaway unpackaged Native AOT console app on arm64: it published, native-compiled, launched, and printed output (exit 0). Every rejection path (--no-build, --manifest, x86, invalid --arch, PublishAot unset) fails with a clear, actionable, non-zero-exit message, and --json keeps stdout pure JSON. Unit tests pass.
One nice signal: the CLI's own direct dotnet publish failed on this machine with the vswhere error, while winapp run --aot succeeded — so BuildAotPublishEnvironment's PATH injection is doing real work.
No blockers. A few optional items are inline, plus the notes below.
Non-blocking (no inline anchor in the diff)
NuGet WinAppRunArgs docs imply --aot works there, but it can't. The NuGet target always runs winapp run on the build-output folder, and folder mode rejects --aot (see the inline note on RunCommand.cs). dotnet build -p:WinAppRunArgs="--aot" ends with --aot requires a .csproj, solution, or source directory that resolves to project mode. Worth a line in docs/dotnet-run-support.md that WinAppRunArgs is for folder-mode switches only.
Considered, intentionally not flagged as blockers
- Recipe UNC source touched during staging.
.build.appxrecipeIncludepaths reachFile.Exists/copy without rejecting UNC, which can trigger outbound SMB auth. This is pre-existing shared code (CopyFilesFromRecipeAsyncalready lives onmain), and running--aoton a hostile project already grants full RCE via MSBuild targets, so the marginal risk is negligible. Fine to leave; a separate hardening pass could reject network paths. - Two
NormalizePackagePathimplementations — the AOT preflight canonicalizes viaPath.GetFullPath/GetRelativePath, the layout path only trims slashes. I couldn't reproduce a real failure (actual WinUI AOT recipes emit canonical paths and tests pass), but unifying them would prevent future drift.
Not exercised
The packaged (MSIX loose-layout) AOT run and the x64 path weren't run here — this box is arm64, and the loose-layout + staged-entry-point validation is covered by unit tests only. One manual packaged run on x64/ARM64 before merge would close the last gap.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
Alexandre Zollinger Chohfi (azchohfi)
left a comment
There was a problem hiding this comment.
Approving. Re-reviewed at df867794; all three points from my earlier review are fixed and verified:
PathSafety.HasReparsePointOnExistingPathand its test are removed.- Property parsing now selects the final MSBuild envelope. I confirmed
dotnet msbuild --getPropertyemits undefined properties as"", so requiring all ofRequestedPropertiesstill matches the real envelope for an unpackaged app. - Both plugin surfaces now state the
--manifestincompatibility.
Verified locally: solution builds clean (0 warnings), 188 tests pass across the AOT, MsBuildPropertyReader, PathSafety, and run-command project-mode suites.
Two non-blocking notes, both yours to take or leave:
- The publish-output display filter still buffers to end-of-publish on a pretty-printed partial
Propertiesobject, and then prints the envelope it means to hide — details and a repro in theProjectRunService.Aot.csthread. Strictly better than the previous revision, and the trigger is contrived. - Nothing exercises
winapp run --aotend to end — the AOT tests drive aFakeDotNetService, and no sample or workflow passes--aot. So recipe staging and staged-entry-point validation ship on unit tests alone. Worth a sample as a follow-up rather than a merge condition.
🤖 Review assisted by the winappcli pr-review skill; findings were reproduced locally before reporting.
Stream redacted publish output directly instead of buffering JSON candidates. Add a real WinUI Native AOT sample regression covering recipe staging and the native entrypoint. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
Verify the returned process runs the staged native executable, creates a responding window, and survives startup. Clean up the exact test process and unique registration. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b7aaf4b-03f2-4a50-898f-74ff7160b51b
#817 (winapp run --aot) squash-merged into main, so main's single squashed commit conflicts with the individual #817 commits this branch was stacked on. This branch already contains all of #817's work plus the pack project-mode feature, so every conflict resolves to this branch's version: the shared AOT files here are the project-mode superset (PublishAndResolve split, native-MSIX/signing evaluation, exact-RID handling, WinAppManifestPath and signing RequestedProperties, and the command-neutral non-executable error message that pack and run now share). Main carried only the older run-only versions of those same lines. The @types/node bump and other main-only changes auto-merged cleanly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: dcabfb3c-9ea1-44c5-b91b-3b04f86a4289
Description
Adds a focused Native AOT mode to project and solution runs.
winapp run --aot; ordinarywinapp runremains a build/JIT workflow.PublishAot=trueand honors the selected configuration, framework, Windows RID/architecture, platform, publish profile, solution properties, and-pvalues.--manifest, and--aot --no-build.dotnet publishwithIncludePublishItemsOutputGroup=true, then launches the evaluatedTargetName.exefromPublishDir.Usage Example
For a one-time override, use
-p PublishAot=true.Related Issue
Related: #816
Type of Change
Checklist
plugins/winapp/skills/Screenshots / Demo
N/A — this is a CLI workflow.
Validation
AI Description
This section is auto-generated by AI when the PR is opened or updated. To opt out, delete this entire section including the marker comments.