Skip to content

Add Windows FMAs (digit batch): 3DF Zephyr Free, 4K Video Downloader+ - #48872

Merged
allenhouchins merged 4 commits into
mainfrom
add-windows-fmas-digit-batch
Jul 7, 2026
Merged

Add Windows FMAs (digit batch): 3DF Zephyr Free, 4K Video Downloader+#48872
allenhouchins merged 4 commits into
mainfrom
add-windows-fmas-digit-batch

Conversation

@allenhouchins

@allenhouchins allenhouchins commented Jul 7, 2026

Copy link
Copy Markdown
Member

Related issue: N/A — part of the Windows Fleet-maintained apps catalog expansion.

Adds two new Windows Fleet-maintained apps (the "digit" batch of the Windows FMA workstream):

App winget package Installer Notes
4K Video Downloader+ OpenMedia.4KVideoDownloaderPlus EXE (WiX burn bundle), machine scope, x64 Bundle registration extracted from the installer: ARP DisplayName "4K Video Downloader+", Publisher "InterPromo GMBH" (differs from the winget locale publisher "Open Media LLC", so program_publisher is set). Registers TWO ARP entries with the same DisplayName (the bundle and its chained MSI). The uninstall script prefers the bundle entry and normalizes msiexec args when only the MSI entry is present (validator-confirmed). Exact DisplayName matching keeps it from touching the non-plus product.
3DF Zephyr Free 3Dflow.3DFZephyr.Free EXE (Inno Setup), machine scope, x64 Registry DisplayName is versioned ("3DF Zephyr Free version 8.038"), so the input uses fuzzy_match_name: true. The paid edition registers as "3DF Zephyr version X" (no "Free") and is not matched. Standard Inno silent switches.

Also considered from this batch but not added:

  • 4K Video Downloader (OpenMedia.4KVideoDownloader, the classic 4.x app): verified and validated successfully, but intentionally dropped — it's in maintenance mode and 4K Video Downloader+ is the actively developed successor, so we're offering only the + app.
  • 3DxWare 10 (3Dconnexion.3DxWare.10): the vendor bootstrapper hung for 10 minutes and exited 1 with no output in the validator's headless SYSTEM session (driver install), so it was dropped after the first validation run.
  • 3CX Desktop App (3CX.Softphone): MSIX with an unversioned InstallerUrl (.../3CX.msix) — the file at that URL is already a newer build (20.0.1162.0) than the manifest's pinned version/SHA (20.0.1102.0), so installs would fail hash validation. Can be revisited with ignore_hash plus Windows-App-style MSIX provisioning scripts.

Installer SHAs in the outputs were verified against the winget manifests. Icons generated via tools/software/icons/generate-icons.sh; icon component names were adjusted to valid JS identifiers (ThreeDfZephyrFree, FourKVideoDownloaderPlus) following the ZeroOneZeroEditor/FourK* precedent.

Checklist for submitter

  • 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.
  • Timeouts are implemented and retries are limited to avoid infinite loops

Testing

  • QA'd all new/changed functionality manually (relying on the FMA CI validator for Windows install/uninstall validation)

Summary by CodeRabbit

  • New Features

    • Added support for 3DF Zephyr Free and 4K Video Downloader+ in the app catalog.
    • Added matching app icons so both products display correctly in the software page.
  • Bug Fixes

    • Improved install and uninstall handling for both apps, including quieter setup/removal and better restart-code handling.
    • Added app detection and version checks so status is reported more accurately.

fleet-release
fleet-release previously approved these changes Jul 7, 2026
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.10%. Comparing base (af51636) to head (ab63f76).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
...es/SoftwarePage/components/icons/3DfZephyrFree.tsx 50.00% 1 Missing ⚠️
...Page/components/icons/FourKVideoDownloaderPlus.tsx 50.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #48872   +/-   ##
=======================================
  Coverage   68.10%   68.10%           
=======================================
  Files        3694     3697    +3     
  Lines      234425   234439   +14     
  Branches    12464    12463    -1     
=======================================
+ Hits       159647   159658   +11     
- Misses      60450    60453    +3     
  Partials    14328    14328           
Flag Coverage Δ
frontend 59.28% <50.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Validator results: the VD+ ARP entry can be the chained MSI (MsiExec.exe
/X{...}) rather than the burn bundle; prepending /uninstall produced invalid
msiexec args. The uninstall script now prefers the bundle entry and normalizes
msiexec args when only the MSI entry exists. 3DxWare 10's vendor bootstrapper
hangs for 10 minutes in the headless SYSTEM session, so it's removed from this
batch.
@allenhouchins allenhouchins changed the title Add Windows FMAs (digit batch): 3DF Zephyr Free, 3DxWare 10, 4K Video Downloader, 4K Video Downloader+ Add Windows FMAs (digit batch): 3DF Zephyr Free, 4K Video Downloader, 4K Video Downloader+ Jul 7, 2026
fleet-release
fleet-release previously approved these changes Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Script Diff Results

ee/maintained-apps/outputs/3df-zephyr-free/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/4k-video-downloader-plus/windows.json

=== Install Script (no changes) ===
=== Uninstall // 5243d726 -> 9283b0c4 ===

--- /tmp/old.Ujzu2a	2026-07-07 18:23:59.606211784 +0000
+++ /tmp/new.E4m9bV	2026-07-07 18:23:59.606211784 +0000
@@ -1,29 +1,29 @@
-# Uninstalls the 4K Video Downloader+ WiX "burn" bundle.
+# Uninstalls 4K Video Downloader+.
 #
-# The app registers a bundle ARP entry (DisplayName "4K Video Downloader+",
-# publisher "InterPromo GMBH") whose UninstallString points at the cached
-# bootstrapper .exe. Burn bundles uninstall by running that .exe with
-# /uninstall /quiet /norestart -- never via msiexec. We look the entry up by
-# its exact DisplayName; the "+" keeps this from matching the separate MSI-based
+# The app is a WiX "burn" bundle that chains an MSI, and the ARP entry with
+# DisplayName "4K Video Downloader+" may be either the bundle (an .exe
+# UninstallString, needs /uninstall /quiet /norestart) or the chained MSI
+# (an MsiExec.exe /X{ProductCode} UninstallString, needs /qn /norestart --
+# NOT /uninstall, which is invalid for msiexec). Handle both shapes. The "+"
+# in the exact-match name keeps this from touching the separate MSI-based
 # "4K Video Downloader" product.
 
 $softwareName = "4K Video Downloader+"
 
-function Invoke-Uninstaller {
-    param([string]$exe, [string]$exeArgs)
-    if ($exeArgs -notmatch '/uninstall') { $exeArgs = "/uninstall $exeArgs" }
-    if ($exeArgs -notmatch '/quiet')     { $exeArgs = "$exeArgs /quiet" }
-    if ($exeArgs -notmatch '/norestart') { $exeArgs = "$exeArgs /norestart" }
-    $exeArgs = $exeArgs.Trim()
-    Write-Host "Uninstall command: $exe"
-    Write-Host "Uninstall args: $exeArgs"
-    $process = Start-Process -FilePath $exe -ArgumentList $exeArgs -NoNewWindow -PassThru -Wait
-    return $process.ExitCode
-}
-
 $machineKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
 $machineKey32on64 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
 
+function Split-UninstallString {
+    param([string]$raw)
+    # Parse into executable + args, handling quoted/unquoted/bare shapes.
+    if ($raw -match '^\s*"([^"]+)"\s*(.*)$') {
+        return @($matches[1], $matches[2].Trim())
+    } elseif ($raw -match '(?i)^\s*(.+?\.exe)\s*(.*)$') {
+        return @($matches[1], $matches[2].Trim())
+    }
+    return @($raw, "")
+}
+
 $exitCode = $null
 
 try {
@@ -33,24 +33,35 @@
     -ErrorAction SilentlyContinue |
         ForEach-Object { Get-ItemProperty $_.PSPath }
 
-foreach ($key in $uninstallKeys) {
-    if ($key.DisplayName -eq $softwareName) {
-        $raw = $key.QuietUninstallString
-        if (-not $raw) { $raw = $key.UninstallString }
-        if (-not $raw) { continue }
-
-        # Parse into executable + args, handling quoted/unquoted/bare shapes.
-        if ($raw -match '^\s*"([^"]+)"\s*(.*)$') {
-            $exe = $matches[1]; $exeArgs = $matches[2].Trim()
-        } elseif ($raw -match '(?i)^\s*(.+?\.exe)\s*(.*)$') {
-            $exe = $matches[1]; $exeArgs = $matches[2].Trim()
-        } else {
-            $exe = $raw; $exeArgs = ""
-        }
+[array]$entries = $uninstallKeys | Where-Object { $_.DisplayName -eq $softwareName }
 
-        $exitCode = Invoke-Uninstaller -exe $exe -exeArgs $exeArgs
-        break
+# Prefer the burn bundle entry (non-msiexec .exe uninstaller): it removes the
+# whole chain, including the MSI. Fall back to the chained MSI entry.
+$bundle = $entries | Where-Object {
+    $raw = if ($_.QuietUninstallString) { $_.QuietUninstallString } else { $_.UninstallString }
+    $raw -and $raw -notmatch '(?i)msiexec'
+} | Select-Object -First 1
+$entry = if ($bundle) { $bundle } else { $entries | Select-Object -First 1 }
+
+if ($entry) {
+    $raw = if ($entry.QuietUninstallString) { $entry.QuietUninstallString } else { $entry.UninstallString }
+    $exe, $exeArgs = Split-UninstallString -raw $raw
+
+    if ($exe -match '(?i)msiexec') {
+        if ($exeArgs -notmatch '(?i)/(x|uninstall)') { $exeArgs = "/X $exeArgs" }
+        if ($exeArgs -notmatch '(?i)/(qn|quiet)') { $exeArgs = "$exeArgs /qn" }
+        if ($exeArgs -notmatch '(?i)/norestart') { $exeArgs = "$exeArgs /norestart" }
+    } else {
+        if ($exeArgs -notmatch '/uninstall') { $exeArgs = "/uninstall $exeArgs" }
+        if ($exeArgs -notmatch '/quiet')     { $exeArgs = "$exeArgs /quiet" }
+        if ($exeArgs -notmatch '/norestart') { $exeArgs = "$exeArgs /norestart" }
     }
+    $exeArgs = $exeArgs.Trim()
+
+    Write-Host "Uninstall command: $exe"
+    Write-Host "Uninstall args: $exeArgs"
+    $process = Start-Process -FilePath $exe -ArgumentList $exeArgs -NoNewWindow -PassThru -Wait
+    $exitCode = $process.ExitCode
 }
 
 } catch {

ee/maintained-apps/outputs/4k-video-downloader/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

The classic 4.x app is in maintenance mode; 4K Video Downloader+ is the
actively developed successor, so the catalog offers only the + app.
@allenhouchins allenhouchins changed the title Add Windows FMAs (digit batch): 3DF Zephyr Free, 4K Video Downloader, 4K Video Downloader+ Add Windows FMAs (digit batch): 3DF Zephyr Free, 4K Video Downloader+ Jul 7, 2026
fleet-release
fleet-release previously approved these changes Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Script Diff Results

ee/maintained-apps/outputs/3df-zephyr-free/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/4k-video-downloader-plus/windows.json

=== Install Script (no changes) ===
=== Uninstall // 5243d726 -> 9283b0c4 ===

--- /tmp/old.HB52CZ	2026-07-07 18:36:33.599429034 +0000
+++ /tmp/new.m8lNlZ	2026-07-07 18:36:33.599429034 +0000
@@ -1,29 +1,29 @@
-# Uninstalls the 4K Video Downloader+ WiX "burn" bundle.
+# Uninstalls 4K Video Downloader+.
 #
-# The app registers a bundle ARP entry (DisplayName "4K Video Downloader+",
-# publisher "InterPromo GMBH") whose UninstallString points at the cached
-# bootstrapper .exe. Burn bundles uninstall by running that .exe with
-# /uninstall /quiet /norestart -- never via msiexec. We look the entry up by
-# its exact DisplayName; the "+" keeps this from matching the separate MSI-based
+# The app is a WiX "burn" bundle that chains an MSI, and the ARP entry with
+# DisplayName "4K Video Downloader+" may be either the bundle (an .exe
+# UninstallString, needs /uninstall /quiet /norestart) or the chained MSI
+# (an MsiExec.exe /X{ProductCode} UninstallString, needs /qn /norestart --
+# NOT /uninstall, which is invalid for msiexec). Handle both shapes. The "+"
+# in the exact-match name keeps this from touching the separate MSI-based
 # "4K Video Downloader" product.
 
 $softwareName = "4K Video Downloader+"
 
-function Invoke-Uninstaller {
-    param([string]$exe, [string]$exeArgs)
-    if ($exeArgs -notmatch '/uninstall') { $exeArgs = "/uninstall $exeArgs" }
-    if ($exeArgs -notmatch '/quiet')     { $exeArgs = "$exeArgs /quiet" }
-    if ($exeArgs -notmatch '/norestart') { $exeArgs = "$exeArgs /norestart" }
-    $exeArgs = $exeArgs.Trim()
-    Write-Host "Uninstall command: $exe"
-    Write-Host "Uninstall args: $exeArgs"
-    $process = Start-Process -FilePath $exe -ArgumentList $exeArgs -NoNewWindow -PassThru -Wait
-    return $process.ExitCode
-}
-
 $machineKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
 $machineKey32on64 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
 
+function Split-UninstallString {
+    param([string]$raw)
+    # Parse into executable + args, handling quoted/unquoted/bare shapes.
+    if ($raw -match '^\s*"([^"]+)"\s*(.*)$') {
+        return @($matches[1], $matches[2].Trim())
+    } elseif ($raw -match '(?i)^\s*(.+?\.exe)\s*(.*)$') {
+        return @($matches[1], $matches[2].Trim())
+    }
+    return @($raw, "")
+}
+
 $exitCode = $null
 
 try {
@@ -33,24 +33,35 @@
     -ErrorAction SilentlyContinue |
         ForEach-Object { Get-ItemProperty $_.PSPath }
 
-foreach ($key in $uninstallKeys) {
-    if ($key.DisplayName -eq $softwareName) {
-        $raw = $key.QuietUninstallString
-        if (-not $raw) { $raw = $key.UninstallString }
-        if (-not $raw) { continue }
-
-        # Parse into executable + args, handling quoted/unquoted/bare shapes.
-        if ($raw -match '^\s*"([^"]+)"\s*(.*)$') {
-            $exe = $matches[1]; $exeArgs = $matches[2].Trim()
-        } elseif ($raw -match '(?i)^\s*(.+?\.exe)\s*(.*)$') {
-            $exe = $matches[1]; $exeArgs = $matches[2].Trim()
-        } else {
-            $exe = $raw; $exeArgs = ""
-        }
+[array]$entries = $uninstallKeys | Where-Object { $_.DisplayName -eq $softwareName }
 
-        $exitCode = Invoke-Uninstaller -exe $exe -exeArgs $exeArgs
-        break
+# Prefer the burn bundle entry (non-msiexec .exe uninstaller): it removes the
+# whole chain, including the MSI. Fall back to the chained MSI entry.
+$bundle = $entries | Where-Object {
+    $raw = if ($_.QuietUninstallString) { $_.QuietUninstallString } else { $_.UninstallString }
+    $raw -and $raw -notmatch '(?i)msiexec'
+} | Select-Object -First 1
+$entry = if ($bundle) { $bundle } else { $entries | Select-Object -First 1 }
+
+if ($entry) {
+    $raw = if ($entry.QuietUninstallString) { $entry.QuietUninstallString } else { $entry.UninstallString }
+    $exe, $exeArgs = Split-UninstallString -raw $raw
+
+    if ($exe -match '(?i)msiexec') {
+        if ($exeArgs -notmatch '(?i)/(x|uninstall)') { $exeArgs = "/X $exeArgs" }
+        if ($exeArgs -notmatch '(?i)/(qn|quiet)') { $exeArgs = "$exeArgs /qn" }
+        if ($exeArgs -notmatch '(?i)/norestart') { $exeArgs = "$exeArgs /norestart" }
+    } else {
+        if ($exeArgs -notmatch '/uninstall') { $exeArgs = "/uninstall $exeArgs" }
+        if ($exeArgs -notmatch '/quiet')     { $exeArgs = "$exeArgs /quiet" }
+        if ($exeArgs -notmatch '/norestart') { $exeArgs = "$exeArgs /norestart" }
     }
+    $exeArgs = $exeArgs.Trim()
+
+    Write-Host "Uninstall command: $exe"
+    Write-Host "Uninstall args: $exeArgs"
+    $process = Start-Process -FilePath $exe -ArgumentList $exeArgs -NoNewWindow -PassThru -Wait
+    $exitCode = $process.ExitCode
 }
 
 } catch {

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Script Diff Results

ee/maintained-apps/outputs/3df-zephyr-free/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/4k-video-downloader-plus/windows.json

=== Install Script (no changes) ===
=== Uninstall // 5243d726 -> 9283b0c4 ===

--- /tmp/old.jne7zz	2026-07-07 18:48:34.146434005 +0000
+++ /tmp/new.sra156	2026-07-07 18:48:34.146434005 +0000
@@ -1,29 +1,29 @@
-# Uninstalls the 4K Video Downloader+ WiX "burn" bundle.
+# Uninstalls 4K Video Downloader+.
 #
-# The app registers a bundle ARP entry (DisplayName "4K Video Downloader+",
-# publisher "InterPromo GMBH") whose UninstallString points at the cached
-# bootstrapper .exe. Burn bundles uninstall by running that .exe with
-# /uninstall /quiet /norestart -- never via msiexec. We look the entry up by
-# its exact DisplayName; the "+" keeps this from matching the separate MSI-based
+# The app is a WiX "burn" bundle that chains an MSI, and the ARP entry with
+# DisplayName "4K Video Downloader+" may be either the bundle (an .exe
+# UninstallString, needs /uninstall /quiet /norestart) or the chained MSI
+# (an MsiExec.exe /X{ProductCode} UninstallString, needs /qn /norestart --
+# NOT /uninstall, which is invalid for msiexec). Handle both shapes. The "+"
+# in the exact-match name keeps this from touching the separate MSI-based
 # "4K Video Downloader" product.
 
 $softwareName = "4K Video Downloader+"
 
-function Invoke-Uninstaller {
-    param([string]$exe, [string]$exeArgs)
-    if ($exeArgs -notmatch '/uninstall') { $exeArgs = "/uninstall $exeArgs" }
-    if ($exeArgs -notmatch '/quiet')     { $exeArgs = "$exeArgs /quiet" }
-    if ($exeArgs -notmatch '/norestart') { $exeArgs = "$exeArgs /norestart" }
-    $exeArgs = $exeArgs.Trim()
-    Write-Host "Uninstall command: $exe"
-    Write-Host "Uninstall args: $exeArgs"
-    $process = Start-Process -FilePath $exe -ArgumentList $exeArgs -NoNewWindow -PassThru -Wait
-    return $process.ExitCode
-}
-
 $machineKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
 $machineKey32on64 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
 
+function Split-UninstallString {
+    param([string]$raw)
+    # Parse into executable + args, handling quoted/unquoted/bare shapes.
+    if ($raw -match '^\s*"([^"]+)"\s*(.*)$') {
+        return @($matches[1], $matches[2].Trim())
+    } elseif ($raw -match '(?i)^\s*(.+?\.exe)\s*(.*)$') {
+        return @($matches[1], $matches[2].Trim())
+    }
+    return @($raw, "")
+}
+
 $exitCode = $null
 
 try {
@@ -33,24 +33,35 @@
     -ErrorAction SilentlyContinue |
         ForEach-Object { Get-ItemProperty $_.PSPath }
 
-foreach ($key in $uninstallKeys) {
-    if ($key.DisplayName -eq $softwareName) {
-        $raw = $key.QuietUninstallString
-        if (-not $raw) { $raw = $key.UninstallString }
-        if (-not $raw) { continue }
-
-        # Parse into executable + args, handling quoted/unquoted/bare shapes.
-        if ($raw -match '^\s*"([^"]+)"\s*(.*)$') {
-            $exe = $matches[1]; $exeArgs = $matches[2].Trim()
-        } elseif ($raw -match '(?i)^\s*(.+?\.exe)\s*(.*)$') {
-            $exe = $matches[1]; $exeArgs = $matches[2].Trim()
-        } else {
-            $exe = $raw; $exeArgs = ""
-        }
+[array]$entries = $uninstallKeys | Where-Object { $_.DisplayName -eq $softwareName }
 
-        $exitCode = Invoke-Uninstaller -exe $exe -exeArgs $exeArgs
-        break
+# Prefer the burn bundle entry (non-msiexec .exe uninstaller): it removes the
+# whole chain, including the MSI. Fall back to the chained MSI entry.
+$bundle = $entries | Where-Object {
+    $raw = if ($_.QuietUninstallString) { $_.QuietUninstallString } else { $_.UninstallString }
+    $raw -and $raw -notmatch '(?i)msiexec'
+} | Select-Object -First 1
+$entry = if ($bundle) { $bundle } else { $entries | Select-Object -First 1 }
+
+if ($entry) {
+    $raw = if ($entry.QuietUninstallString) { $entry.QuietUninstallString } else { $entry.UninstallString }
+    $exe, $exeArgs = Split-UninstallString -raw $raw
+
+    if ($exe -match '(?i)msiexec') {
+        if ($exeArgs -notmatch '(?i)/(x|uninstall)') { $exeArgs = "/X $exeArgs" }
+        if ($exeArgs -notmatch '(?i)/(qn|quiet)') { $exeArgs = "$exeArgs /qn" }
+        if ($exeArgs -notmatch '(?i)/norestart') { $exeArgs = "$exeArgs /norestart" }
+    } else {
+        if ($exeArgs -notmatch '/uninstall') { $exeArgs = "/uninstall $exeArgs" }
+        if ($exeArgs -notmatch '/quiet')     { $exeArgs = "$exeArgs /quiet" }
+        if ($exeArgs -notmatch '/norestart') { $exeArgs = "$exeArgs /norestart" }
     }
+    $exeArgs = $exeArgs.Trim()
+
+    Write-Host "Uninstall command: $exe"
+    Write-Host "Uninstall args: $exeArgs"
+    $process = Start-Process -FilePath $exe -ArgumentList $exeArgs -NoNewWindow -PassThru -Wait
+    $exitCode = $process.ExitCode
 }
 
 } catch {

@allenhouchins
allenhouchins marked this pull request as ready for review July 7, 2026 18:58
@allenhouchins
allenhouchins requested a review from a team as a code owner July 7, 2026 18:58
Copilot AI review requested due to automatic review settings July 7, 2026 18:58
@fleet-release
fleet-release requested a review from eashaw July 7, 2026 18:58

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@allenhouchins
allenhouchins merged commit ceeaf9e into main Jul 7, 2026
29 of 31 checks passed
@allenhouchins
allenhouchins deleted the add-windows-fmas-digit-batch branch July 7, 2026 18:58
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 713651c6-7e5e-4df4-9ba3-c3b3c69cfea0

📥 Commits

Reviewing files that changed from the base of the PR and between 28f4da0 and ab63f76.

⛔ Files ignored due to path filters (2)
  • website/assets/images/app-icon-3df-zephyr-free-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-4k-video-downloader-plus-60x60@2x.png is excluded by !**/*.png
📒 Files selected for processing (12)
  • ee/maintained-apps/inputs/winget/3df-zephyr-free.json
  • ee/maintained-apps/inputs/winget/4k-video-downloader-plus.json
  • ee/maintained-apps/inputs/winget/scripts/3df-zephyr-free_install.ps1
  • ee/maintained-apps/inputs/winget/scripts/3df-zephyr-free_uninstall.ps1
  • ee/maintained-apps/inputs/winget/scripts/4k-video-downloader-plus_install.ps1
  • ee/maintained-apps/inputs/winget/scripts/4k-video-downloader-plus_uninstall.ps1
  • ee/maintained-apps/outputs/3df-zephyr-free/windows.json
  • ee/maintained-apps/outputs/4k-video-downloader-plus/windows.json
  • ee/maintained-apps/outputs/apps.json
  • frontend/pages/SoftwarePage/components/icons/3DfZephyrFree.tsx
  • frontend/pages/SoftwarePage/components/icons/FourKVideoDownloaderPlus.tsx
  • frontend/pages/SoftwarePage/components/icons/index.ts

Walkthrough

This PR adds two new maintained Windows applications: "3DF Zephyr Free" and "4K Video Downloader+". For each app, it introduces a winget input JSON manifest, install and uninstall PowerShell scripts (Inno Setup silent flags for the former, WiX burn/MSI handling with reboot-code awareness for the latter), a generated output windows.json definition with SQL-based presence/patch detection, and a new entry in apps.json. Frontend changes add two SVG icon components and extend the software name-to-icon mapping.

Changes

Cohort / File(s) Summary
3DF Zephyr Free input/scripts/output New winget manifest, install/uninstall PowerShell scripts using Inno Setup silent flags, generated windows.json definition, apps.json entry
4K Video Downloader+ input/scripts/output New winget manifest, install/uninstall PowerShell scripts handling WiX burn bootstrapper and reboot exit codes, generated windows.json definition, apps.json entry
Frontend icons New ThreeDfZephyrFree and FourKVideoDownloaderPlus SVG icon components; updated SOFTWARE_NAME_TO_ICON_MAP with imports and a new fuzzy-match entry

Related issues: None specified.

Related PRs: None specified.

Possibly related PRs

  • fleetdm/fleet#46988: Adds entries to ee/maintained-apps/outputs/apps.json and extends SOFTWARE_NAME_TO_ICON_MAP for other apps.
  • fleetdm/fleet#48762: Adds new Windows app entries plus icon components and extends SOFTWARE_NAME_TO_ICON_MAP similarly.
  • fleetdm/fleet#47659: Adds new Windows app icons/components and extends the shared SOFTWARE_NAME_TO_ICON_MAP.

Suggested labels: maintained-apps, winget, frontend

Suggested reviewers: None specified.

🐰 Two new apps hop into the fold,
Zephyr and Downloader, scripts unrolled,
Silent installs, quiet uninstalls too,
Icons rendered fresh and new,
The carrot patch of apps.json grows bold.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-windows-fmas-digit-batch

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.

Copilot AI 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.

Pull request overview

Expands Fleet’s Windows Fleet-maintained apps (FMA) catalog by adding 3DF Zephyr Free and 4K Video Downloader+, including the required install/uninstall scripts, generated catalog outputs, and frontend software icons so the titles display with the correct branding in the UI.

Changes:

  • Added new Windows FMA definitions (inputs) and published catalog entries (outputs) for 3DF Zephyr Free and 4K Video Downloader+.
  • Introduced install/uninstall PowerShell scripts for both apps (including bundle/MSI uninstall handling for 4K Video Downloader+).
  • Added new frontend icon components and mapped software titles to those icons.

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
frontend/pages/SoftwarePage/components/icons/index.ts Registers new icon components and maps software names to icons.
frontend/pages/SoftwarePage/components/icons/FourKVideoDownloaderPlus.tsx Adds the 4K Video Downloader+ icon component.
frontend/pages/SoftwarePage/components/icons/3DfZephyrFree.tsx Adds the 3DF Zephyr Free icon component.
ee/maintained-apps/outputs/apps.json Adds the two apps to the published Windows maintained-apps catalog list.
ee/maintained-apps/outputs/4k-video-downloader-plus/windows.json Publishes installer metadata + embedded scripts for 4K Video Downloader+.
ee/maintained-apps/outputs/3df-zephyr-free/windows.json Publishes installer metadata + embedded scripts for 3DF Zephyr Free.
ee/maintained-apps/inputs/winget/scripts/4k-video-downloader-plus_uninstall.ps1 Uninstall script that selects bundle vs MSI ARP entry and runs silent uninstall.
ee/maintained-apps/inputs/winget/scripts/4k-video-downloader-plus_install.ps1 Silent installer script for the burn bundle.
ee/maintained-apps/inputs/winget/scripts/3df-zephyr-free_uninstall.ps1 Uninstall script for versioned Inno Setup DisplayName.
ee/maintained-apps/inputs/winget/scripts/3df-zephyr-free_install.ps1 Silent installer script for the Inno Setup EXE.
ee/maintained-apps/inputs/winget/4k-video-downloader-plus.json Winget input definition for 4K Video Downloader+.
ee/maintained-apps/inputs/winget/3df-zephyr-free.json Winget input definition for 3DF Zephyr Free (with fuzzy name matching).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +50 to +53
if ($exe -match '(?i)msiexec') {
if ($exeArgs -notmatch '(?i)/(x|uninstall)') { $exeArgs = "/X $exeArgs" }
if ($exeArgs -notmatch '(?i)/(qn|quiet)') { $exeArgs = "$exeArgs /qn" }
if ($exeArgs -notmatch '(?i)/norestart') { $exeArgs = "$exeArgs /norestart" }
}
],
"refs": {
"9283b0c4": "# Uninstalls 4K Video Downloader+.\n#\n# The app is a WiX \"burn\" bundle that chains an MSI, and the ARP entry with\n# DisplayName \"4K Video Downloader+\" may be either the bundle (an .exe\n# UninstallString, needs /uninstall /quiet /norestart) or the chained MSI\n# (an MsiExec.exe /X{ProductCode} UninstallString, needs /qn /norestart --\n# NOT /uninstall, which is invalid for msiexec). Handle both shapes. The \"+\"\n# in the exact-match name keeps this from touching the separate MSI-based\n# \"4K Video Downloader\" product.\n\n$softwareName = \"4K Video Downloader+\"\n\n$machineKey = 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*'\n$machineKey32on64 = 'HKLM:\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*'\n\nfunction Split-UninstallString {\n param([string]$raw)\n # Parse into executable + args, handling quoted/unquoted/bare shapes.\n if ($raw -match '^\\s*\"([^\"]+)\"\\s*(.*)$') {\n return @($matches[1], $matches[2].Trim())\n } elseif ($raw -match '(?i)^\\s*(.+?\\.exe)\\s*(.*)$') {\n return @($matches[1], $matches[2].Trim())\n }\n return @($raw, \"\")\n}\n\n$exitCode = $null\n\ntry {\n\n[array]$uninstallKeys = Get-ChildItem `\n -Path @($machineKey, $machineKey32on64) `\n -ErrorAction SilentlyContinue |\n ForEach-Object { Get-ItemProperty $_.PSPath }\n\n[array]$entries = $uninstallKeys | Where-Object { $_.DisplayName -eq $softwareName }\n\n# Prefer the burn bundle entry (non-msiexec .exe uninstaller): it removes the\n# whole chain, including the MSI. Fall back to the chained MSI entry.\n$bundle = $entries | Where-Object {\n $raw = if ($_.QuietUninstallString) { $_.QuietUninstallString } else { $_.UninstallString }\n $raw -and $raw -notmatch '(?i)msiexec'\n} | Select-Object -First 1\n$entry = if ($bundle) { $bundle } else { $entries | Select-Object -First 1 }\n\nif ($entry) {\n $raw = if ($entry.QuietUninstallString) { $entry.QuietUninstallString } else { $entry.UninstallString }\n $exe, $exeArgs = Split-UninstallString -raw $raw\n\n if ($exe -match '(?i)msiexec') {\n if ($exeArgs -notmatch '(?i)/(x|uninstall)') { $exeArgs = \"/X $exeArgs\" }\n if ($exeArgs -notmatch '(?i)/(qn|quiet)') { $exeArgs = \"$exeArgs /qn\" }\n if ($exeArgs -notmatch '(?i)/norestart') { $exeArgs = \"$exeArgs /norestart\" }\n } else {\n if ($exeArgs -notmatch '/uninstall') { $exeArgs = \"/uninstall $exeArgs\" }\n if ($exeArgs -notmatch '/quiet') { $exeArgs = \"$exeArgs /quiet\" }\n if ($exeArgs -notmatch '/norestart') { $exeArgs = \"$exeArgs /norestart\" }\n }\n $exeArgs = $exeArgs.Trim()\n\n Write-Host \"Uninstall command: $exe\"\n Write-Host \"Uninstall args: $exeArgs\"\n $process = Start-Process -FilePath $exe -ArgumentList $exeArgs -NoNewWindow -PassThru -Wait\n $exitCode = $process.ExitCode\n}\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n\nif ($null -eq $exitCode) {\n Write-Host \"Uninstall entry not found for '$softwareName'.\"\n Exit 1\n}\n\nWrite-Host \"Uninstall exit code: $exitCode\"\n# 0 = success, 3010 = success but reboot required, 1641 = reboot initiated\nif ($exitCode -eq 3010 -or $exitCode -eq 1641) { Exit 0 }\nExit $exitCode\n",
allenhouchins added a commit that referenced this pull request Jul 8, 2026
**Related issue:** N/A — part of the Windows Fleet-maintained apps
catalog expansion (letter B batch; follows #48872 and #48881).

Adds six new Windows Fleet-maintained apps:

| App | winget package | Installer | Notes |
|-----|----------------|-----------|-------|
| BandiView | `Bandisoft.BandiView` | EXE (NSIS-style), machine, x64 |
Unversioned InstallerUrl → `ignore_hash` (URL verified to serve the
binary reliably without Referer tricks). DisplayName "BandiView" stable
across releases. |
| BleachBit | `BleachBit.BleachBit` | EXE (NsisMultiUser), machine, x86
| Install script passes the case-sensitive `/allusers /S` — without
`/allusers` the NsisMultiUser installer's scope is ambiguous. winget
declares a VCRedist 2010 x86 dependency Fleet can't satisfy; noted as a
caveat. |
| Bulk Crap Uninstaller | `Klocman.BulkCrapUninstaller` | EXE (Inno),
machine, x86 | ARP DisplayName is versioned ("BCUninstaller 6.2.0.0")
and never matches the friendly name, so `unique_identifier`
"BCUninstaller" + `fuzzy_match_name`. Registry version is 4-part vs
winget's "6.2" — `version_compare` pads, verified consistent. |
| BrowserStackLocal | `BrowserStack.BrowserStackLocal` | MSI (WiX),
machine, x64 | Clean MSI (ALLUSERS=1, identity verified via msiinfo).
Unversioned InstallerUrl → `ignore_hash`. |
| Burp Suite Professional | `PortSwigger.BurpSuite.Professional` | EXE
(install4j), machine, x64 | Mirrors the existing Burp Suite Community
FMA: `-q -Dinstall4j.suppressUnattendedReboot=true` plus the
load-bearing `-dir` into Program Files (install4j defaults to per-user
otherwise). DisplayName is versioned **without** "Edition" ("Burp Suite
Professional 2026.3.3"), unlike Community — fuzzy pattern `Burp Suite
Professional %` can't collide with Community's. |
| Bytello Share | `Bytello.BytelloShare` | EXE (NSIS), machine, x86 |
Uses the nullsoft `agent=d` variant whose ARP identity ("Bytello Share"
/ publisher "Bytello Share") matches real-world inventory; the zip
variant registers a different name ("BytelloShare") and its nested-MSI
path is version-pinned and already stale. Vendor URL is a latest-pointer
already ahead of winget → `ignore_hash`. **Note:** the input says
`installer_type: "msi"` — the ingester classifies this nullsoft entry as
msi because its URL has no file extension (vendor-type → URL-extension →
machine-scope fallback chain in `ingester.go`); the custom scripts
handle the actual NSIS exe. |

Considered but **not** added (recorded in the workstream tracker):
- **Bambu Studio** (`Bambulab.Bambustudio`): the uninstaller shows a
keep-user-data confirmation dialog even with `/S` (deployment guides
work around it with Send-Keys, impossible in a SYSTEM session) — same
failure class that disqualified Adobe AIR in the letter A batch.
- **Bridge Designer** (`StephenRessler.BridgeDesigner`): installer URL
404s (file removed from SourceForge) and the desktop product was
discontinued July 1, 2026 in favor of a browser-based edition.
- **BurnAware Free** (`Burnaware.BurnAwareFree`): the vendor deletes
each old release URL — the winget-pinned installer already redirects to
their homepage, so pinned downloads break every release cycle.

Registry identities were verified per app (msiinfo Property tables for
MSIs; vendor installer sources, winget AppsAndFeaturesEntries, and
uninstall-database corroboration for EXEs). Installer SHAs verified
against manifests where URLs are version-pinned; unversioned URLs use
`ignore_hash` per the TeamViewer/Chrome precedent. Icons generated via
`tools/software/icons/generate-icons.sh`.

# 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops

## Testing

- [ ] QA'd all new/changed functionality manually (relying on the FMA CI
validator for Windows install/uninstall validation)
allenhouchins added a commit that referenced this pull request Jul 8, 2026
**Related issue:** N/A — part of the Windows Fleet-maintained apps
catalog expansion (letter C batch; follows #48872, #48881, #48950).

Adds seventeen new Windows Fleet-maintained apps:

| App | winget package | Installer | Notes |
|-----|----------------|-----------|-------|
| Advanced Installer | `Caphyon.AdvancedInstaller` | MSI, machine, x64 |
Versioned ARP name ("Advanced Installer 23.8") → fuzzy match. |
| Certify The Web | `CertifyTheWeb.CertifySSLManager` | Inno, machine,
x64 | ARP name is "Certify Certificate Manager version 7.1.0.0" (product
renamed at v7) → fuzzy on "Certify Certificate Manager". |
| Chatbox | `Bin-Huang.Chatbox` | NSIS (electron-builder), machine, x64
| `/allusers /S`; custom exists query excludes the separate Chatbox
Community Edition; uninstall matches `Chatbox [0-9]*`. |
| Citrix Workspace | `Citrix.Workspace` | Vendor bootstrapper EXE,
machine, x86 universal | ARP name "Citrix Workspace \<YYMM\>" → fuzzy.
Uninstall runs the registered TrolleyExpress with `/uninstall /cleanup
/silent`; 3010 treated as success. See "not added" for the LTSR caveat.
|
| CPU-Z | `CPUID.CPU-Z` | Inno, machine | `installer_locale` pinned to
en-US (manifest also carries zh-CN); `/ALLUSERS` added to Inno switches;
versioned ARP name → fuzzy. |
| CodeMeter Runtime Kit | `Wibu-Systems.CodeMeterRuntimeKit` | Vendor
bootstrapper embedding MSI, x64 | `installer_scope: ""` (manifest
declares no scope; embedded MSI is per-machine). Install `/q /nosplash
/ComponentArgs "*":"/quiet /norestart"`; uninstall via msiexec by ARP
name prefix. Identity verified by carving the embedded MSI. |
| ClipboardFusion | `BinaryFortress.ClipboardFusion` | Inno, machine,
x64 | ARP name carries a locale-dependent "(64-bit)" suffix → fuzzy
pattern `ClipboardFusion%`; `/LAUNCHAFTER=0` prevents post-install
launch. |
| CloudShow | `BinaryFortress.CloudShow` | Inno, machine, x64 | Same
Binary Fortress framework; ARP name "CloudShow Launcher (64-bit)" is
framework-inferred (no third-party corroboration exists) — the CI
validator is the confirmation. |
| ClockAssist | `ClockAssist.ClockAssist` | MSI, machine, x64 |
Latest-pointer URL but actively maintained manifest (bot replaces the
single version dir ~6-weekly) → `ignore_hash`. |
| Crestron AirMedia | `Crestron.AirMedia` | MSI, machine, x86 | ARP name
is "Crestron AirMedia Machine-Wide Installer" (Teams-style per-user
stamping); identity from msiinfo. |
| Crestron AirMedia Peripherals | `Crestron.AirMediaPeripherals` | MSI,
machine, x64 | Clean MSI. LaunchCondition fails install (1603) on hosts
with a pending reboot — noted. |
| CrisisGo | `CrisisGo.CrisisGo` | InstallShield Basic MSI, machine, x86
| Custom install script passes `ISSETUPDRIVEN=1` to defuse the "must run
setup.exe" guard (vendor ships this bare MSI for network deployment;
winget sandbox validates it). |
| Cyberduck CLI | `Iterate.CyberduckCLI` | MSI, machine, x64 | Clean WiX
MSI. winget lags the vendor by ~4 releases, but pinned URLs stay live,
so installs work — just not bleeding-edge. |
| Cisco Webex Recorder and Player | `Cisco.WebexRecorderAndPlayer` |
InstallShield MSI, machine, x86 | Legacy WRF player but actively updated
by Cisco (8 winget bumps in 10 months); latest-pointer URL →
`ignore_hash`. |
| Creative Force Kelvin | `CreativeForce.Kelvin` | WiX MSI, machine, x64
| Manifest offers per-user NSIS, machine NSIS, and MSI — the MSI is
selected (unversioned ARP name "Kelvin"). |
| Creative Force Triad | `CreativeForce.Triad` | NSIS
(electron-builder), machine, x64 | `/allusers /S`; versioned ARP name →
fuzzy. |
| Cube Browser | `RystadEnergy.CubeBrowser` | WiX burn bundle, machine,
x64 | Bundle Arp manifest carved from the installer ("Cube Browser (64
bit)"); dual-mode uninstall (prefers bundle entry, msiexec fallback)
since bundle + chained MSI may both register the same name. |

Considered but **not** added (recorded in the workstream tracker):
- **Citrix Workspace app LTSR** (`Citrix.Workspace.LTSR`): the LTSR and
current tracks register the **identical** ARP key
(`CitrixOnlinePluginPackWeb`) and the same "Citrix Workspace \<YYMM\>"
DisplayName with no LTSR marker — the only discriminator is a registry
value outside the programs table, so Fleet inventory cannot tell the
tracks apart. An LTSR FMA would cross-match current-release installs
(and vice versa). **Consequence for the shipped Citrix Workspace FMA:**
hosts running LTSR will match it and may show "update available" toward
the current release — flagged here for reviewer judgment.
- **Charles** (`XK72.Charles`): all winget 5.x manifests are per-user
MSIX only. The vendor ships a machine-scope MSI for 5.2 but it isn't
indexed in winget; revisit if the manifest adds it.
- **Calibrite Profiler** (`Calibrite.PROFILER`): abandoned winget
manifest — one version dir ever while the vendor is five releases and a
major version ahead.
- **Cloud Drive Mapper** (`IAMCloud.CloudDriveMapperV3`): latest-pointer
URL whose manifest SHA is already stale, and the MSI sets
`ARPSYSTEMCOMPONENT=1`, hiding the ARP entry from inventory entirely.
- **CloudCompare** (`CloudCompare.CloudCompare`): registry
DisplayVersion (and the winget PackageVersion itself) embed a
parenthetical date — "2.13.2 (07-06-2024)" — which breaks version
comparison; manifest also stale.
- **Classic Shell** (`IvoSoft.ClassicShell`): development ended in 2017;
superseded by Open-Shell. (CutePDF Writer was already deferred in the
letter A batch.)

Identities verified per app (msiinfo Property tables; burn bundle Arp
manifests carved from installers; AppxManifest/electron-builder sources;
uninstall-database corroboration). SHAs verified against manifests for
pinned URLs; `ignore_hash` used only where the manifest is demonstrably
actively maintained. Icons via `tools/software/icons/generate-icons.sh`;
the pre-existing CitrixWorkspace icon component is reused untouched.

# 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops

## Testing

- [ ] QA'd all new/changed functionality manually (relying on the FMA CI
validator for Windows install/uninstall validation)
allenhouchins added a commit that referenced this pull request Jul 10, 2026
**Related issue:** N/A — part of the Windows Fleet-maintained apps
catalog expansion (letter D batch; follows #48872, #48881, #48950,
#48969).

Adds twelve new Windows Fleet-maintained apps:

| App | winget package | Installer | Notes |
|-----|----------------|-----------|-------|
| DataSpell | `JetBrains.DataSpell` | NSIS (install4j), machine, x64 |
Mirrors the DataGrip FMA pattern: `fuzzy_match_name` +
`use_display_version_for_patch` (registry version is a JetBrains build
number; marketing version parsed from the name). |
| dnGrep | `dnGrep.dnGrep` | MSI, machine, x64 | Versioned+arch ARP name
("dnGrep 5.0.9 (x64)") → fuzzy match. |
| Draftable Desktop | `Draftable.Draftable` | MSI, machine, x64 | Uses
the machine-scope `DraftableDesktopSystem` MSI. **Caveat:** hard winget
dependency on .NET 10 Desktop Runtime — installs fine but won't launch
without it (same class as the BleachBit VCRedist dependency). |
| dRofus | `dRofus.dRofus` | MSI, machine, x64 | Versioned ARP name
("dRofus 2.18") → fuzzy match. |
| Devolutions Launcher | `Devolutions.Launcher` | MSI, machine, x64 |
Distinct ARP identity from the existing Remote Desktop Manager FMA. |
| Devolutions Workspace | `Devolutions.Workspace` | MSI, machine, x64 |
Product renamed: ARP DisplayName is **"Devolutions Password Manager"**
(set as `unique_identifier`). `program_publisher` overridden to
"Devolutions Inc." (capital I) — verified via msiinfo; the
locale-derived lowercase would not match. |
| Delinea Connection Manager | `Delinea.DelineaConnectionManager` | MSI,
machine, x64 | Dual-purpose MSI defaults to **per-user** (ALLUSERS=2 +
MSIINSTALLPERUSER=1); custom install script forces `ALLUSERS=1
MSIINSTALLPERUSER=""`. `program_publisher` = "Delinea Inc.." (double
period, as stored in the MSI). Unversioned URL → `ignore_hash`. |
| DAX Studio | `DaxStudio.DaxStudio` | Inno, machine, x64 | `/ALLUSERS`
for machine scope; versioned ARP name → fuzzy. |
| DevPod | `LoftLabs.DevPod` | MSI, machine, x64 | Uses the WiX MSI
variant (the manifest also has an NSIS per-user one). |
| Directory Opus | `GPSoftware.DirectoryOpus` | Inno, machine, x64 |
Standard Inno silent install/uninstall. |
| DYMO ID | `DYMO.DYMOID` | InstallShield→MSI, machine, x86 | `/S /V"/qn
/norestart"`; uninstall via msiexec by ProductCode. |
| digiSeal Reader | `secrypt.digiSealreader` | EXE (self-extracting),
machine, x86 | `installer_scope: ""` (manifest declares no scope);
`-silent` install, shipped uninstaller with `-silent`. Unversioned URL →
`ignore_hash`. |

Considered but **not** added (recorded in the workstream tracker):
- **Datadog Agent** (`Datadog.Agent`): winget PackageVersion is
`7.81.0.1` but the MSI and registry report `7.81.0.0`, so the patch
policy would flag every install as perpetually outdated.
`use_display_version_for_patch` can't fix it (the manifest has no
`AppsAndFeaturesEntries`); it needs a version-normalizing ingester ref
(like `onepassword_version_shortener`). Deferred pending that helper
(task spawned).
- **Dell Display and Peripheral Manager**
(`Dell.DisplayAndPeripheralManager`, covers both "Dell Display Manager"
and "Dell Peripheral Manager"): the winget-pinned host `dl.dell.com`
returns **403** to non-browser User-Agents, and Fleet's downloader sends
`Go-http-client` (same failure that dropped Crestron AirMedia in letter
C). The `downloads.dell.com` mirror serves the identical path to any UA,
but there's no input field to override the manifest URL. Deferred
pending a downloader User-Agent fix (task spawned — would also unblock
Crestron).
- **Dell EMC System Update** (`Dell.SystemUpdate`): registers its ARP
entry under **HKCU** (per-user) under a SYSTEM install; legacy product
Dell steers users away from (toward Dell Command Update, already an
FMA); firmware/driver DUP bootstrapper.
- **Devolutions Remote Desktop Manager Agent**
(`Devolutions.RemoteDesktopManagerAgent`): legacy/superseded by
"Devolutions Agent" (2026.1); winget manifest frozen at 2025.2.28.0
since Sept 2025; vendor docs page 404s.
- **Dedoose** (`Dedoose.Dedoose`): per-user-only scope, x86,
latest-pointer URL (CloudShow class).
- **Defraggler** (`Piriform.Defraggler`): abandoned (final release 2020,
no winget commits since 2023); legacy defrag tool with an unverified ARP
publisher string.
- **DiRoots ProSheets** (`DiRoots.ProSheets`): validated install but its
Advanced Installer bootstrapper uninstall hung to the timeout and it
drags bundled PDF24 Creator entries into inventory (dropped at
validation).
- **DroidCam Client** (`dev47apps.DroidCam`): NSIS `/S` install hung
headless to the timeout (inline vc_redist); 3DxWare/Citrix headless-hang
class (dropped at validation).

Identities verified per app (msiinfo Property tables; installer
PE/version resources; winget AppsAndFeaturesEntries; uninstall-database
corroboration). Two publisher-casing mismatches (Devolutions Workspace,
Delinea) were caught by direct MSI inspection and fixed with
`program_publisher` before they could silently break the exists queries.
SHAs verified against manifests for pinned URLs; `ignore_hash` used only
where the manifest is demonstrably actively maintained. Icons via
`tools/software/icons/generate-icons.sh` (the pre-existing DataSpell
icon component is reused untouched).

# 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops

## Testing

- [ ] QA'd all new/changed functionality manually (relying on the FMA CI
validator for Windows install/uninstall validation)
allenhouchins added a commit that referenced this pull request Jul 13, 2026
**Related issue:** N/A — part of the Windows Fleet-maintained apps
catalog expansion (letter E batch; follows #48872, #48881, #48950,
#48969, #49086).

Adds eleven new Windows Fleet-maintained apps:

| App | winget package | Installer | Notes |
|-----|----------------|-----------|-------|
| Eclipse Temurin JDK 8 | `EclipseAdoptium.Temurin.8.JDK` | MSI (WiX),
machine, x64 | Per-major pin. |
| Eclipse Temurin JDK 11 | `EclipseAdoptium.Temurin.11.JDK` | MSI (WiX),
machine, x64 | Per-major pin. |
| Eclipse Temurin JDK 17 | `EclipseAdoptium.Temurin.17.JDK` | MSI (WiX),
machine, x64 | Per-major pin. |
| Eclipse Temurin JDK 21 | `EclipseAdoptium.Temurin.21.JDK` | MSI (WiX),
machine, x64 | Per-major pin. |
| Eclipse Temurin JRE 8 | `EclipseAdoptium.Temurin.8.JRE` | MSI (WiX),
machine, x64 | Per-major pin. |
| Eclipse Temurin JRE 11 | `EclipseAdoptium.Temurin.11.JRE` | MSI (WiX),
machine, x64 | Per-major pin. |
| Eclipse Temurin JRE 17 | `EclipseAdoptium.Temurin.17.JRE` | MSI (WiX),
machine, x64 | Per-major pin. |
| Eclipse Temurin JRE 21 | `EclipseAdoptium.Temurin.21.JRE` | MSI (WiX),
machine, x64 | Per-major pin. |
| exacqVision Client | `ExacqTechnologies.exacqVisionClient` | MSI,
machine, x64 | Clean MSI; ARP name `exacqVision Client (x64)`. |
| Egnyte WebEdit | `Egnyte.EgnyteWebEdit` | MSI, machine, x86 | Distinct
product from the existing Egnyte Desktop FMA (separate
ProductCode/UpgradeCode/ARP name). |
| Elevate UC | `Serverdata.ElevateUC` | MSI, machine, x64 | Intermedia
UC client. Unversioned latest-pointer URL with ~monthly cadence →
`ignore_hash`. |

**Eclipse Temurin (8 apps).** All are clean machine-scope WiX MSIs from
Eclipse Adoptium. The ARP DisplayName embeds the full patch version —
`Eclipse Temurin JDK with Hotspot 17.0.19+10 (x64)` — and JDK/JRE of the
same major share a version prefix, so each major is pinned with an
`exists_query` that combines the JDK-vs-JRE name prefix, the publisher,
and a major version filter, e.g.:
```
SELECT 1 FROM programs WHERE name LIKE 'Eclipse Temurin JDK%' AND publisher = 'Eclipse Adoptium' AND version LIKE '17.%';
```
The `JDK`/`JRE` token in the name prefix keeps a JDK install from
matching the JRE FMA and vice-versa; the `version LIKE '<major>.%'`
keeps each major distinct. This mirrors the existing Amazon Corretto
per-major FMAs. Identities (DisplayName, publisher `Eclipse Adoptium`,
4-part ProductVersion) were verified via `msiinfo` on the real x64 MSIs.

Considered but **not** added (recorded in the workstream tracker):
- **ESET Endpoint Antivirus** (`ESET.EndpointAntivirus`) and **ESET
Endpoint Security** (`ESET.EndpointSecurity`): the install succeeds
headless without a license, but uninstall is Self-Defense (HIPS)
protected — it requires a reboot to complete and is widely documented to
fail unattended (needing the ESET Uninstaller Tool in Safe Mode), so a
reliable silent SYSTEM-context removal can't be guaranteed. They're also
managed enterprise agents meant for central ESET PROTECT deployment
(standalone installs land unactivated and disable Windows Defender).

Still to verify (not in this PR): EndNote, Enpass, Evernote, and
UltraISO — their verification pass was interrupted and will be handled
separately.

Identities verified via `msiinfo` Property tables. SHAs verified against
manifests for pinned URLs; `ignore_hash` used only for Elevate UC's
actively-maintained latest-pointer URL. Icons via
`tools/software/icons/generate-icons.sh`.

# 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops

## Testing

- [ ] QA'd all new/changed functionality manually (relying on the FMA CI
validator for Windows install/uninstall validation)
allenhouchins added a commit that referenced this pull request Jul 13, 2026
**Related issue:** N/A — part of the Windows Fleet-maintained apps
catalog expansion (letter F batch; follows #48872, #48881, #48950,
#48969, #49086, #49186).

Adds eight new Windows Fleet-maintained apps:

| App | winget package | Installer | Notes |
|-----|----------------|-----------|-------|
| Foxit PDF Editor | `Foxit.PhantomPDF` | WiX bootstrapper EXE, machine,
x64 | Covers both "Foxit PDF Editor" and "…Pro" from the inventory (one
package). Runs an updater service → process-stopping uninstall. ARP key
lives in the WOW6432Node hive. |
| Foxit PDF Reader | `Foxit.FoxitReader` | WiX bootstrapper EXE,
machine, x64 | Distinct DisplayName from the Editor (verified via
msiinfo). Updater service → process-stopping uninstall. |
| FreeCAD | `FreeCAD.FreeCAD` | NSIS (MultiUser), machine, x64 |
`/AllUsers /S`; versioned ARP name ("FreeCAD 1.1.1") → `FreeCAD%` fuzzy.
|
| FastPictureViewer Professional |
`AxelRietschin.FastPictureViewer.Professional` | MSI, machine, x64 |
Versioned ARP name → fuzzy. Unversioned URL → `ignore_hash`. Declares
VCRedist deps (near-ubiquitous; noted). |
| FastStone Capture | `FastStone.Capture` | NSIS, machine, x86 | `/S`;
versioned ARP name → fuzzy. Paid trialware, but silent
install/detect/uninstall are clean. |
| FastStone Image Viewer | `FastStone.Viewer` | NSIS, machine, x86 |
`/S`; versioned ARP name → fuzzy. |
| FlexWhere for Desktop | `Dutchview.Flexwhere` | MSI, machine, x64 |
Auto-start tray app → process-stopping uninstall (stop process, then
msiexec /x via UpgradeCode). |
| Fortify | `PeculiarVentures.Fortify` | WiX MSI, machine, x64 (en-US) |
Smart-card/cert bridge; auto-start tray → process-stopping uninstall.
Per-arch+locale ProductCode, so `installer_locale: en-US`. |

Considered but **not** added (recorded in the workstream tracker):
- **FactSet Workstation** (`FactSet.FactSetWorkstation`): MSI defaults
to per-user (ALLUSERS=2 + MSIINSTALLPERUSER=1) with no machine-scope
override, AND a `SpawnFDSWorkstation` custom action launches the app at
install (headless-hang risk in a SYSTEM session). Niche licensed
terminal.
- **Filius** (`StefanFreischlad.Filius`): winget manifest is de-DE only
(no en-US locale); the ingester hard-codes the en-US locale fetch (same
limitation that deferred Araxis Merge).
- **FlashFXP** (`OpenSight.FlashFXP`): abandoned (frozen at 2017), the
vendor site returns HTTP 500, only a 16×16 icon is available, and its
InstallAware uninstall needs a fragile cached-setup `/s` injection.
- **Front** (`FrontApp.Front`): per-user-only electron-builder installer
(`Front-user-*.exe` → `%LocalAppData%`, HKCU); no machine/all-users
artifact in winget.
- **Autodesk Fusion** (`Autodesk.Fusion`): the winget "installer" is
`Fusion Client Downloader.exe`, a per-user streaming/web bootstrapper
that downloads at runtime, hangs headless, and needs interactive
Autodesk sign-in.

Identities verified per app (msiinfo Property tables; NSIS header
decompilation; winget AppsAndFeaturesEntries; uninstall-database
corroboration). Apps that run a service or auto-start tray (both Foxit
products, FlexWhere, Fortify) get process-stopping uninstalls up front
to avoid the MSI-rollback failure class. SHAs verified against manifests
for pinned URLs; `ignore_hash` only for FastPictureViewer's
actively-maintained latest-pointer URL. Icons via
`tools/software/icons/generate-icons.sh` (all ≥256px except
FastPictureViewer/Fortify at 256/180).

# 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops

## Testing

- [ ] QA'd all new/changed functionality manually (relying on the FMA CI
validator for Windows install/uninstall validation)
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.

3 participants