Fix .NET runtime winget ingestion after upstream Scope flip - #51022
Conversation
Microsoft republished the Microsoft.DotNet.* winget manifests on schema 1.12.0 and added a top-level "Scope: machine" that the previous 1.9.0 manifests did not carry. The ingester reads the manifest-level scope first (ingester.go:312), so scope became "machine" where it had resolved to "", and the installer match on scope == input.InstallerScope failed for every installer in the manifest. That returns "failed to find installer for app", which is fatal for the whole run: panic: ingesting winget app: failed to find installer for app Set installer_scope to "machine" on the three affected inputs and regenerate their outputs. "exe" is in fileTypes, so the machine-scope MSI coercion at ingester.go:326 does not apply and the type still matches. Also picks up the versions the failed runs never ingested: .NET Desktop Runtime 10 and .NET Runtime 10 to 10.0.11, .NET Runtime 8 to 8.0.30.
Script Diff Resultsee/maintained-apps/outputs/microsoft-dotnet-desktop-runtime-10/windows.json=== Install Script (no changes) ===
=== Uninstall // 661ee74e -> a95e65db ===
--- /tmp/old.2XPGDG 2026-08-12 02:26:05.179388645 +0000
+++ /tmp/new.qQlljF 2026-08-12 02:26:05.179388645 +0000
@@ -8,7 +8,7 @@
# We target the bundle by its ProductCode (injected by the ingester) and fall back
# to the cached bootstrapper in the Package Cache.
-$productCode = '{866BECDA-F284-473A-9E84-0CCE816BF06F}'
+$productCode = '{96749152-C361-49E0-BAC6-818F491B9276}'
function Invoke-Uninstaller {
param([string]$exe, [string]$exeArgs)ee/maintained-apps/outputs/microsoft-dotnet-runtime-10/windows.json=== Install Script (no changes) ===
=== Uninstall // 9da7afd4 -> f4222761 ===
--- /tmp/old.4GTMzN 2026-08-12 02:26:05.235388187 +0000
+++ /tmp/new.bDfU2X 2026-08-12 02:26:05.236388179 +0000
@@ -8,7 +8,7 @@
# We target the bundle by its ProductCode (injected by the ingester) and fall back
# to the cached bootstrapper in the Package Cache.
-$productCode = '{086851AF-5BEF-47A0-95C5-A8EED09F3F36}'
+$productCode = '{9515CC72-C8E1-498C-9423-E0DA05D6B55B}'
function Invoke-Uninstaller {
param([string]$exe, [string]$exeArgs)ee/maintained-apps/outputs/microsoft-dotnet-runtime-8/windows.json=== Install Script (no changes) ===
=== Uninstall // d9aa0345 -> da06c0ef ===
--- /tmp/old.dIOcN5 2026-08-12 02:26:05.283387795 +0000
+++ /tmp/new.sGrR08 2026-08-12 02:26:05.284387787 +0000
@@ -8,7 +8,7 @@
# We target the bundle by its ProductCode (injected by the ingester) and fall back
# to the cached bootstrapper in the Package Cache.
-$productCode = '{0636c53d-0ac0-4636-9b38-7fe0a4f09a00}'
+$productCode = '{bba240a5-2d02-4aed-99b0-f1b79cb1c4c1}'
function Invoke-Uninstaller {
param([string]$exe, [string]$exeArgs) |
There was a problem hiding this comment.
Pull request overview
This PR updates Fleet-maintained app (FMA) winget input manifests for Microsoft .NET runtimes to match an upstream winget manifest change (Scope: machine), preventing the winget ingester from failing to select an installer and aborting the scheduled “Ingest maintained apps” workflow.
Changes:
- Set
installer_scopeto"machine"for the three affected winget inputs (Microsoft.DotNet.*). - Regenerated the corresponding Windows output manifests to bump versions and update installer URLs/SHA256s (and related uninstall ProductCodes).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| ee/maintained-apps/inputs/winget/microsoft-dotnet-runtime-8.json | Sets installer_scope to machine to match upstream manifest scope. |
| ee/maintained-apps/inputs/winget/microsoft-dotnet-runtime-10.json | Sets installer_scope to machine to match upstream manifest scope. |
| ee/maintained-apps/inputs/winget/microsoft-dotnet-desktop-runtime-10.json | Sets installer_scope to machine to match upstream manifest scope. |
| ee/maintained-apps/outputs/microsoft-dotnet-runtime-8/windows.json | Regenerates output (version bump, updated download URL/SHA, updated uninstall ProductCode). |
| ee/maintained-apps/outputs/microsoft-dotnet-runtime-10/windows.json | Regenerates output (version bump, updated download URL/SHA, updated uninstall ProductCode). |
| ee/maintained-apps/outputs/microsoft-dotnet-desktop-runtime-10/windows.json | Regenerates output (version bump, updated download URL/SHA, updated uninstall ProductCode). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
WalkthroughThe Winget inputs now specify machine-wide installation for .NET Desktop Runtime 10, .NET Runtime 10, and .NET Runtime 8. The Windows outputs update .NET Desktop Runtime 10 to 10.0.11, .NET Runtime 10 to 10.0.11, and .NET Runtime 8 to 8.0.30. Each update includes new patch thresholds, installer URLs, checksums, uninstall references, and bundle product codes. Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…51040) <!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** N/A — fixes a failing scheduled workflow ([Ingest maintained apps](https://github.com/fleetdm/fleet/actions/workflows/ingest-maintained-apps.yml)) ## What's happening The `Ingest maintained apps` workflow panics on every run: ``` {"level":"INFO","msg":"ingesting homebrew app","name":"Worksheet Crafter"} panic: ingesting homebrew app: app not found in brew API ``` The homebrew ingester aborts on the first failing app, so no FMA update PR is generated at all — the failure is not limited to Worksheet Crafter's own data. ## Root cause Homebrew renamed the cask from `worksheet-crafter` to `worksheetcrafter`. The old token now 404s, which `fetchCask` treats (correctly) as a non-transient, fatal error at [ingester.go:533](https://github.com/fleetdm/fleet/blob/main/ee/maintained-apps/ingesters/homebrew/ingester.go#L533). The upstream cask records the rename itself: ```json { "token": "worksheetcrafter", "old_tokens": ["worksheet-crafter"], "name": ["WorksheetCrafter"], "version": "2026.2.5" } ``` The app is not gone from Homebrew, and it is unrelated to the app being `frozen` — `frozen: true` only gates the *output write* in [main.go:106](https://github.com/fleetdm/fleet/blob/main/cmd/maintained-apps/main.go#L106); the ingester still fetches the cask for every input, so a frozen app with a dead token still takes the whole run down. ## The fix Point the input at the new token: ```diff - "token": "worksheet-crafter", + "token": "worksheetcrafter", ``` Deliberately unchanged: - **`slug` stays `worksheet-crafter/darwin`.** It is user-facing and already published in `apps.json`, and the validator derives the input file path from the slug (`isFrozen()` in [app_commander.go:36](https://github.com/fleetdm/fleet/blob/main/cmd/maintained-apps/validate/app_commander.go#L36) reads `inputs/homebrew/<slug-name>.json`), so the input file keeps its current name too. - **`name` stays `Worksheet Crafter`.** The output's `name` comes from the input, not the cask, so adopting the cask's new `WorksheetCrafter` spelling would only churn the display name for existing users. - **`frozen: true` stays.** The freeze predates this (#49055) and is a separate question; this PR is scoped to unbreaking the workflow. The output stays at 2026.2.4 while upstream is at 2026.2.5. ## Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. No changes file: this is FMA manifest data, consistent with previous FMA data fixes (e.g. #51022). ## Testing - [x] QA'd all new/changed functionality manually The affected slug now ingests cleanly where it previously panicked, and writes nothing (it is frozen and its output already exists): ``` go run ./cmd/maintained-apps -slug worksheet-crafter/darwin ``` Because the run aborts on the first failure, a fix for one app can just move the panic to the next one. Two checks against that: 1. Every homebrew input token was cross-checked against the full brew cask index (`https://formulae.brew.sh/api/cask.json`, 7691 casks). `worksheet-crafter` was the only token missing — no other input is at risk of a 404. 2. The whole homebrew leg was run end to end (`-slug /darwin` matches all 962 homebrew inputs and no winget inputs). It completed with exit 0. `outputs/worksheet-crafter/darwin.json` was untouched, as expected for a frozen app; the other output changes were ordinary upstream version bumps and were reverted rather than included here, since they belong to the automated update PR. ### Unrelated issue noticed while verifying The full run logged, without failing: ``` Error enriching app net.whatsapp.WhatsApp: Expected WhatsApp version to start with '2.' but found '26.32.15' ``` This is pre-existing, not something this PR touches. WhatsApp has moved to a `26.x` version scheme, so `whats_app_version_shortener.go`'s `2.`-prefix assumption no longer holds. `EnrichManifest` only prints the error and moves on ([main.go:118](https://github.com/fleetdm/fleet/blob/main/ee/maintained-apps/ingesters/homebrew/external_refs/main.go#L118)), so the shortener is skipped and the unshortened version ships — `outputs/whatsapp/darwin.json` is already on `26.32.12`. Worth a follow-up. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Updated the Homebrew app token to use the correct identifier for Worksheet Crafter. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Related issue: N/A — fixes a failing scheduled workflow (Ingest maintained apps)
What's happening
The
Ingest maintained appsworkflow is failing on every run:Because a single unmatched app aborts the whole run, no FMA update PR is being generated at all. This cron runs every 4 hours, so it has been failing repeatedly since the upstream change landed — not just once.
Root cause
Microsoft republished the
Microsoft.DotNet.*winget manifests on manifest schema 1.12.0 and added a top-levelScope: machinethat the previous 1.9.0 manifests did not carry:The ingester reads the manifest-level scope first (
scope := m.Scope, ingester.go:312). Previously that was empty, so it fell through to the per-installer scope (also empty) and then to the installer-type switch — which has noexecase, leaving scope as""and matching the input's"installer_scope": "".Now it resolves to
"machine", so the match at ingester.go:338:fails for every installer in the manifest,
selectedInstallerstays nil, and the ingester returnsfailed to find installer for app.The package was not removed from winget — a removal would surface as a 404 from
GetContents, not this error.The fix
Set
"installer_scope": "machine"on the three affected inputs and regenerate their outputs.exeis present infileTypes, so the machine-scope→MSI coercion at ingester.go:326 does not apply andinstaller_type: "exe"still matches. Worth confirming on review — without that, the scope fix alone would have moved the failure rather than resolved it.All three .NET inputs were affected, not just the one named in the log. The run aborted at the first failure and filename ordering put
microsoft-dotnet-desktop-runtime-10ahead of the other two, so they were never reached:Microsoft.DotNet.DesktopRuntime.10Microsoft.DotNet.Runtime.10Microsoft.DotNet.Runtime.8Microsoft also moved the download host from
builds.dotnet.microsoft.comtodownload.microsoft.com, so the installer URLs and SHA256s in the regenerated outputs change too.Checklist for submitter
SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.No changes file: this is FMA manifest data, consistent with previous FMA data fixes (e.g. #50370).
Testing
Ran the ingester locally against live upstream manifests for each affected slug — all three ingest cleanly where they previously panicked:
The regenerated outputs carry real upstream SHA256s and the new download URLs.
Because the ingester aborts on the first failure, fixing only these three would move the panic rather than resolve it if any other manifest got the same treatment. So every winget input was ingested individually against live upstream manifests:
No other package is affected by the Scope flip.
Unrelated fragility noticed while verifying
A full-run attempt died on a different app with:
That was a transient TCP reset, but it is still fatal to the entire run:
isTransientGitHubErroronly recognizesRateLimitError,AbuseRateLimitError, andErrorResponsewith 429/5xx, so a bare connection reset falls through to the fatal path and takes all 434 apps down with it. Out of scope for this PR, but worth a follow-up given this workflow runs every 4 hours.Summary by CodeRabbit