Add AOMEI Backupper Standard as a Windows Fleet-maintained app - #50021
Conversation
The registry DisplayName is 'AOMEI Backupper' -- AOMEI unified the ARP name across editions around v7.4, so the previous 'AOMEI Backupper Standard' value matched nothing and the uninstall script reported 'Uninstaller not found'.
There was a problem hiding this comment.
Pull request overview
Adds AOMEI Backupper Standard as a Windows Fleet-maintained app (winget-sourced) and wires up a matching software catalog icon in the frontend. This is part of the ongoing effort to get remaining Windows FMAs passing install → detect → uninstall validation.
Changes:
- Adds new winget input metadata and install/uninstall PowerShell scripts for
aomei-backupper-standard. - Adds generated maintained-app output artifacts (
apps.jsonentry +windows.jsonmanifest). - Adds a frontend software icon component and name→icon map entry for “AOMEI Backupper Standard”.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| frontend/pages/SoftwarePage/components/icons/index.ts | Registers the new icon and maps the software name to it. |
| frontend/pages/SoftwarePage/components/icons/AomeiBackupperStandard.tsx | Adds the AOMEI Backupper Standard SVG/icon asset (base64 image). |
| ee/maintained-apps/outputs/apps.json | Adds the app to the generated maintained-app catalog list. |
| ee/maintained-apps/outputs/aomei-backupper-standard/windows.json | Adds the generated Windows manifest (queries + installer URL + script refs). |
| ee/maintained-apps/inputs/winget/scripts/aomei_backupper_standard_uninstall.ps1 | Implements registry-based uninstall lookup for the app. |
| ee/maintained-apps/inputs/winget/scripts/aomei_backupper_standard_install.ps1 | Implements the silent installer invocation for the app. |
| ee/maintained-apps/inputs/winget/aomei-backupper-standard.json | Adds the winget input definition used to generate outputs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| foreach ($key in $uninstallKeys) { | ||
| if ($key.DisplayName -like $softwareNameLike) { | ||
| $foundUninstaller = $true | ||
| $uninstallCommand = if ($key.QuietUninstallString) { $key.QuietUninstallString } else { $key.UninstallString } |
There was a problem hiding this comment.
Good catch — fixed. The lookup now matches the DisplayName exactly and also requires Publisher -eq 'AOMEI International Network Limited.', mirroring the exists query (name = 'AOMEI Backupper' AND publisher = ...).
The publisher value is verified, not assumed: the installer's PE version resource has CompanyName = 'AOMEI International Network Limited.', trailing period included. And requiring it here matters beyond picking the right key — the validator's appExists matches on name only, so a wrong exists-query publisher would otherwise ship undetected (that's what happened to Spyder in #50016).
Also added -ErrorAction SilentlyContinue on the key read and the bare-token UninstallString fallback while here. Output regenerated; run 30372509008 passes.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #50021 +/- ##
==========================================
+ Coverage 67.97% 68.04% +0.07%
==========================================
Files 3922 3934 +12
Lines 250032 250284 +252
Branches 13334 13275 -59
==========================================
+ Hits 169949 170307 +358
+ Misses 64781 64673 -108
- Partials 15302 15304 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Addresses Copilot's review: a substring match could select an AOMEI add-on and uninstall the wrong product. Match the DisplayName exactly and require the publisher, mirroring the exists query. Requiring the publisher also makes CI exercise it -- the validator's appExists looks up by name only, so a wrong exists-query publisher would otherwise ship undetected (cf. the Spyder finding in #50016). Also adds -ErrorAction SilentlyContinue on the key read, the bare-token UninstallString fallback, and Exit 0 when no entry is found (the repo convention: 155 uninstall scripts do this vs 21 that exit 1).
Script Diff Resultsee/maintained-apps/outputs/aomei-backupper-standard/windows.json=== Install Script (no changes) ===
=== Uninstall // dd1d0362 -> 7b8eb20c ===
--- /tmp/old.mxmIkk 2026-07-28 15:17:19.709444460 +0000
+++ /tmp/new.d5g7nJ 2026-07-28 15:17:19.710444453 +0000
@@ -1,8 +1,15 @@
# AOMEI unified the ARP DisplayName across editions around v7.4: the registry
# entry reads "AOMEI Backupper", with no "Standard" suffix. Matching the catalog
# name here finds nothing, so match the DisplayName the installer actually writes.
+#
+# Match the DisplayName exactly and require the publisher, mirroring the manifest's
+# exists query. A substring match would also select AOMEI add-ons whose DisplayName
+# merely contains this one, and uninstall the wrong product. The publisher was
+# verified against the installer's PE version resource (CompanyName) -- trailing
+# period included -- and requiring it here means CI exercises the same value the
+# exists query depends on, which the validator's name-only lookup never does.
$softwareName = "AOMEI Backupper"
-$softwareNameLike = "*$softwareName*"
+$softwarePublisher = "AOMEI International Network Limited."
$uninstallArgs = "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART"
$machineKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
@@ -11,17 +18,19 @@
try {
[array]$uninstallKeys = Get-ChildItem -Path @($machineKey, $machineKey32on64) -ErrorAction SilentlyContinue |
- ForEach-Object { Get-ItemProperty $_.PSPath }
+ ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue }
$foundUninstaller = $false
foreach ($key in $uninstallKeys) {
- if ($key.DisplayName -like $softwareNameLike) {
+ if ($key.DisplayName -eq $softwareName -and $key.Publisher -eq $softwarePublisher) {
$foundUninstaller = $true
$uninstallCommand = if ($key.QuietUninstallString) { $key.QuietUninstallString } else { $key.UninstallString }
if ($uninstallCommand -match '^\s*"([^"]+)"\s*(.*)$') {
$uninstallCommand = $Matches[1]; if ($Matches[2]) { $uninstallArgs = "$($Matches[2]) $uninstallArgs".Trim() }
} elseif ($uninstallCommand -match '(?i)^\s*(.+?\.exe)\s*(.*)$') {
$uninstallCommand = $Matches[1]; if ($Matches[2]) { $uninstallArgs = "$($Matches[2]) $uninstallArgs".Trim() }
+ } elseif ($uninstallCommand -match '^\s*(\S+)\s*(.*)$') {
+ $uninstallCommand = $Matches[1]; if ($Matches[2]) { $uninstallArgs = "$($Matches[2]) $uninstallArgs".Trim() }
}
Write-Host "Uninstall command: $uninstallCommand"; Write-Host "Uninstall args: $uninstallArgs"
$processOptions = @{ FilePath = $uninstallCommand; PassThru = $true; Wait = $true }
@@ -30,7 +39,9 @@
$exitCode = $process.ExitCode; Write-Host "Uninstall exit code: $exitCode"; break
}
}
- if (-not $foundUninstaller) { Write-Host "Uninstaller for '$softwareName' not found."; Exit 1 }
+ # Nothing to remove is not a failure: uninstall scripts are idempotent here, as
+ # in nordpass_uninstall.ps1 and windsurf_uninstall.ps1.
+ if (-not $foundUninstaller) { Write-Host "Uninstall entry not found for '$softwareName'."; Exit 0 }
} catch { Write-Host "Error: $_"; Exit 1 }
Exit $exitCode |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 8 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
frontend/pages/SoftwarePage/components/icons/index.ts:1215
getMatchedSoftwareIcondoes loose prefix matching where the software name must start with the map key (matchLoosePrefixToKeycheckss === key || s.startsWith(key + " ")). The maintained app'sunique_identifier/inventory name isAOMEI Backupper, which will not match the current key"aomei backupper standard", so the icon likely won't show up for installed software entries. Add an alias key for"aomei backupper"pointing to the same icon (keeping the more specific key too).
"aomei backupper standard": AomeiBackupperStandard,
WalkthroughAdds AOMEI Backupper Standard as a maintained Windows application with Winget metadata, silent installation and uninstallation scripts, a versioned catalog manifest, an application catalog entry, and a registered software icon. Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 |
There was a problem hiding this comment.
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/aomei_backupper_standard_uninstall.ps1`:
- Around line 20-21: Update the uninstall registry lookup that populates
$uninstallKeys so missing registry views are handled separately from registry
enumeration or provider failures. Replace suppressed registry access with
terminating error handling and explicitly distinguish an absent uninstall entry
from an access/read error; only return the existing “not installed” success path
when both registry views are confirmed absent.
🪄 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: 51d843b8-b6a8-40ea-88c4-c7d42a9f1383
⛔ Files ignored due to path filters (1)
website/assets/images/app-icon-aomei-backupper-standard-60x60@2x.pngis excluded by!**/*.png
📒 Files selected for processing (7)
ee/maintained-apps/inputs/winget/aomei-backupper-standard.jsonee/maintained-apps/inputs/winget/scripts/aomei_backupper_standard_install.ps1ee/maintained-apps/inputs/winget/scripts/aomei_backupper_standard_uninstall.ps1ee/maintained-apps/outputs/aomei-backupper-standard/windows.jsonee/maintained-apps/outputs/apps.jsonfrontend/pages/SoftwarePage/components/icons/AomeiBackupperStandard.tsxfrontend/pages/SoftwarePage/components/icons/index.ts
| [array]$uninstallKeys = Get-ChildItem -Path @($machineKey, $machineKey32on64) -ErrorAction SilentlyContinue | | ||
| ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf 'Files:\n'
git ls-files | rg 'aomei_backupper_standard_uninstall\.ps1|winget/scripts' || true
printf '\nTarget file excerpt:\n'
sed -n '1,120p' ee/maintained-apps/inputs/winget/scripts/aomei_backupper_standard_uninstall.ps1
printf '\nSearch for similar uninstall registry handling:\n'
rg -n 'SilentlyContinue|Get-ItemProperty|Get-ChildItem|Exit 0|Exit -?\d+' ee/maintained-apps/inputs/winget/scripts || trueRepository: fleetdm/fleet
Length of output: 50370
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
# Read-only behavioral probe for the relevant PowerShell semantics as represented by the script:
# - Get-ChildItem / Get-ItemProperty with -ErrorAction SilentlyContinue suppress errors and continue returning
# only the items/properties that did not error.
# - An empty result from a missing registry path is indistinguishable from a failed registry read.
import ast
from pathlib import Path
path = Path("ee/maintained-apps/inputs/winget/scripts/aomei_backupper_standard_uninstall.ps1")
text = path.read_text()
checks = {
"uses_silently_continue_on_getchilditem": "-ErrorAction SilentlyContinue" in text.splitlines()[19],
"uses_silently_continue_on_getitreprop": "-ErrorAction SilentlyContinue" in text.splitlines()[20],
"exits_zero_if_not_found_inside_try": "if (-not $foundUninstaller) { Write-Host \"Uninstall entry not found for '$softwareName'.\"; Exit 0 }" in text,
"exits_zero_after_try_when_not_found": "Exit $exitCode" in text,
"catches_try_errors": "catch { Write-Host \"Error: $_\"; Exit 1 }" in text,
}
for k, v in checks.items():
print(f"{k}={v}")
# Static shape check: the assignment to uninstallKeys contains no split handling for absent provider/permission errors.
assignment = next(line.strip() for line in text.splitlines() if line.strip().startswith("[array]$uninstallKeys"))
print("uninstallKeys_assignment=", assignment)
print("uses_error_action_stop_on_registry_read=", "-ErrorAction Stop" in assignment)
PYRepository: fleetdm/fleet
Length of output: 526
Do not treat every registry read error as “not installed.”
SilentlyContinue suppresses registry enumeration/provider failures, so Exit 0 on “uninstall entry not found” can report success when AOMEI may still be installed. Handle absent registry views separately, and use ErrorAction Stop for expected registry access failures.
🤖 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/aomei_backupper_standard_uninstall.ps1`
around lines 20 - 21, Update the uninstall registry lookup that populates
$uninstallKeys so missing registry views are handled separately from registry
enumeration or provider failures. Replace suppressed registry access with
terminating error handling and explicitly distinguish an absent uninstall entry
from an access/read error; only return the existing “not installed” success path
when both registry views are confirmed absent.
Script Diff Resultsee/maintained-apps/outputs/aomei-backupper-standard/windows.json=== Install Script (no changes) ===
=== Uninstall // 7b8eb20c -> e92f2668 ===
--- /tmp/old.qBPUzA 2026-07-28 17:41:07.444976232 +0000
+++ /tmp/new.fmzBMZ 2026-07-28 17:41:07.444976232 +0000
@@ -1,13 +1,5 @@
-# AOMEI unified the ARP DisplayName across editions around v7.4: the registry
-# entry reads "AOMEI Backupper", with no "Standard" suffix. Matching the catalog
-# name here finds nothing, so match the DisplayName the installer actually writes.
-#
-# Match the DisplayName exactly and require the publisher, mirroring the manifest's
-# exists query. A substring match would also select AOMEI add-ons whose DisplayName
-# merely contains this one, and uninstall the wrong product. The publisher was
-# verified against the installer's PE version resource (CompanyName) -- trailing
-# period included -- and requiring it here means CI exercises the same value the
-# exists query depends on, which the validator's name-only lookup never does.
+# The ARP DisplayName is "AOMEI Backupper" (no "Standard" suffix since ~v7.4).
+# Match name and publisher exactly so add-ons sharing the name prefix aren't hit.
$softwareName = "AOMEI Backupper"
$softwarePublisher = "AOMEI International Network Limited."
$uninstallArgs = "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART"
@@ -39,8 +31,6 @@
$exitCode = $process.ExitCode; Write-Host "Uninstall exit code: $exitCode"; break
}
}
- # Nothing to remove is not a failure: uninstall scripts are idempotent here, as
- # in nordpass_uninstall.ps1 and windsurf_uninstall.ps1.
if (-not $foundUninstaller) { Write-Host "Uninstall entry not found for '$softwareName'."; Exit 0 }
} catch { Write-Host "Error: $_"; Exit 1 } |
Replaces the Aomei Backupper Standard app icon with an updated image in both the Software page icon component and the website asset, keeping the in-app and site visuals in sync with the refreshed source artwork.
Script Diff Resultsee/maintained-apps/outputs/aomei-backupper-standard/windows.json=== Install Script (no changes) ===
=== Uninstall // 7b8eb20c -> e92f2668 ===
--- /tmp/old.ycGtNT 2026-07-29 04:10:48.223597417 +0000
+++ /tmp/new.Uza52g 2026-07-29 04:10:48.224597375 +0000
@@ -1,13 +1,5 @@
-# AOMEI unified the ARP DisplayName across editions around v7.4: the registry
-# entry reads "AOMEI Backupper", with no "Standard" suffix. Matching the catalog
-# name here finds nothing, so match the DisplayName the installer actually writes.
-#
-# Match the DisplayName exactly and require the publisher, mirroring the manifest's
-# exists query. A substring match would also select AOMEI add-ons whose DisplayName
-# merely contains this one, and uninstall the wrong product. The publisher was
-# verified against the installer's PE version resource (CompanyName) -- trailing
-# period included -- and requiring it here means CI exercises the same value the
-# exists query depends on, which the validator's name-only lookup never does.
+# The ARP DisplayName is "AOMEI Backupper" (no "Standard" suffix since ~v7.4).
+# Match name and publisher exactly so add-ons sharing the name prefix aren't hit.
$softwareName = "AOMEI Backupper"
$softwarePublisher = "AOMEI International Network Limited."
$uninstallArgs = "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART"
@@ -39,8 +31,6 @@
$exitCode = $process.ExitCode; Write-Host "Uninstall exit code: $exitCode"; break
}
}
- # Nothing to remove is not a failure: uninstall scripts are idempotent here, as
- # in nordpass_uninstall.ps1 and windsurf_uninstall.ps1.
if (-not $foundUninstaller) { Write-Host "Uninstall entry not found for '$softwareName'."; Exit 0 }
} catch { Write-Host "Error: $_"; Exit 1 } |
Related issue: #50020
What this does
Adds AOMEI Backupper Standard as a Windows Fleet-maintained app. One of the 11 apps split out of #48501 that failed the FMA validator; #50016 shipped the 6 that passed.
Why it was failing
Install and detection were already fine on the SYSTEM-context Windows runner — the validator installed it and osquery found
AOMEI Backupper8.4.0. Uninstall was the failure:AOMEI unified the ARP
DisplayNameacross editions around v7.4 — the registry entry readsAOMEI Backupper, with noStandardsuffix. The uninstall script searched for the catalog name and matched nothing.unique_identifierwas already corrected toAOMEI Backupper; this fixes the uninstall script to match.Notes
AOMEI Backupperalso matches the paid Pro/Workstation/Server editions — AOMEI shares the DisplayName across editions and no registry value distinguishes them. Detecting the free edition specifically isn't possible from inventory.https://www2.aomeisoftware.com/download/adb/AOMEIBackupperStd.exeis a "latest" URL, so the pinned SHA will drift when AOMEI ships a new build until the FMA auto-update bumps it.C:\Program Files (x86). The validator's "no changes detected inC:\Program Files" line is an expected warning, not a failure.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.Testing
All checks passed)apps.jsonis valid JSON with a description filled in.Summary by CodeRabbit