fix(windows): bake app name into assembly so taskbar right-click shows it (#9)#17
Conversation
…s it The taskbar right-click menu showed "app-it-host" instead of the app name (Christian-Katzmann#9). Windows reads that label from the executable's embedded FileDescription, which the build-once-then-rename flow left at the project default. desktop-build.ps1 now publishes the WebView2 host per-app with -p:AssemblyName="<App Name>". The .csproj sets neither AssemblyTitle nor Product, so that one flag cascades into FileDescription + ProductName + OriginalFilename, which all then read the app name. Per-app exes are cached under assets\build\wrapper-windows\<slug>\ (mtime-aware); the trade vs the old single shared publish is one dotnet publish per app on first build. The per-app publish runs inside the build loop; a publish failure falls that app back to the Edge launcher (and skips the host build for later apps). dotnet stdout is piped to Out-Host so it cannot pollute the function return value. Validated on real Windows 11 hardware. Embedded metadata reads the app name; a freshly-named host shows it in the taskbar right-click immediately (an already-seen exe shows it after the shell cache turns over). PSScriptAnalyzer clean against the repo settings.
Reviewer's GuideThis PR changes the Windows desktop build script so that the WebView2 host executable is published per app with the app name baked into the assembly metadata, ensuring the taskbar label and right‑click menu show the app name instead of File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- Publish-HostForApp relies on outer-scope variables ($Root, $csproj, $newestSrc), which makes the function harder to reason about and reuse; consider passing these as explicit parameters so the function has clearer dependencies.
- The per-app caching logic assumes the published exe is named exactly "$AppName.exe" when checking $existing, but you already note that MSBuild may strip or alter unusual characters; in those cases you'll end up rebuilding on every run, so it may be worth deriving the cache key from the slug or reusing the same fallback (largest .exe) when deciding whether a rebuild is needed.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Publish-HostForApp relies on outer-scope variables ($Root, $csproj, $newestSrc), which makes the function harder to reason about and reuse; consider passing these as explicit parameters so the function has clearer dependencies.
- The per-app caching logic assumes the published exe is named exactly "$AppName.exe" when checking $existing, but you already note that MSBuild may strip or alter unusual characters; in those cases you'll end up rebuilding on every run, so it may be worth deriving the cache key from the slug or reusing the same fallback (largest .exe) when deciding whether a rebuild is needed.
## Individual Comments
### Comment 1
<location path="plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1" line_range="119-120" />
<code_context>
+# instead of "app-it-host" (issue #9). Cached per slug, mtime-aware. Returns the
+# published "<App Name>.exe" path, or $null on a build failure (the caller then
+# falls that app back to the Edge launcher).
+function Publish-HostForApp {
+ param([string]$AppName, [string]$Slug)
+
+ $appPublishDir = Join-Path $Root "assets\build\wrapper-windows\$Slug"
</code_context>
<issue_to_address>
**issue:** Consider sanitizing or validating `$AppName` before using it as `-p:AssemblyName` to avoid invalid filename/assembly characters breaking `dotnet publish`.
If `$AppName` includes characters invalid for assembly names/Windows filenames (`:`, `*`, `?`, `\`, `/`, etc.), `dotnet publish` can fail before the fallback logic runs. Consider either deriving `AssemblyName` from a sanitized slug and using `$AppName` only for user-facing text, or normalizing `$AppName` (e.g., stripping/replacing invalid chars) before passing it to `-p:AssemblyName`.
</issue_to_address>
### Comment 2
<location path="plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1" line_range="123-124" />
<code_context>
+ param([string]$AppName, [string]$Slug)
+
+ $appPublishDir = Join-Path $Root "assets\build\wrapper-windows\$Slug"
+ $existing = if (Test-Path $appPublishDir) {
+ Get-ChildItem -Path $appPublishDir -Filter "$AppName.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
+ } else { $null }
+ $needsBuild = (-not $existing) -or ($newestSrc -and $newestSrc.LastWriteTimeUtc -gt $existing.LastWriteTimeUtc)
</code_context>
<issue_to_address>
**suggestion:** Using `$AppName` inside `-Filter` can misbehave when the name contains wildcard characters; consider a more literal lookup.
Because `-Filter` does wildcard matching, names containing `*`, `?`, `[` or `]` are treated as patterns, which can cause incorrect matches when checking for an existing exe. Prefer a literal lookup, e.g. `Get-Item` on the full path (with `-ErrorAction SilentlyContinue`), or `Get-ChildItem -LiteralPath` instead of `-Filter` to avoid this issue.
</issue_to_address>
### Comment 3
<location path="plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1" line_range="146-122" />
<code_context>
+ if ($LASTEXITCODE -ne 0) { return $null }
+ }
+
+ $exe = Get-ChildItem -Path $appPublishDir -Filter "$AppName.exe" -ErrorAction SilentlyContinue |
+ Select-Object -First 1 -ExpandProperty FullName
+ if (-not $exe) {
+ # Name didn't map 1:1 (MSBuild stripped unusual chars) - take the
</code_context>
<issue_to_address>
**suggestion (performance):** If the actual exe name diverges from `$AppName.exe`, the cache check will never hit and each run will keep rebuilding that app.
You already fall back to the largest `.exe` when `$AppName.exe` doesn’t exist, but the cache check still only looks for `$AppName.exe`, so `$needsBuild` stays `true` on every run. Consider basing the cache check on the resolved exe path (e.g., any `.exe` in that directory with an mtime check) or persisting the resolved exe name and reusing it for subsequent runs.
Suggested implementation:
```
$exe = Get-ChildItem -Path $appPublishDir -Filter "$AppName.exe" -ErrorAction SilentlyContinue |
Select-Object -First 1 -ExpandProperty FullName
if (-not $exe) {
# Name didn't map 1:1 (MSBuild stripped unusual chars) - take the
# largest .exe as the host.
$exe = Get-ChildItem -Path $appPublishDir -Filter *.exe -ErrorAction SilentlyContinue |
Sort-Object Length -Descending | Select-Object -First 1 -ExpandProperty FullName
}
if ($exe) {
# Persist the resolved exe name so cache checks can reuse the actual host
# file name even when it diverges from "$AppName.exe".
$resolvedExeNamePath = Join-Path $appPublishDir '.resolved-exe'
(Split-Path -Path $exe -Leaf) | Set-Content -Path $resolvedExeNamePath -Encoding ASCII
}
return $exe
```
To fully implement the caching suggestion, also update the earlier cache check logic in this script:
1. Before deciding whether `$needsBuild` is true, look for the `.resolved-exe` file in `$appPublishDir`. If it exists, read the exe name from it and construct the full path (e.g., `$resolvedExe = Join-Path $appPublishDir (Get-Content $resolvedExeNamePath)`), then use that path for the timestamp/exists check instead of hard-coding `$AppName.exe`.
2. If `.resolved-exe` does not exist yet, fall back to the old behavior (checking `$AppName.exe`) or to a wildcard `.exe` probe similar to the code above, then use that resolved path for the cache decision.
3. Ensure any logic that currently assumes the host exe is `$AppName.exe` (for cache hit, `Test-Path`, `Get-Item`, etc.) is switched to use the resolved exe path so that once the name divergence is detected and persisted, subsequent runs will hit the cache correctly.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Reworks the Christian-Katzmann#9 fix in response to PR review (sourcery-ai): - Publish with -p:AssemblyName=<slug> (always [a-z0-9-], a valid assembly/file name) and -p:AssemblyTitle/-p:Product=<App Name>. The slug drives the published file name, so the per-app cache lookup is exact - no MSBuild char-stripping to second-guess (the prior code keyed the cache on "<App Name>.exe" and would rebuild every run if MSBuild altered the name). The free-form app name now only feeds FileDescription/ProductName, the fields the taskbar reads, so it never has to be a valid file name - removing the invalid-char failure mode for -p:AssemblyName. - Pass build inputs (csproj, repo root, newest-source mtime) into Publish-HostForApp as parameters instead of reading script scope. - Use Get-Item -LiteralPath for the cache probe. Verified on Windows 11: a fresh-named app shows its name in the taskbar right-click on first launch; FileDescription/ProductName read the app name, OriginalFilename is the slug; a second build hits the cache (no republish). PSScriptAnalyzer clean.
|
Thanks @sourcery-ai - the feedback hit one real weak point: the app name was doing triple duty as assembly name, file name, and cache key. Fixed by splitting identity from the file name:
Re-validated on Windows 11: fresh-named app shows its name in the taskbar right-click on first launch. |
…-runtime fixes (#17, #18) He went from first real-hardware validator to the Windows beta's recurring real-hardware tester — three fixes now, each traced and verified on Windows 11 (window title #8, taskbar right-click app name #17, WebView2-runtime-missing path #18). Refresh his existing nudge line to reflect that arc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Merged — thank you, @Firnschnee. The field-tracing here was the difference: pinning down that the taskbar right-click reads the executable's embedded You're now credited in the README's Community nudge as the Windows beta's real-hardware tester. Much appreciated. |
…e landed (#17, #18) The recruitment paragraph still framed #8 as the single real-hardware milestone ("first run … window-title bug"). It's had a run of fixes since (#17 taskbar app name, #18 WebView2-runtime-missing path), so update it to "a run of fixes since — a real start, not a finish line." No overclaim — it still says "needing more," the author still runs only macOS, and the concrete fix-by-fix list stays in the README Community nudge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes #9.
The problem
The taskbar right-click menu showed
app-it-hostinstead of the app name. I assumed the fix was just-p:AssemblyName(per the issue), but real hardware made me check which embedded field the shell actually reads first.What the field tracing showed (Windows 11)
desktop-build.ps1published the host once asapp-it-host.exeand copied it per-app under a new filename. The embedded metadata stayedapp-it-host, and the taskbar right-click reads the executable'sFileDescription-- not the file name -- so it kept showingapp-it-host.Setting
-p:AssemblyName="<App Name>"at publish fixes it: the.csprojsets neitherAssemblyTitlenorProduct, so that one flag cascades intoFileDescription+ProductName+OriginalFilename. Confirmed by inspectingVersionInfoon the built exe -- all three read the app name.Because the version resource is baked at compile time, a single shared binary cannot carry per-app identity. Rewriting it post-publish would need an external resource editor (rcedit), which the no-dependencies contract rules out -- so the host is now published per app, cached per slug under
assets\build\wrapper-windows\<slug>\(mtime-aware). Trade vs the old single publish: onedotnet publishper app on first build. For the common 1-2 app case it is unchanged.The shell-cache gotcha (worth a note for the next tester)
After rebuilding
Vite Test.exewith correct metadata, the right-click still showedapp-it-host. It looked like the fix had failed. It hadn't -- the Windows shell caches taskbar identity (label + icon) per executable, and we had launched manyapp-it-host-flavoredVite Test.exebuilds earlier in the day.Decisive test: I published a host with a name the shell had never seen (
Zephyr Probe 7) and launched it. The right-click showedZephyr Probe 7immediately. So the fix is correct; the stale label was our own iterative-testing cache. A clean install shows the right name straight away.Verification
VersionInfo.FileDescription/ProductName/OriginalFilenameon the built<App Name>.exeall read the app name (wasapp-it-host).-p:AssemblyName=$namesurvives native arg-passing under both pwsh 7 (Windows mode) and Windows PowerShell 5.1 (Legacy) -- theAssemblyName=$nametoken carries no literal space, so neither shell splits it. Tested both.desktop-build.ps1->desktop-install.ps1flow still builds, installs, and launches.PSScriptAnalyzerclean againstPSScriptAnalyzerSettings.psd1.Notes
dotnetstdout is piped toOut-Hostinside the newPublish-HostForAppfunction, so build-log lines cannot leak into the returned exe path (a PowerShell function-return gotcha I hit and fixed during testing)..csproj: leavingAssemblyTitle/Productunset is exactly what makes the single-p:AssemblyNameflag sufficient.Test plan
Get-Item <exe>).VersionInfo)Summary by Sourcery
Publish the Windows WebView2 host per app so each generated launcher embeds the correct app name in its assembly metadata and shows the right label in the taskbar right-click menu.
Bug Fixes:
app-it-hostby baking the app name into the host assembly metadata via per-app publishes.Enhancements:
Documentation: