Skip to content

Add Reactor single-file app template (reactor-singlefile) - #6824

Closed
Alexandre Zollinger Chohfi (azchohfi) wants to merge 4 commits into
mainfrom
azchohfi-reactor-file-based-app-template
Closed

Alexandre Zollinger Chohfi (azchohfi) wants to merge 4 commits into
mainfrom
azchohfi-reactor-file-based-app-template

Conversation

@azchohfi

@azchohfi Alexandre Zollinger Chohfi (azchohfi) commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a Reactor Single-File App template to the Microsoft.WindowsAppSDK.WinUI.CSharp.Templates dotnet new pack, alongside the four Reactor templates from #6620. It scaffolds the blank Reactor (Microsoft.UI.Reactor) app as a single-file .NET 10 file-based app: one .cs file whose #:package / #:property directives replace the .csproj. This is the shape Reactor's getting-started guide documents under One file, no project (microsoft/microsoft-ui-reactor#1283).

Template Short names What it scaffolds
Reactor Single-File App (Experimental) reactor-singlefile, winui-reactor-singlefile The blank Reactor app as a single <name>.cs, with no .csproj, assets or manifest
dotnet new reactor-singlefile -n MyApp   # creates MyApp\MyApp.cs and nothing else
cd MyApp
dotnet run MyApp.cs                      # launches with package identity

The generated MyApp.cs with default parameters (the body is the same as the reactor blank app):

#:package Microsoft.UI.Reactor@0.1.0-preview.16
#:package Microsoft.WindowsAppSDK@*
#:package Microsoft.Windows.SDK.BuildTools.WinApp@*
#:property OutputType=WinExe
#:property TargetFramework=net10.0-windows10.0.26100.0
#:property UseWinUI=true

// A .NET file-based app: the #: directives above take the place of a .csproj.
// `dotnet run MyApp.cs` launches it with package identity (needs Developer Mode).
// To run it unpackaged instead, add `#:property WindowsPackageType=None` above.

using Microsoft.UI.Reactor;
// ...same usings and App component as ReactorBlankApp/App.cs...
ReactorApp.Run<App>("MyApp");

What changed

  • dev/Templates/Source/ProjectTemplates/Desktop/CSharp/ReactorSingleFileApp/ProjectTemplate.cs is the whole template: the directive header above plus the ReactorBlankApp/App.cs body. sourceName renames it to <name>.cs, so the assembly name and the synthesized package identity follow the project name.
  • dev/Templates/Dotnet/templates/reactor-singlefile-app/.template.config (template.json, dotnetcli.host.json, ide.host.json, icon.png):
    • Identity is Microsoft.WindowsAppSDK.Reactor.CSharp.SingleFileApp; classifications are Windows/WinUI/Desktop/Reactor/SingleFile/Experimental.
    • Parameters that carry over from the Reactor siblings keep the same names and defaults: --dotnet-version (net10.0), --reactor-version (0.1.0-preview.16), --wasdk-version (*, 2.2.0 or later), --windowsSdkBuildToolsWinAppVersion (*, 0.7.0 or later) and --no-restore.
    • The standard restore post action runs dotnet restore <name>.cs, which works on a .cs primary output.
  • dev/Templates/templates.props:
    • A new reactor.singlefile row in the DotnetNewOnlyProjectTemplate slot.
    • A new, documented IncludeGitignore metadata value (default true). The new template sets it to false so its output stays a single file.
    • Also fixes the stale "no instances today" note on that slot.
  • dev/Templates/Dotnet/WinAppSdk.CSharp.DotnetNewTemplates.csproj: the pack task skips the shared .gitignore when IncludeGitignore=false. Every other template's packed file list is unchanged.
  • dev/Templates/Dotnet/README.md: adds a table row, a "Quickstart a single-file Reactor app" section, and a note on setting the WinAppRun* properties with #:property in a single-file app.
  • dev/Templates/Dotnet/Test-DotnetNewTemplates.ps1: a minimal FileBasedApp kind, named after the .NET mechanism. It asserts that the scaffold is exactly <name>.cs, then runs dotnet build <name>.cs in the default (packaged) configuration.

Design notes

  • Naming. The template is named single-file to match the only first-party dotnet new template for a .NET file-based app, Aspire's aspire-apphost-singlefile ("Aspire Single-File App Host", classification SingleFile). The descriptions and README keep the official .NET term file-based app, which also separates this from PublishSingleFile.
  • Why it references Microsoft.WindowsAppSDK. With the published Microsoft.UI.Reactor 0.1.0-preview.16 alone, the Windows App SDK defaults to self-contained and the build fails with WindowsAppSDKSelfContained requires a supported Windows architecture. The Runtime dependency restored by Restore the Windows App Runtime dependency and document single-file apps microsoft-ui-reactor#1283 hasn't shipped yet. Referencing the metapackage, as the sibling templates do, makes the app framework-dependent and keeps --wasdk-version meaningful.
  • Why WinApp 0.7.0 or later. Packaged dotnet run <file>.cs (Run a .NET file-based app with package identity winappCli#794, Support packaged dotnet run for .NET file-based apps winappCli#874) is new in Microsoft.Windows.SDK.BuildTools.WinApp 0.7.0. With 0.6.1, _WinAppRunSupportActive stays false, so dotnet run starts the unpackaged exe and the app fails at startup.
  • What was left out of the siblings' setup.
    • The manifest is synthesized from the directives. Its display name is the file stem, which is the project name, just like the siblings' manifest.
    • So publisherDn, publisherDisplay, guid9 and the Windows SDK BuildTools pin were dropped. Microsoft.Windows.SDK.BuildTools comes in transitively.
    • Devtools are left out, because a #:package can't be Debug-only and file-based apps default to PublishAot=true.
    • Unlike the siblings' .csproj, there's no app.manifest, since Reactor sets PerMonitorV2 DPI awareness itself.
  • Hidden from Visual Studio's New Project dialog. ide.host.json sets "unsupportedHosts": [{ "id": "vs" }], because a file-based app has no project for VS to open. The Aspire single-file template linked above does the same. The .NET CLI ignores ide.host.json, so dotnet new and winapp new are unaffected.
  • Why the winui-reactor-singlefile alias. winapp new finds templates with dotnet new list winui, which matches names and short names only. Like the other Reactor templates, this one carries a winui-reactor* alias so winapp new --list and winapp new -t reactor-singlefile pick it up.

Validation

Local runs used .NET SDK 10.0.401, winapp 0.7.0 and Developer Mode, against the renamed template pack packed from this branch.

  • Pack and install
    • dotnet pack: content/reactor.singlefile/ holds ProjectTemplate.cs plus the four .template.config files and no gitignore.txt. Compared with the published 0.0.7-alpha, the only content difference is those 5 new entries; the 10 sibling project templates still get their gitignore.txt.
    • dotnet new list winui and dotnet new list reactor both list Reactor Single-File App (Experimental) reactor-singlefile,winui-reactor-singlefile.
  • Scaffold and build
    • dotnet new reactor-singlefile -n MyApp creates exactly MyApp\MyApp.cs and prints Restoring ...\MyApp\MyApp.cs: ... Restore succeeded..
    • dotnet build MyApp.cs in Debug and Release: 0 warnings, 0 errors.
    • Builds and runs write nothing next to the .cs file; the output goes to %TEMP%\dotnet\runfile\.
  • Run
    • dotnet run MyApp.cs prints ✅ Manifest generated for MyApp-53b1794c 1.0.0.0 and then launched. The window titled "MyApp" opens, and GetPackageFullName on the process returns MyApp-53b1794c_1.0.0.0_x64__gspb8g6x97k2t (AUMID …!App). The package is a dev-mode registration with a dependency on Microsoft.WindowsAppRuntime.2 2.5.1.0.
    • Unpackaged: with #:property WindowsPackageType=None added, dotnet run opens the window, the process has no package identity (APPMODEL_ERROR_NO_PACKAGE), and nothing is registered.
    • winapp run MyApp.cs --detach launches the same identity.
    • #:property WinAppRunNoLaunch=true registers the app without launching it (checks the README note).
  • dotnet publish MyApp.cs (Native AOT, the default for file-based apps)
    • Publishes to .\artifacts\MyApp\MyApp.exe with 0 warnings.
    • On my machine it only succeeds once %ProgramFiles(x86)%\Microsoft Visual Studio\Installer is on PATH. Without that, VS 18's vcvarsall fails with 'vswhere.exe' is not recognized. This is a toolchain environment issue, not a template one.
  • winapp and pinned versions
    • winapp new --list --template-version installed lists Reactor Single-File App (Experimental) │ reactor-singlefile.
    • winapp new -t reactor-singlefile -n WinappApp scaffolds WinappApp\WinappApp.cs.
    • Pinning --wasdk-version 2.2.0 (the documented floor) builds with 0 warnings.
  • Test script
    • Test-DotnetNewTemplates.ps1 -PackagePath <local nupkg> -SkipAppLaunch: All templates validated successfully. 41 summary rows are Succeeded: the 6 WinUI and 4 existing Reactor project templates, the new reactor-singlefile (create and build, where the build step first asserts the scaffold is a single file), the 7 item templates and the version scenarios. The one Skipped row is the default-wildcard build, which the script skips whenever * resolves to a concrete version.
    • Feed caveat: from my machine the repo's internal feed timed out (100 s) downloading the .NET 10.0.12 runtime packs, so a direct run failed on the first winui build before any Reactor template ran. That is a network problem on my end, not this change. The passing run used a byte-identical copy of the script whose generated NuGet.config points at a nuget.org proxy. The WindowsAppSDK-Templates-PR pipeline runs the script against the internal feed.

CI note

Since the pipeline cutover (#6835), main requires WinAppSDK-Foundation-PR and WindowsAppSDK-Templates-PR. On 40ce46e8:

  • WinAppSDK-Foundation-PR (OS project) passes, 48/48 jobs.
  • WindowsAppSDK-Templates-PR reports from two Azure DevOps projects under the same check name:
    • The OS copy (definition 202122) passes every stage: pack, SDL, VSIX validation, and the smoke test that scaffolds and builds reactor-singlefile along with the other templates.
    • The legacy ProjectReunion copy (definition 194418) fails, as it has on every completed run since the cutover. Its VSIX stage looks for runs of nightly pipeline 190463 in its own project (WASDK_PROJECT_ID: $(System.TeamProjectId)), but 190463 lives in the OS project, so the step fails with No run of pipeline 190463 on refs/heads/release/main in the last 7 days publishes an artifact named 'WindowsAppSDK-NuGet-And-MSIX'. That step doesn't depend on this PR, which doesn't touch the VSIX templates, and re-running doesn't help. The fix is to disable that definition or remove its PR trigger.

Follow-up (outside this repo)

  • winapp new ends with Next: cd "<name>" then winapp run. For a single-file template that fails with Manifest file not found, because winapp run only enters single-file mode for an explicit .cs path. The correct command is winapp run <name>.cs (or dotnet run <name>.cs). That hint needs a fix in winappCli; this template's comments and the README already give the right command.

A Microsoft employee must use /azp run to validate using the PR pipelines.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).
1 pipeline(s) were filtered out due to trigger conditions.

Adds a "Reactor Single-File App (Experimental)" template to the
Microsoft.WindowsAppSDK.WinUI.CSharp.Templates dotnet new pack. It scaffolds
the blank Microsoft.UI.Reactor app as a single-file .NET 10 file-based app:
one <name>.cs whose #:package / #:property directives replace the .csproj.
It runs packaged by default: for `dotnet run <name>.cs`,
Microsoft.Windows.SDK.BuildTools.WinApp 0.7.0+ synthesizes an appxmanifest
from the directives and launches the app with package identity. The name
follows Aspire's aspire-apphost-singlefile, the only first-party dotnet new
template for a .NET file-based app.

- templates.props: new DotnetNewOnlyProjectTemplate row (reactor.singlefile)
  and an IncludeGitignore metadata opt-out (default true) so the scaffolded
  output stays that single file.
- WinAppSdk.CSharp.DotnetNewTemplates.csproj: honor IncludeGitignore=false.
- reactor-singlefile-app/.template.config: short names reactor-singlefile
  and winui-reactor-singlefile (winapp new enumerates with `dotnet new list
  winui`), and hidden from the Visual Studio New Project dialog through
  ide.host.json unsupportedHosts, since VS cannot open a file-based app.
- The file references Microsoft.WindowsAppSDK: with Microsoft.UI.Reactor
  0.1.0-preview.16 alone the Windows App SDK defaults to self-contained and
  the build fails.
- README and Test-DotnetNewTemplates.ps1 (new FileBasedApp kind) updated.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azchohfi
Alexandre Zollinger Chohfi (azchohfi) force-pushed the azchohfi-reactor-file-based-app-template branch from cbbd4f9 to 2b60370 Compare September 30, 2026 00:42
@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).
1 pipeline(s) were filtered out due to trigger conditions.

@azchohfi Alexandre Zollinger Chohfi (azchohfi) changed the title Add Reactor file-based app template (reactor-filebased) Add Reactor single-file app template (reactor-singlefile) Sep 30, 2026
@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run WindowsAppSDK-Templates-PR

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).
1 pipeline(s) were filtered out due to trigger conditions.

@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run WinAppSDK-Foundation-PR

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
No pipelines were found matching this branch/path.

@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

@azchohfi

Copy link
Copy Markdown
Contributor Author

Kyaw Thant (@kythant) It looks like the old ProjectReunion copy of WindowsAppSDK-Templates-PR (definition 194418) still runs on PRs to main after the cutover. Its "Validate VSIX templates build" stage looks up nightly pipeline 190463 in its own project (WASDK_PROJECT_ID: $(System.TeamProjectId)), but 190463 lives in the OS project, so it always fails with No run of pipeline 190463 on refs/heads/release/main in the last 7 days publishes an artifact named 'WindowsAppSDK-NuGet-And-MSIX' (build).

Because it reports under the required WindowsAppSDK-Templates-PR name, it blocks every PR that touches templates (this one and #6805), even though the OS copy (definition 202122) passes every stage (build). Could you disable 194418 or remove its PR trigger?

@lauren-ciha

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@lauren-ciha

Copy link
Copy Markdown
Member

There was a bug where both the old and the new WindowsAppSDK-Templates-PR pipelines are running. The new one succeeds, the old one no longer works. I've disabled the pipeline and will close and reopen this PR to see if that reset the CI requirements for merging this in.

@azchohfi

Copy link
Copy Markdown
Contributor Author

Superseded by #6844: the same change, recreated as a fresh commit on current main so it doesn't carry this PR's stale check from the disabled legacy pipeline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants