Add R and Git as Windows Fleet-maintained apps (#46988)
Adds two winget-sourced Windows FMAs:
- R (RProject.R) -> 'R for Windows', Inno Setup exe, machine scope
- Git (Git.Git) -> 'Git', Inno Setup exe, machine scope
Both use custom Inno install/uninstall scripts (no MSI ProductCode) with
registry-UninstallString-based uninstall and fuzzy name matching, since
their ARP DisplayName embeds the version ('R for Windows <ver>', 'Git
version <ver>').
test-fma-windows-pr-only.yml: add has_r/has_git detection and removal of
the runner's pre-installed R and Git. Git for Windows provides the Git
Bash 'bash' the workflow uses, so the apps.json filtering is split into
its own step that runs before Git removal, and validation runs 'go run
-buildvcs=false' (so Go does not invoke the now-removed git for VCS
stamping).
SSMS was investigated but intentionally skipped: SSMS 21/22 is a Visual
Studio online bootstrapper (no MSI ProductCode, multi-GB network
install), which is a fragile FMA candidate, and the runner has no
standalone SSMS to uninstall.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added support for Git (Windows) with automated install/uninstall and
UI icon.
* Added support for R for Windows with automated install/uninstall and
UI icon.
* Windows testing workflow now detects and conditionally prepares
environments when Git or R are present.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -100,6 +100,8 @@ jobs:
|
||||
"has_firefox=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
"has_nodejs=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
"has_powershell=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
"has_r=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
"has_git=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -114,6 +116,8 @@ jobs:
|
||||
"has_firefox=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
"has_nodejs=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
"has_powershell=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
"has_r=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
"has_git=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
Write-Host "No windows apps changed, skipping Windows workflow"
|
||||
} else {
|
||||
"has_windows_apps=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
@@ -160,6 +164,22 @@ jobs:
|
||||
} else {
|
||||
"has_powershell=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
}
|
||||
|
||||
# Check if r/windows is in the changed apps
|
||||
if ("r/windows" -in $windowsSlugs) {
|
||||
"has_r=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
Write-Host "R detected in changed apps"
|
||||
} else {
|
||||
"has_r=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
}
|
||||
|
||||
# Check if git/windows is in the changed apps
|
||||
if ("git/windows" -in $windowsSlugs) {
|
||||
"has_git=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
Write-Host "Git detected in changed apps"
|
||||
} else {
|
||||
"has_git=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
||||
}
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
@@ -488,7 +508,93 @@ jobs:
|
||||
}
|
||||
shell: powershell
|
||||
|
||||
- name: Filter apps.json and verify changed apps
|
||||
- name: Remove pre-installed R
|
||||
if: steps.check-windows-apps.outputs.has_windows_apps == 'true' && steps.check-windows-apps.outputs.has_r == 'true'
|
||||
run: |
|
||||
Write-Host "Listing all installed packages containing 'R for Windows':"
|
||||
Get-Package | Where-Object { $_.Name -like "*R for Windows*" } | ForEach-Object {
|
||||
Write-Host " - $($_.Name) (Version: $($_.Version))"
|
||||
}
|
||||
|
||||
# Stop any R processes so the uninstaller doesn't fail on locked files
|
||||
Get-Process -Name "Rgui","Rterm","Rscript" -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# R for Windows installs via Inno Setup and registers under "R for Windows <ver>"
|
||||
# / "R Core Team". The version is embedded in the DisplayName, so match by prefix
|
||||
# and use the registry UninstallString (Inno has no MSI ProductCode).
|
||||
$uninstallPaths = @(
|
||||
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
||||
"HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
||||
)
|
||||
|
||||
$found = $false
|
||||
foreach ($path in $uninstallPaths) {
|
||||
$entries = Get-ItemProperty $path -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "R for Windows*" -and $_.Publisher -like "*R Core Team*" }
|
||||
foreach ($entry in $entries) {
|
||||
if (-not $entry) { continue }
|
||||
$found = $true
|
||||
Write-Host "Found R uninstall entry: $($entry.DisplayName) (Version: $($entry.DisplayVersion))"
|
||||
|
||||
$uninstallString = if ($entry.QuietUninstallString) {
|
||||
$entry.QuietUninstallString
|
||||
} elseif ($entry.UninstallString) {
|
||||
$entry.UninstallString
|
||||
} else {
|
||||
$null
|
||||
}
|
||||
|
||||
if ($uninstallString) {
|
||||
Write-Host "Found R uninstall path: $uninstallString"
|
||||
try {
|
||||
# R uses an Inno Setup uninstaller (unins000.exe). Parse the exe path
|
||||
# (quoted or unquoted) and run it with silent Inno switches.
|
||||
$exePath = ""
|
||||
if ($uninstallString -match '^\s*"([^"]+)"') {
|
||||
$exePath = $matches[1]
|
||||
} elseif ($uninstallString -match '(?i)^\s*(.+?\.exe)') {
|
||||
$exePath = $matches[1]
|
||||
}
|
||||
if ($exePath) {
|
||||
Write-Host "Uninstalling R via: $exePath"
|
||||
Start-Process -FilePath $exePath -ArgumentList "/VERYSILENT","/SUPPRESSMSGBOXES","/NORESTART" -Wait -NoNewWindow
|
||||
Write-Host "Successfully removed R via Inno uninstaller"
|
||||
} else {
|
||||
Write-Host "Could not parse uninstall string format: $uninstallString"
|
||||
}
|
||||
} catch {
|
||||
Write-Host "Failed to remove R: $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
Write-Host "R uninstall string not found in registry entry"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $found) {
|
||||
Write-Host "R uninstall path not found in registry"
|
||||
}
|
||||
|
||||
# Force-remove leftover R directory in case files remain after uninstall
|
||||
$rDir = "C:\Program Files\R"
|
||||
if (Test-Path $rDir) {
|
||||
Write-Host "Removing leftover directory: $rDir"
|
||||
Remove-Item -Path $rDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
if (Test-Path $rDir) {
|
||||
Write-Host "WARNING: Failed to fully remove $rDir"
|
||||
} else {
|
||||
Write-Host "Removed $rDir"
|
||||
}
|
||||
}
|
||||
# Use Windows PowerShell 5.1 (not pwsh): the "Remove pre-installed PowerShell"
|
||||
# step above may have uninstalled PowerShell 7, so pwsh.exe may be unavailable.
|
||||
shell: powershell
|
||||
|
||||
# NOTE: filtering is split out from validation and runs BEFORE "Remove pre-installed
|
||||
# Git" below. Git for Windows provides the Git Bash 'bash' that this step's
|
||||
# filter-apps-json.sh call depends on; validation itself does not need bash.
|
||||
- name: Filter apps.json for changed apps
|
||||
if: steps.check-windows-apps.outputs.has_windows_apps == 'true'
|
||||
run: |
|
||||
cd fleet
|
||||
@@ -534,10 +640,109 @@ jobs:
|
||||
|
||||
# Replace apps.json with filtered version
|
||||
Move-Item -Path $filteredAppsJson -Destination "ee\maintained-apps\outputs\apps.json" -Force
|
||||
# Use Windows PowerShell 5.1 (not pwsh): the "Remove pre-installed PowerShell"
|
||||
# step above may have uninstalled PowerShell 7, so pwsh.exe may be unavailable.
|
||||
shell: powershell
|
||||
|
||||
- name: Remove pre-installed Git
|
||||
if: steps.check-windows-apps.outputs.has_windows_apps == 'true' && steps.check-windows-apps.outputs.has_git == 'true'
|
||||
# IMPORTANT: this MUST run AFTER "Filter apps.json for changed apps" (which uses Git
|
||||
# Bash) and BEFORE "Validate changed apps". Git for Windows provides the 'bash' the
|
||||
# filter step relies on; validation runs 'go run -buildvcs=false' and needs no bash.
|
||||
run: |
|
||||
Write-Host "Listing all installed packages containing 'Git':"
|
||||
Get-Package | Where-Object { $_.Name -like "*Git*" } | ForEach-Object {
|
||||
Write-Host " - $($_.Name) (Version: $($_.Version))"
|
||||
}
|
||||
|
||||
# Stop Git-related processes so the uninstaller doesn't fail on locked files
|
||||
Get-Process -Name "git","bash","sh","ssh-agent","gitk","wish" -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# Git for Windows installs via Inno Setup. Its registry DisplayName is not
|
||||
# reliably "Git version <ver>" (the runner's pre-installed Git is listed as
|
||||
# just "Git"), so anchor on the publisher -- which is unique to Git for
|
||||
# Windows -- and loosely guard the DisplayName. Use the registry
|
||||
# UninstallString (Inno has no MSI ProductCode).
|
||||
$uninstallPaths = @(
|
||||
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
||||
"HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
||||
)
|
||||
|
||||
$found = $false
|
||||
foreach ($path in $uninstallPaths) {
|
||||
$entries = Get-ItemProperty $path -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "Git*" -and $_.Publisher -like "*The Git Development Community*" }
|
||||
foreach ($entry in $entries) {
|
||||
if (-not $entry) { continue }
|
||||
$found = $true
|
||||
Write-Host "Found Git uninstall entry: $($entry.DisplayName) (Version: $($entry.DisplayVersion))"
|
||||
|
||||
$uninstallString = if ($entry.QuietUninstallString) {
|
||||
$entry.QuietUninstallString
|
||||
} elseif ($entry.UninstallString) {
|
||||
$entry.UninstallString
|
||||
} else {
|
||||
$null
|
||||
}
|
||||
|
||||
if ($uninstallString) {
|
||||
Write-Host "Found Git uninstall path: $uninstallString"
|
||||
try {
|
||||
# Git for Windows uses an Inno Setup uninstaller (unins000.exe). Parse the
|
||||
# exe path (quoted or unquoted) and run it with silent Inno switches.
|
||||
$exePath = ""
|
||||
if ($uninstallString -match '^\s*"([^"]+)"') {
|
||||
$exePath = $matches[1]
|
||||
} elseif ($uninstallString -match '(?i)^\s*(.+?\.exe)') {
|
||||
$exePath = $matches[1]
|
||||
}
|
||||
if ($exePath) {
|
||||
Write-Host "Uninstalling Git via: $exePath"
|
||||
Start-Process -FilePath $exePath -ArgumentList "/VERYSILENT","/SUPPRESSMSGBOXES","/NORESTART" -Wait -NoNewWindow
|
||||
Write-Host "Successfully removed Git via Inno uninstaller"
|
||||
} else {
|
||||
Write-Host "Could not parse uninstall string format: $uninstallString"
|
||||
}
|
||||
} catch {
|
||||
Write-Host "Failed to remove Git: $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
Write-Host "Git uninstall string not found in registry entry"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $found) {
|
||||
Write-Host "Git uninstall path not found in registry"
|
||||
}
|
||||
|
||||
# Force-remove leftover Git directory in case files remain after uninstall
|
||||
$gitDir = "C:\Program Files\Git"
|
||||
if (Test-Path $gitDir) {
|
||||
Write-Host "Removing leftover directory: $gitDir"
|
||||
Remove-Item -Path $gitDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
if (Test-Path $gitDir) {
|
||||
Write-Host "WARNING: Failed to fully remove $gitDir"
|
||||
} else {
|
||||
Write-Host "Removed $gitDir"
|
||||
}
|
||||
}
|
||||
# Use Windows PowerShell 5.1 (not pwsh): the "Remove pre-installed PowerShell"
|
||||
# step above may have uninstalled PowerShell 7, so pwsh.exe may be unavailable.
|
||||
shell: powershell
|
||||
|
||||
- name: Validate changed apps
|
||||
if: steps.check-windows-apps.outputs.has_windows_apps == 'true'
|
||||
# -buildvcs=false so 'go run' does not invoke git for VCS stamping: the
|
||||
# "Remove pre-installed Git" step above may have removed git from the runner.
|
||||
run: |
|
||||
cd fleet
|
||||
$env:GITHUB_WORKSPACE = (Get-Location).Path
|
||||
|
||||
# Run validation
|
||||
ls "C:\Program Files"
|
||||
go run ./cmd/maintained-apps/validate
|
||||
go run -buildvcs=false ./cmd/maintained-apps/validate
|
||||
|
||||
# Restore original apps.json
|
||||
Move-Item -Path "ee\maintained-apps\outputs\apps.json.backup" -Destination "ee\maintained-apps\outputs\apps.json" -Force
|
||||
|
||||
Reference in New Issue
Block a user