Update Fleet-maintained apps - #46386
Conversation
Generated automatically with cmd/maintained-apps.
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
Script Diff Resultsee/maintained-apps/outputs/arc/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/brave-browser/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/camtasia/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/canva/windows.json=== Install Script (no changes) ===
=== Uninstall // 58022dd8 -> f683286d ===
--- /tmp/old.k8Ugbb 2026-05-28 20:48:04.119491983 +0000
+++ /tmp/new.3AJKR1 2026-05-28 20:48:04.120491987 +0000
@@ -32,7 +32,7 @@
# Parse the uninstall string. Handle both quoted and unquoted exe paths.
if ($uninstallString -match '^"([^"]+)"(.*)') {
$exePath = $matches[1]
-} elseif ($uninstallString -match '^([^\s]+)(.*)') {
+} elseif ($uninstallString -match '^(.+?\.exe)(.*)$') {
$exePath = $matches[1]
} else {
Write-Host "Error: Could not parse uninstall string: $uninstallString"ee/maintained-apps/outputs/claude/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/dialpad/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/grammarly-desktop/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/granola/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/granola/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/notepad++/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/parallels/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/power-bi/windows.json=== Install // ee068356 -> 4be62464 ===
--- /tmp/old.iVcndz 2026-05-28 20:48:04.408493365 +0000
+++ /tmp/new.R7xWBU 2026-05-28 20:48:04.408493365 +0000
@@ -1,12 +1,8 @@
-# Learn more about .exe install scripts:
-# http://fleetdm.com/learn-more-about/exe-install-scripts
-
$exeFilePath = "${env:INSTALLER_PATH}"
try {
# Add argument to install silently
-# Asana uses --silent for silent installation
$processOptions = @{
FilePath = "$exeFilePath"
ArgumentList = "/silent", "/norestart", "ACCEPT_EULA=1"
=== Uninstall // fa3b8ea6 -> 201475bb ===
--- /tmp/old.HbuBlI 2026-05-28 20:48:04.429493465 +0000
+++ /tmp/new.5zmvDi 2026-05-28 20:48:04.429493465 +0000
@@ -8,13 +8,16 @@
# Removing the MSI directly orphans the bundle: its uninstall then no-ops
# (returns 0) and leaves the "Microsoft PowerBI Desktop (x64)" registration
# behind, which is what Fleet's osquery-based validator keeps detecting.
-# Correct approach: uninstall via the BUNDLE first; it removes the MSI and its
-# own registration. Burn relaunches a cached copy of itself + spawns msiexec
-# asynchronously, so wait for those to finish.
+#
+# Correct approach: uninstall via the BUNDLE first. It removes both the MSI and
+# its own registration. Burn relaunches a cached copy of itself and spawns
+# msiexec asynchronously, so wait for those to finish.
$ExpectedExitCodes = @(0, 1605, 1641, 3010)
$exitCode = 0
+# Power BI's default install folder; used as a safety check before deleting any
+# stale registration that survives a successful uninstall.
$installDirs = @(
(Join-Path $env:ProgramFiles 'Microsoft Power BI Desktop'),
(Join-Path ${env:ProgramFiles(x86)} 'Microsoft Power BI Desktop')
@@ -58,6 +61,8 @@
}
try {
+
+ # Uninstall roots across all hives (matches osquery's "programs" table).
$roots = [System.Collections.Generic.List[string]]::new()
$roots.Add('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall')
$roots.Add('HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')
@@ -77,20 +82,23 @@
Stop-Process -Name $proc -Force -ErrorAction SilentlyContinue
}
- # Phase 1: uninstall via the Burn bundle bootstrapper(s) FIRST.
+ # --- Phase 1: uninstall via the Burn bundle bootstrapper(s) FIRST. ---
foreach ($e in ($entries | Where-Object { $_.Command -match "(?i)PBIDesktopSetup.*\.exe" })) {
$exe = Get-ExePath $e.Command
if (-not $exe) { Write-Host "Could not parse bundle exe from: $($e.Command)"; continue }
if (-not (Test-Path -LiteralPath $exe)) { Write-Host "Bundle exe missing: $exe"; continue }
Write-Host "Uninstalling bundle: '$($e.DisplayName)'"
+ Write-Host " Command: $exe"
+ Write-Host " Args: /uninstall /quiet /norestart"
$p = Start-Process -FilePath $exe -ArgumentList "/uninstall /quiet /norestart" -PassThru -Wait
Write-Host " Exit code: $($p.ExitCode)"
if (($ExpectedExitCodes -notcontains $p.ExitCode) -and ($exitCode -eq 0)) { $exitCode = $p.ExitCode }
+
Wait-ForProcessExit -Names @("PBIDesktopSetup_x64", "PBIDesktopSetup", "msiexec") -TimeoutSeconds 240
}
- # Phase 2: remove any MSI entries the bundle didn't clean up.
+ # --- Phase 2: remove any MSI entries the bundle didn't clean up. ---
foreach ($e in (Get-PowerBIEntries -Roots $roots)) {
$msiCode = $null
if ($e.Command -match "(?i)MsiExec\.exe\s+/[IX]\s*(\{[A-F0-9-]+\})") { $msiCode = $Matches[1] }
@@ -104,16 +112,22 @@
if (($ExpectedExitCodes -notcontains $p.ExitCode) -and ($exitCode -eq 0)) { $exitCode = $p.ExitCode }
}
- # Phase 3: safety net for stale registration when product files are gone.
+ # --- Phase 3: safety net. If the product files are gone but a stale ARP
+ # registration lingers, remove the orphaned key so detection clears. Gated
+ # on the install folder being absent so we never hide a real install. ---
$productGone = -not ($installDirs | Where-Object { $_ -and (Test-Path -LiteralPath $_) })
foreach ($e in (Get-PowerBIEntries -Roots $roots)) {
- if ($productGone) {
+ $isMachineKey = $e.KeyPath -like 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\*'
+ if ($productGone -and $isMachineKey) {
Write-Host "Removing orphaned registration: '$($e.DisplayName)' ($($e.KeyPath))"
Remove-Item -Path $e.KeyPath -Recurse -Force -ErrorAction SilentlyContinue
} else {
Write-Host "WARNING: entry still present and product files remain: '$($e.DisplayName)'"
if ($exitCode -eq 0) { $exitCode = 1 }
}
+ Write-Host "WARNING: entry still present and product files remain: '$($e.DisplayName)'"
+ if ($exitCode -eq 0) { $exitCode = 1 }
+ }
}
} catch { |
WalkthroughThis pull request updates maintained-app manifest metadata across 12 applications spanning macOS and Windows platforms. Most updates are straightforward version bumps with corresponding changes to installer URLs, SHA256 checksums, and version-comparison thresholds in patched queries. The Canva Windows update refines uninstall script path parsing logic by changing from whitespace-based to Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
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/outputs/power-bi/windows.json`:
- Line 19: Phase 3 contains duplicated warning lines and an extra closing brace
causing a parse error; inside the loop over (Get-PowerBIEntries -Roots $roots)
that checks $productGone and $isMachineKey (the if/else under "Phase 3"), remove
the duplicated Write-Host and if ($exitCode -eq 0) { $exitCode = 1 } lines that
appear after the else block and delete the unmatched extra "}" so the if/else
for removing orphaned registration remains the only code path; verify braces
balance and that $productGone and $isMachineKey logic is unchanged in
Get-PowerBIEntries/Phase 3.
🪄 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
Run ID: 8848f5b4-8c2e-4b2e-ae97-2aff9c1124e8
📒 Files selected for processing (12)
ee/maintained-apps/outputs/arc/darwin.jsonee/maintained-apps/outputs/brave-browser/darwin.jsonee/maintained-apps/outputs/camtasia/darwin.jsonee/maintained-apps/outputs/canva/windows.jsonee/maintained-apps/outputs/claude/darwin.jsonee/maintained-apps/outputs/dialpad/darwin.jsonee/maintained-apps/outputs/grammarly-desktop/darwin.jsonee/maintained-apps/outputs/granola/darwin.jsonee/maintained-apps/outputs/granola/windows.jsonee/maintained-apps/outputs/notepad++/windows.jsonee/maintained-apps/outputs/parallels/darwin.jsonee/maintained-apps/outputs/power-bi/windows.json
| "refs": { | ||
| "ee068356": "# Learn more about .exe install scripts:\n# http://fleetdm.com/learn-more-about/exe-install-scripts\n\n$exeFilePath = \"${env:INSTALLER_PATH}\"\n\ntry {\n\n# Add argument to install silently\n# Asana uses --silent for silent installation\n$processOptions = @{\n FilePath = \"$exeFilePath\"\n ArgumentList = \"/silent\", \"/norestart\", \"ACCEPT_EULA=1\"\n PassThru = $true\n Wait = $true\n}\n\n# Start process and track exit code\n$process = Start-Process @processOptions\n$exitCode = $process.ExitCode\n\n# Prints the exit code\nWrite-Host \"Install exit code: $exitCode\"\nExit $exitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n", | ||
| "fa3b8ea6": "# Power BI Desktop's EXE installer is a WiX \"Burn\" bundle. It registers TWO\n# uninstall entries:\n# * \"Microsoft PowerBI Desktop (x64)\" -> the bundle bootstrapper\n# (...\\Package Cache\\{afa18d15-...}\\PBIDesktopSetup_x64.exe)\n# * \"Microsoft Power BI Desktop (x64)\" -> the MSI the bundle installed\n# (MsiExec.exe /X{c7d2053f-...})\n#\n# Removing the MSI directly orphans the bundle: its uninstall then no-ops\n# (returns 0) and leaves the \"Microsoft PowerBI Desktop (x64)\" registration\n# behind, which is what Fleet's osquery-based validator keeps detecting.\n# Correct approach: uninstall via the BUNDLE first; it removes the MSI and its\n# own registration. Burn relaunches a cached copy of itself + spawns msiexec\n# asynchronously, so wait for those to finish.\n\n$ExpectedExitCodes = @(0, 1605, 1641, 3010)\n$exitCode = 0\n\n$installDirs = @(\n (Join-Path $env:ProgramFiles 'Microsoft Power BI Desktop'),\n (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Power BI Desktop')\n)\n\nfunction Wait-ForProcessExit {\n param([string[]]$Names, [int]$TimeoutSeconds = 240)\n $elapsed = 0\n while ($elapsed -lt $TimeoutSeconds) {\n $running = $Names | Where-Object { Get-Process -Name $_ -ErrorAction SilentlyContinue }\n if (-not $running) { break }\n Start-Sleep -Seconds 3\n $elapsed += 3\n }\n}\n\nfunction Get-PowerBIEntries {\n param([string[]]$Roots)\n $list = @()\n foreach ($root in $Roots) {\n foreach ($sub in (Get-ChildItem -Path $root -ErrorAction SilentlyContinue)) {\n $key = Get-ItemProperty $sub.PSPath -ErrorAction SilentlyContinue\n if (-not $key.DisplayName) { continue }\n if (-not (($key.DisplayName -replace '\\s', '').ToLower().Contains(\"powerbidesktop\"))) { continue }\n $list += [PSCustomObject]@{\n DisplayName = $key.DisplayName\n KeyPath = $sub.PSPath\n KeyName = $sub.PSChildName\n Command = if ($key.QuietUninstallString) { $key.QuietUninstallString } else { $key.UninstallString }\n }\n }\n }\n return $list\n}\n\nfunction Get-ExePath {\n param([string]$Command)\n if ($Command -match '\"([^\"]+\\.exe)\"') { return $Matches[1] }\n if ($Command -match '(?i)([A-Z]:\\\\[^\"]+?\\.exe)') { return $Matches[1] }\n return $null\n}\n\ntry {\n $roots = [System.Collections.Generic.List[string]]::new()\n $roots.Add('HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall')\n $roots.Add('HKLM:\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall')\n foreach ($hive in (Get-ChildItem 'Registry::HKEY_USERS' -ErrorAction SilentlyContinue)) {\n if ($hive.Name -match '_Classes$') { continue }\n $roots.Add(\"Registry::$($hive.Name)\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\")\n $roots.Add(\"Registry::$($hive.Name)\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\")\n }\n\n $entries = Get-PowerBIEntries -Roots $roots\n if ($entries.Count -eq 0) {\n Write-Host \"No Power BI Desktop entries found (already removed).\"\n Exit 0\n }\n\n foreach ($proc in @(\"PBIDesktop\", \"msmdsrv\", \"Microsoft.Mashup.Container\")) {\n Stop-Process -Name $proc -Force -ErrorAction SilentlyContinue\n }\n\n # Phase 1: uninstall via the Burn bundle bootstrapper(s) FIRST.\n foreach ($e in ($entries | Where-Object { $_.Command -match \"(?i)PBIDesktopSetup.*\\.exe\" })) {\n $exe = Get-ExePath $e.Command\n if (-not $exe) { Write-Host \"Could not parse bundle exe from: $($e.Command)\"; continue }\n if (-not (Test-Path -LiteralPath $exe)) { Write-Host \"Bundle exe missing: $exe\"; continue }\n\n Write-Host \"Uninstalling bundle: '$($e.DisplayName)'\"\n $p = Start-Process -FilePath $exe -ArgumentList \"/uninstall /quiet /norestart\" -PassThru -Wait\n Write-Host \" Exit code: $($p.ExitCode)\"\n if (($ExpectedExitCodes -notcontains $p.ExitCode) -and ($exitCode -eq 0)) { $exitCode = $p.ExitCode }\n Wait-ForProcessExit -Names @(\"PBIDesktopSetup_x64\", \"PBIDesktopSetup\", \"msiexec\") -TimeoutSeconds 240\n }\n\n # Phase 2: remove any MSI entries the bundle didn't clean up.\n foreach ($e in (Get-PowerBIEntries -Roots $roots)) {\n $msiCode = $null\n if ($e.Command -match \"(?i)MsiExec\\.exe\\s+/[IX]\\s*(\\{[A-F0-9-]+\\})\") { $msiCode = $Matches[1] }\n elseif ($e.KeyName -match \"(?i)^\\{[A-F0-9-]+\\}$\") { $msiCode = $e.KeyName }\n if (-not $msiCode) { continue }\n\n Write-Host \"Removing leftover MSI: '$($e.DisplayName)' ($msiCode)\"\n $p = Start-Process -FilePath \"MsiExec.exe\" -ArgumentList \"/X $msiCode /qn /norestart\" -PassThru -Wait\n Write-Host \" Exit code: $($p.ExitCode)\"\n Wait-ForProcessExit -Names @(\"msiexec\") -TimeoutSeconds 180\n if (($ExpectedExitCodes -notcontains $p.ExitCode) -and ($exitCode -eq 0)) { $exitCode = $p.ExitCode }\n }\n\n # Phase 3: safety net for stale registration when product files are gone.\n $productGone = -not ($installDirs | Where-Object { $_ -and (Test-Path -LiteralPath $_) })\n foreach ($e in (Get-PowerBIEntries -Roots $roots)) {\n if ($productGone) {\n Write-Host \"Removing orphaned registration: '$($e.DisplayName)' ($($e.KeyPath))\"\n Remove-Item -Path $e.KeyPath -Recurse -Force -ErrorAction SilentlyContinue\n } else {\n Write-Host \"WARNING: entry still present and product files remain: '$($e.DisplayName)'\"\n if ($exitCode -eq 0) { $exitCode = 1 }\n }\n }\n\n} catch {\n Write-Host \"Error: $_\"\n $exitCode = 1\n}\n\nif ($ExpectedExitCodes -contains $exitCode) { Exit 0 } else { Exit $exitCode }\n" | ||
| "201475bb": "# Power BI Desktop's EXE installer is a WiX \"Burn\" bundle. It registers TWO\n# uninstall entries:\n# * \"Microsoft PowerBI Desktop (x64)\" -> the bundle bootstrapper\n# (...\\Package Cache\\{afa18d15-...}\\PBIDesktopSetup_x64.exe)\n# * \"Microsoft Power BI Desktop (x64)\" -> the MSI the bundle installed\n# (MsiExec.exe /X{c7d2053f-...})\n#\n# Removing the MSI directly orphans the bundle: its uninstall then no-ops\n# (returns 0) and leaves the \"Microsoft PowerBI Desktop (x64)\" registration\n# behind, which is what Fleet's osquery-based validator keeps detecting.\n#\n# Correct approach: uninstall via the BUNDLE first. It removes both the MSI and\n# its own registration. Burn relaunches a cached copy of itself and spawns\n# msiexec asynchronously, so wait for those to finish.\n\n$ExpectedExitCodes = @(0, 1605, 1641, 3010)\n$exitCode = 0\n\n# Power BI's default install folder; used as a safety check before deleting any\n# stale registration that survives a successful uninstall.\n$installDirs = @(\n (Join-Path $env:ProgramFiles 'Microsoft Power BI Desktop'),\n (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Power BI Desktop')\n)\n\nfunction Wait-ForProcessExit {\n param([string[]]$Names, [int]$TimeoutSeconds = 240)\n $elapsed = 0\n while ($elapsed -lt $TimeoutSeconds) {\n $running = $Names | Where-Object { Get-Process -Name $_ -ErrorAction SilentlyContinue }\n if (-not $running) { break }\n Start-Sleep -Seconds 3\n $elapsed += 3\n }\n}\n\nfunction Get-PowerBIEntries {\n param([string[]]$Roots)\n $list = @()\n foreach ($root in $Roots) {\n foreach ($sub in (Get-ChildItem -Path $root -ErrorAction SilentlyContinue)) {\n $key = Get-ItemProperty $sub.PSPath -ErrorAction SilentlyContinue\n if (-not $key.DisplayName) { continue }\n if (-not (($key.DisplayName -replace '\\s', '').ToLower().Contains(\"powerbidesktop\"))) { continue }\n $list += [PSCustomObject]@{\n DisplayName = $key.DisplayName\n KeyPath = $sub.PSPath\n KeyName = $sub.PSChildName\n Command = if ($key.QuietUninstallString) { $key.QuietUninstallString } else { $key.UninstallString }\n }\n }\n }\n return $list\n}\n\nfunction Get-ExePath {\n param([string]$Command)\n if ($Command -match '\"([^\"]+\\.exe)\"') { return $Matches[1] }\n if ($Command -match '(?i)([A-Z]:\\\\[^\"]+?\\.exe)') { return $Matches[1] }\n return $null\n}\n\ntry {\n\n # Uninstall roots across all hives (matches osquery's \"programs\" table).\n $roots = [System.Collections.Generic.List[string]]::new()\n $roots.Add('HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall')\n $roots.Add('HKLM:\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall')\n foreach ($hive in (Get-ChildItem 'Registry::HKEY_USERS' -ErrorAction SilentlyContinue)) {\n if ($hive.Name -match '_Classes$') { continue }\n $roots.Add(\"Registry::$($hive.Name)\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\")\n $roots.Add(\"Registry::$($hive.Name)\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\")\n }\n\n $entries = Get-PowerBIEntries -Roots $roots\n if ($entries.Count -eq 0) {\n Write-Host \"No Power BI Desktop entries found (already removed).\"\n Exit 0\n }\n\n foreach ($proc in @(\"PBIDesktop\", \"msmdsrv\", \"Microsoft.Mashup.Container\")) {\n Stop-Process -Name $proc -Force -ErrorAction SilentlyContinue\n }\n\n # --- Phase 1: uninstall via the Burn bundle bootstrapper(s) FIRST. ---\n foreach ($e in ($entries | Where-Object { $_.Command -match \"(?i)PBIDesktopSetup.*\\.exe\" })) {\n $exe = Get-ExePath $e.Command\n if (-not $exe) { Write-Host \"Could not parse bundle exe from: $($e.Command)\"; continue }\n if (-not (Test-Path -LiteralPath $exe)) { Write-Host \"Bundle exe missing: $exe\"; continue }\n\n Write-Host \"Uninstalling bundle: '$($e.DisplayName)'\"\n Write-Host \" Command: $exe\"\n Write-Host \" Args: /uninstall /quiet /norestart\"\n $p = Start-Process -FilePath $exe -ArgumentList \"/uninstall /quiet /norestart\" -PassThru -Wait\n Write-Host \" Exit code: $($p.ExitCode)\"\n if (($ExpectedExitCodes -notcontains $p.ExitCode) -and ($exitCode -eq 0)) { $exitCode = $p.ExitCode }\n\n Wait-ForProcessExit -Names @(\"PBIDesktopSetup_x64\", \"PBIDesktopSetup\", \"msiexec\") -TimeoutSeconds 240\n }\n\n # --- Phase 2: remove any MSI entries the bundle didn't clean up. ---\n foreach ($e in (Get-PowerBIEntries -Roots $roots)) {\n $msiCode = $null\n if ($e.Command -match \"(?i)MsiExec\\.exe\\s+/[IX]\\s*(\\{[A-F0-9-]+\\})\") { $msiCode = $Matches[1] }\n elseif ($e.KeyName -match \"(?i)^\\{[A-F0-9-]+\\}$\") { $msiCode = $e.KeyName }\n if (-not $msiCode) { continue }\n\n Write-Host \"Removing leftover MSI: '$($e.DisplayName)' ($msiCode)\"\n $p = Start-Process -FilePath \"MsiExec.exe\" -ArgumentList \"/X $msiCode /qn /norestart\" -PassThru -Wait\n Write-Host \" Exit code: $($p.ExitCode)\"\n Wait-ForProcessExit -Names @(\"msiexec\") -TimeoutSeconds 180\n if (($ExpectedExitCodes -notcontains $p.ExitCode) -and ($exitCode -eq 0)) { $exitCode = $p.ExitCode }\n }\n\n # --- Phase 3: safety net. If the product files are gone but a stale ARP\n # registration lingers, remove the orphaned key so detection clears. Gated\n # on the install folder being absent so we never hide a real install. ---\n $productGone = -not ($installDirs | Where-Object { $_ -and (Test-Path -LiteralPath $_) })\n foreach ($e in (Get-PowerBIEntries -Roots $roots)) {\n $isMachineKey = $e.KeyPath -like 'Microsoft.PowerShell.Core\\Registry::HKEY_LOCAL_MACHINE\\*'\n if ($productGone -and $isMachineKey) {\n Write-Host \"Removing orphaned registration: '$($e.DisplayName)' ($($e.KeyPath))\"\n Remove-Item -Path $e.KeyPath -Recurse -Force -ErrorAction SilentlyContinue\n } else {\n Write-Host \"WARNING: entry still present and product files remain: '$($e.DisplayName)'\"\n if ($exitCode -eq 0) { $exitCode = 1 }\n }\n Write-Host \"WARNING: entry still present and product files remain: '$($e.DisplayName)'\"\n if ($exitCode -eq 0) { $exitCode = 1 }\n }\n }\n\n} catch {\n Write-Host \"Error: $_\"\n $exitCode = 1\n}\n\nif ($ExpectedExitCodes -contains $exitCode) { Exit 0 } else { Exit $exitCode }\n", |
There was a problem hiding this comment.
Syntax error: duplicated code block with unbalanced brace in Phase 3.
The uninstall script has a critical bug in the Phase 3 safety-net section. After the if-else block closes, the warning/exit-code lines are duplicated and followed by an extra closing brace } that has no matching opener:
} else {
Write-Host "WARNING: entry still present..."
if ($exitCode -eq 0) { $exitCode = 1 }
}
Write-Host "WARNING: entry still present..." # ← duplicated, runs unconditionally
if ($exitCode -eq 0) { $exitCode = 1 } # ← duplicated
} # ← EXTRA BRACE - no matching opener
}This will cause a PowerShell parse error, preventing the uninstall script from executing at all.
🐛 Proposed fix: remove the duplicated block
Remove the duplicated lines and extra brace so the structure is:
} else {
Write-Host \"WARNING: entry still present and product files remain: '$($e.DisplayName)'\"
if ($exitCode -eq 0) { $exitCode = 1 }
}
- Write-Host \"WARNING: entry still present and product files remain: '$($e.DisplayName)'\"
- if ($exitCode -eq 0) { $exitCode = 1 }
- }
}🤖 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/outputs/power-bi/windows.json` at line 19, Phase 3
contains duplicated warning lines and an extra closing brace causing a parse
error; inside the loop over (Get-PowerBIEntries -Roots $roots) that checks
$productGone and $isMachineKey (the if/else under "Phase 3"), remove the
duplicated Write-Host and if ($exitCode -eq 0) { $exitCode = 1 } lines that
appear after the else block and delete the unmatched extra "}" so the if/else
for removing orphaned registration remains the only code path; verify braces
balance and that $productGone and $isMachineKey logic is unchanged in
Get-PowerBIEntries/Phase 3.
CI Feedback 🧐A test triggered by this PR failed. Here is an AI-generated analysis of the failure:
|
|
Closing in favor of #46389. |
Automated ingestion of latest Fleet-maintained app data.
Summary by CodeRabbit
Release Notes