Skip to content

Add HandBrake as a Windows Fleet-maintained app - #50352

Open
kitzy wants to merge 1 commit into
mainfrom
add-handbrake-windows-fma
Open

Add HandBrake as a Windows Fleet-maintained app#50352
kitzy wants to merge 1 commit into
mainfrom
add-handbrake-windows-fma

Conversation

@kitzy

@kitzy kitzy commented Jul 31, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #50323

Adds HandBrake as a Windows Fleet-maintained app, from winget HandBrake.HandBrake (1.11.2, NSIS, machine scope, x64). Found in a customer's ManageEngine ServiceDesk Plus Windows deployment catalog with no Fleet equivalent.

Per @allenhouchins' guidance on the issue, the .NET Desktop Runtime 10 prerequisite is handled in the install script with a clear failure message rather than left to fail silently after install.

The prerequisite check

HandBrake's own installer text states it plainly:

HandBrake requires Microsoft .NET Desktop Runtime 10. If this is not installed, you will be prompted to install it when you first run the app.

The installer neither bundles nor installs that runtime, and the FMA ingester drops winget's Dependencies field. Without a check, Fleet would install HandBrake, register it in Add/Remove Programs, report success, and the user would hit a runtime prompt on first launch. The script now checks %ProgramFiles%\dotnet\shared\Microsoft.WindowsDesktop.App\10.* and exits 1 with an actionable message when it is absent.

Two things worth reviewers' judgement:

  • We fail rather than warn. The vendor does prompt the user at first run, so an install without the runtime is recoverable by hand. I chose to fail because in a SYSTEM-context managed deployment an end-user runtime prompt is a support ticket, and a truthful "install failed, here is why" is more useful than a silently unusable app. Easy to soften to a warning + Exit 0 if we would rather defer to the vendor's prompt.
  • We ship no .NET Desktop Runtime FMA today. Fleet has microsoft-dotnet-runtime-8/-10, but those are the base runtime, not the Desktop runtime — a separate package with its own ARP entry. So there is currently no in-product way to satisfy this prerequisite; an admin has to deploy the runtime by other means. That gap is worth its own issue.

Identity — two traps, both from reading the vendor's NSIS script

HandBrake's Installer64.nsi writes only four values:

Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
...
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayName"     "$(^Name)"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayIcon"     "$INSTDIR\HandBrake.exe"
WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayVersion"  "${PRODUCT_VERSION}"
  1. DisplayName is version-suffixed (HandBrake 1.11.2), so the exists query is a prefix match, not equality.
  2. No Publisher is ever written. The default generated query would have pinned publisher = 'The HandBrake Team' from the winget locale manifest and matched nothing, forever, while the validator still passed. The exists query is overridden to drop the publisher clause.

UninstallString is also an unquoted path containing spaces (C:\Program Files\HandBrake\uninst.exe), which the defensive parser in the uninstall script handles by capturing through .exe.

The uninstall script additionally waits for the registry entry to disappear: a silent NSIS uninstaller returns before removal completes, so its exit code alone is not a reliable completion signal.

Verification

  • Installer SHA confirmed against a local download (6becb8e5…f8cd); the URL is GitHub Releases, so none of the SourceForge trouble from New FMA: CrystalDiskMark #50322 applies.
  • No icon work needed. Reusing the catalog name HandBrake means this shares the existing handbrake icon with handbrake-app/darwin, and the two group together in the FMA library.

Checklist for submitter

  • QA'd all new/changed functionality manually

Summary by CodeRabbit

  • New Features
    • Added HandBrake 1.11.2 to the Windows maintained applications catalog.
    • Added support for silent installation and uninstallation, including version detection and installer verification.
    • Added validation for the required .NET Desktop Runtime 10 prerequisite.
    • Added handling for installation completion, reboot-required results, and uninstall status reporting.

@kitzy
kitzy marked this pull request as ready for review July 31, 2026 21:58
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds HandBrake 1.11.2 as a Windows maintained app. The change includes Winget metadata, catalog records, installer verification, Productivity categorization, and hashed PowerShell scripts. The installation script validates .NET Desktop Runtime 10, runs the silent installer, handles timeouts and reboot-related exit codes, and checks registry registration. The uninstallation script resolves registry uninstall commands, runs them, and verifies removal.

Possibly related PRs

  • fleetdm/fleet#49478: Adds another Windows maintained app with Winget metadata and PowerShell lifecycle scripts.
  • fleetdm/fleet#50024: Implements a Windows maintained app with registry-based uninstall handling and removal polling.
  • fleetdm/fleet#50016: Adds a Windows Winget app manifest with registry-based detection and PowerShell scripts.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: adding HandBrake as a Windows Fleet-maintained app.
Description check ✅ Passed The description identifies the issue, explains key implementation decisions, and documents manual QA, but leaves several checklist items unaddressed.
Linked Issues check ✅ Passed The PR adds the required input, handles verified registry identity, provides install and uninstall scripts, and reuses the existing catalog icon for #50323.
Out of Scope Changes check ✅ Passed All changes support the linked issue objectives for adding and managing the HandBrake Windows application.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-handbrake-windows-fma

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ee/maintained-apps/inputs/winget/scripts/handbrake_uninstall.ps1`:
- Around line 31-42: Update the uninstall process handling around Start-Process,
WaitForExit, and $process.HasExited to detect when the wait reaches
$removalTimeoutSeconds without process termination, then stop that process
forcefully with Stop-Process -Force before polling the registry. Match the
timeout fallback pattern used by handbrake_install.ps1 while preserving the
existing exit-code logging for processes that exit normally.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2529db82-d0d7-4c01-ab10-c49a6ee79b4f

📥 Commits

Reviewing files that changed from the base of the PR and between 46aed2d and 7a32b8d.

📒 Files selected for processing (5)
  • ee/maintained-apps/inputs/winget/handbrake.json
  • ee/maintained-apps/inputs/winget/scripts/handbrake_install.ps1
  • ee/maintained-apps/inputs/winget/scripts/handbrake_uninstall.ps1
  • ee/maintained-apps/outputs/apps.json
  • ee/maintained-apps/outputs/handbrake/windows.json

Comment on lines +31 to +42
$process = Start-Process -FilePath $uninstallCommand -ArgumentList $uninstallArgs -PassThru
$null = $process.Handle
$null = $process.WaitForExit($removalTimeoutSeconds * 1000)
if ($process.HasExited) { $exitCode = $process.ExitCode; Write-Host "Uninstall exit code: $exitCode" }

# A silent NSIS uninstaller returns before removal finishes, so the registry
# entry disappearing is the real completion signal.
$elapsed = 0
while ((Get-HandBrakeEntry) -and ($elapsed -lt $removalTimeoutSeconds)) {
Start-Sleep -Seconds 5
$elapsed += 5
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add a kill fallback for a hung uninstall process.

If the watched uninstall process does not exit within $removalTimeoutSeconds, the script does not stop it before moving on to poll the registry. handbrake_install.ps1 stops a hung installer process with Stop-Process -Force after its own timeout; this uninstall script has no equivalent, so a hung process can keep running in the background even after this script exits.

Add a timeout-based kill, consistent with the install script's pattern.

🔧 Proposed fix
     Write-Host "Uninstall command: $uninstallCommand"; Write-Host "Uninstall args: $uninstallArgs"
     $process = Start-Process -FilePath $uninstallCommand -ArgumentList $uninstallArgs -PassThru
     $null = $process.Handle
-    $null = $process.WaitForExit($removalTimeoutSeconds * 1000)
-    if ($process.HasExited) { $exitCode = $process.ExitCode; Write-Host "Uninstall exit code: $exitCode" }
+    if (-not $process.WaitForExit($removalTimeoutSeconds * 1000)) {
+        Write-Host "Uninstall process did not exit within ${removalTimeoutSeconds}s, stopping it."
+        Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
+        $null = $process.WaitForExit(30 * 1000)
+    }
+    if ($process.HasExited) { $exitCode = $process.ExitCode; Write-Host "Uninstall exit code: $exitCode" }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$process = Start-Process -FilePath $uninstallCommand -ArgumentList $uninstallArgs -PassThru
$null = $process.Handle
$null = $process.WaitForExit($removalTimeoutSeconds * 1000)
if ($process.HasExited) { $exitCode = $process.ExitCode; Write-Host "Uninstall exit code: $exitCode" }
# A silent NSIS uninstaller returns before removal finishes, so the registry
# entry disappearing is the real completion signal.
$elapsed = 0
while ((Get-HandBrakeEntry) -and ($elapsed -lt $removalTimeoutSeconds)) {
Start-Sleep -Seconds 5
$elapsed += 5
}
$process = Start-Process -FilePath $uninstallCommand -ArgumentList $uninstallArgs -PassThru
$null = $process.Handle
if (-not $process.WaitForExit($removalTimeoutSeconds * 1000)) {
Write-Host "Uninstall process did not exit within ${removalTimeoutSeconds}s, stopping it."
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
$null = $process.WaitForExit(30 * 1000)
}
if ($process.HasExited) { $exitCode = $process.ExitCode; Write-Host "Uninstall exit code: $exitCode" }
# A silent NSIS uninstaller returns before removal finishes, so the registry
# entry disappearing is the real completion signal.
$elapsed = 0
while ((Get-HandBrakeEntry) -and ($elapsed -lt $removalTimeoutSeconds)) {
Start-Sleep -Seconds 5
$elapsed += 5
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/inputs/winget/scripts/handbrake_uninstall.ps1` around
lines 31 - 42, Update the uninstall process handling around Start-Process,
WaitForExit, and $process.HasExited to detect when the wait reaches
$removalTimeoutSeconds without process termination, then stop that process
forcefully with Stop-Process -Force before polling the registry. Match the
timeout fallback pattern used by handbrake_install.ps1 while preserving the
existing exit-code logging for processes that exit normally.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New FMA: HandBrake for Windows

1 participant