From 051d12718cf45fa2cc43dc89ff4412fdd7404d23 Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Fri, 7 Aug 2026 12:16:17 -0500 Subject: [PATCH] Add JetBrains ReSharper as a Windows Fleet-maintained app (#50659) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Related issue:** Resolves #50567 Adds JetBrains ReSharper as a Windows Fleet-maintained app (winget `JetBrains.ReSharper`, version `2026.2.0.2`). ReSharper is a Visual Studio extension rather than a standalone app, so it does not follow the pattern of the other JetBrains FMAs (Rider, PhpStorm, DataGrip, etc.), which are plain NSIS installers that take `/S`. Reviewers should read the risks below before approving — a couple of things can only be confirmed from a validator run. ## What's here - `ee/maintained-apps/inputs/winget/resharper.json` - `ee/maintained-apps/inputs/winget/scripts/resharper_install.ps1` / `resharper_uninstall.ps1` - Generated `ee/maintained-apps/outputs/resharper/windows.json` + `apps.json` entry - Icon (`Resharper.tsx`, website PNG, alphabetical `index.ts` entries), generated from JetBrains' own brand asset ## Decisions that differ from the other JetBrains FMAs **`use_display_version_for_patch` is omitted.** Every other JetBrains winget input sets it, but the ReSharper manifest has no `AppsAndFeaturesEntries`, so the ingester hard-errors with `use_display_version_for_patch is set but no DisplayVersion found in winget manifest`. The patch policy therefore compares against winget's `2026.2.0.2`. **Custom `exists_query` instead of `fuzzy_match_name`.** The prefix is loose enough to match a possible per-VS-instance suffix, and excludes the separate ReSharper C++ and ReSharper SDK products: ```sql SELECT 1 FROM programs WHERE name LIKE 'JetBrains ReSharper%' AND name NOT LIKE 'JetBrains ReSharper C++%' AND name NOT LIKE 'JetBrains ReSharper SDK%' AND publisher = 'JetBrains s.r.o.'; ``` **Install script detects Visual Studio.** It builds `/VsVersion` from the instances `vswhere` reports and runs the installer with `/Silent=True /PerMachine=True /SkipEtwService=True`: - `/PerMachine=True` — the installer otherwise targets `%LocalAppData%`, which under Fleet's SYSTEM context would land in the SYSTEM profile instead of the developer's. The path is not configurable ([RSRP-428991](https://youtrack.jetbrains.com/issue/RSRP-428991)). - `/SkipEtwService=True` — JetBrains documents that `EtwHostService.msi` always raises a UAC prompt, so a fully silent install of every component is not possible ([SUPPORT-A-3189](https://youtrack.jetbrains.com/articles/SUPPORT-A-3189)). - It then waits for the uninstall registry entry (what osquery reads), because the web bootstrapper can outlive its own exit code, and logs the resulting ARP entries. **Uninstall removes every matching entry**, since ReSharper registers one per Visual Studio instance, using the defensive `UninstallString` parser and appending `/Silent=True` rather than the NSIS `/S`. Switches come from [JetBrains' silent install/uninstall article](https://resharper-support.jetbrains.com/hc/en-us/articles/207241485-How-to-use-silent-install-and-silent-uninstall-of-ReSharper-via-Command-Line), not guesswork. ## `unique_identifier` is provisional `program_publisher` is verified — `JetBrains s.r.o.` is hard-coded next to the ARP value names (`DisplayName`, `DisplayVersion`, `UninstallString`, `Publisher`) in `JetBrains.Platform.Installer.exe`, extracted from the installer. The **DisplayName is not verifiable offline.** The winget URL is a two-stage web bootstrapper: the 69 MB `.web.exe` contains `JetBrains.Platform.Installer.Bootstrap.exe`, which downloads the JetBrains dotUltimate installer, which downloads the product packages. The ARP entry is written by that downloaded stage under `Software\Microsoft\Windows\CurrentVersion\Uninstall\{GUID}`, with `DisplayName` taken from a per-VS-host `PresentableName`. So `JetBrains ReSharper` is a best-supported guess. It can be confirmed from a validator run: `cmd/maintained-apps/validate/windows.go` searches `programs` with a loose `LOWER(name) LIKE '%…%'` on both the catalog name and `unique_identifier`, and logs `Found app: '' … Version: ` after running `MutateSoftwareOnIngestion`. That reveals both the true DisplayName and the post-mutation version. The install script prints the same information. **Expect a follow-up commit correcting `unique_identifier` (and possibly the exists query) once that log lands.** ## Risks 1. **Payload is not pinned.** The SHA covers only the 69 MB bootstrapper; roughly 1.7 GB is fetched from `download.jetbrains.com` at install time. JetBrains publishes only a `windowsWeb` download for ReSharper, so there is no offline installer to point at. Install duration may exceed script timeouts. 2. **Requires Visual Studio.** With no VS present the installer has nothing to install, so the script exits 1 with a clear message. `windows-latest` runners ship Visual Studio 2022 Enterprise, so validation should be able to install. 3. **`/PerMachine=True` conflicts with pre-existing per-user installs.** JetBrains states machine-wide mode "is not compatible with existing installations in user profiles"; one must be removed first. 4. **`/SkipEtwService=True` omits the ETW host service**, so dotTrace/dotMemory profiling integration is incomplete. This is the documented tradeoff for an unattended install. 5. **Version reconciliation unconfirmed.** If the DisplayName ends in a marketing version, the JetBrains name-based version mutation fires and the validator's prefix check passes; a VS-suffixed name would instead fall back to the registry `DisplayVersion`. # 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 - [x] `go test ./ee/maintained-apps/...` passes; both output JSON files parse; generated SHA matches the winget manifest. - [ ] QA'd all new/changed functionality manually — **not done.** Install/uninstall need a Windows host with Visual Studio; relying on the FMA Windows validator, which is also how `unique_identifier` gets confirmed. ## Summary by CodeRabbit * **New Features** * Added ReSharper to the maintained Windows applications catalog. * Added support for silent machine-wide installation and uninstallation. * Added Visual Studio compatibility checks and installation failure reporting. * Added a ReSharper icon for display in the software catalog. --- .../inputs/winget/resharper.json | 13 +++ .../winget/scripts/resharper_install.ps1 | 97 ++++++++++++++++++ .../winget/scripts/resharper_uninstall.ps1 | 93 +++++++++++++++++ ee/maintained-apps/outputs/apps.json | 7 ++ .../outputs/resharper/windows.json | 22 ++++ .../components/icons/Resharper.tsx | 14 +++ .../SoftwarePage/components/icons/index.ts | 2 + .../images/app-icon-resharper-60x60@2x.png | Bin 0 -> 9137 bytes 8 files changed, 248 insertions(+) create mode 100644 ee/maintained-apps/inputs/winget/resharper.json create mode 100644 ee/maintained-apps/inputs/winget/scripts/resharper_install.ps1 create mode 100644 ee/maintained-apps/inputs/winget/scripts/resharper_uninstall.ps1 create mode 100644 ee/maintained-apps/outputs/resharper/windows.json create mode 100644 frontend/pages/SoftwarePage/components/icons/Resharper.tsx create mode 100644 website/assets/images/app-icon-resharper-60x60@2x.png diff --git a/ee/maintained-apps/inputs/winget/resharper.json b/ee/maintained-apps/inputs/winget/resharper.json new file mode 100644 index 0000000000..c8e952021d --- /dev/null +++ b/ee/maintained-apps/inputs/winget/resharper.json @@ -0,0 +1,13 @@ +{ + "name": "ReSharper", + "slug": "resharper/windows", + "package_identifier": "JetBrains.ReSharper", + "unique_identifier": "JetBrains ReSharper", + "exists_query": "SELECT 1 FROM programs WHERE name LIKE 'JetBrains ReSharper%' AND name NOT LIKE 'JetBrains ReSharper C++%' AND name NOT LIKE 'JetBrains ReSharper SDK%' AND publisher = 'JetBrains s.r.o.';", + "install_script_path": "ee/maintained-apps/inputs/winget/scripts/resharper_install.ps1", + "uninstall_script_path": "ee/maintained-apps/inputs/winget/scripts/resharper_uninstall.ps1", + "installer_arch": "x64", + "installer_type": "exe", + "installer_scope": "machine", + "default_categories": ["Developer tools"] +} diff --git a/ee/maintained-apps/inputs/winget/scripts/resharper_install.ps1 b/ee/maintained-apps/inputs/winget/scripts/resharper_install.ps1 new file mode 100644 index 0000000000..25e3b8ec23 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/scripts/resharper_install.ps1 @@ -0,0 +1,97 @@ +# Learn more about .exe install scripts: +# http://fleetdm.com/learn-more-about/exe-install-scripts +# +# ReSharper is a Visual Studio extension installed by a web bootstrapper. +# /PerMachine=True keeps it out of the SYSTEM profile, /SkipEtwService=True avoids +# an unavoidable UAC prompt, /VsVersion picks the VS instances to integrate into. +# https://resharper-support.jetbrains.com/hc/en-us/articles/207241485 + +$exeFilePath = "${env:INSTALLER_PATH}" + +$registryTimeoutSeconds = 3300 + +$uninstallPaths = @( + 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall' +) + +function Get-ReSharperEntries { + Get-ChildItem -Path $uninstallPaths -ErrorAction SilentlyContinue | + ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } | + Where-Object { + $_.DisplayName -like 'JetBrains ReSharper*' -and + $_.DisplayName -notlike 'JetBrains ReSharper C++*' -and + $_.DisplayName -notlike 'JetBrains ReSharper SDK*' -and + $_.Publisher -like '*JetBrains*' + } +} + +try { + +$vsWhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe' +if (-not (Test-Path $vsWhere)) { + Write-Host "Visual Studio Installer not found at $vsWhere." + Write-Host "ReSharper is a Visual Studio extension and cannot be installed without Visual Studio." + Exit 1 +} + +$installationVersions = & $vsWhere -all -prerelease -products '*' -property installationVersion 2>$null +$vsMajors = @( + $installationVersions | + Where-Object { $_ -match '^\d+' } | + ForEach-Object { [int](($_ -split '\.')[0]) } | + Sort-Object -Unique -Descending +) + +if ($vsMajors.Count -eq 0) { + Write-Host "No Visual Studio instances were reported by vswhere." + Write-Host "ReSharper is a Visual Studio extension and cannot be installed without Visual Studio." + Exit 1 +} + +$vsVersions = ($vsMajors | ForEach-Object { "$_.0" }) -join ';' +Write-Host "Visual Studio instances detected: $vsVersions" + +$logFile = Join-Path $env:TEMP 'resharper-install.log' + +$processOptions = @{ + FilePath = "$exeFilePath" + ArgumentList = "/Silent=True /PerMachine=True /SkipEtwService=True /VsVersion=$vsVersions /LogFile=`"$logFile`"" + PassThru = $true + Wait = $true +} + +$process = Start-Process @processOptions +$exitCode = $process.ExitCode +Write-Host "Installer exit code: $exitCode" + +$deadline = (Get-Date).AddSeconds($registryTimeoutSeconds) +$entries = @(Get-ReSharperEntries) +while ($entries.Count -eq 0 -and (Get-Date) -lt $deadline) { + $installerWasRunning = @(Get-Process -Name 'JetBrains.Platform.Installer*' -ErrorAction SilentlyContinue).Count -gt 0 + Start-Sleep -Seconds 10 + $entries = @(Get-ReSharperEntries) + $installerIsRunning = @(Get-Process -Name 'JetBrains.Platform.Installer*' -ErrorAction SilentlyContinue).Count -gt 0 + if ($entries.Count -eq 0 -and -not $installerWasRunning -and -not $installerIsRunning) { break } +} + +if ($entries.Count -eq 0) { + Write-Host "The ReSharper uninstall registry entry did not appear." + if (Test-Path $logFile) { + Write-Host "--- last 50 lines of $logFile ---" + Get-Content $logFile -Tail 50 | ForEach-Object { Write-Host $_ } + } + if ($exitCode -eq 0) { Exit 1 } + Exit $exitCode +} + +foreach ($entry in $entries) { + Write-Host "Installed: DisplayName='$($entry.DisplayName)' DisplayVersion='$($entry.DisplayVersion)' Publisher='$($entry.Publisher)'" +} + +Exit $exitCode + +} catch { + Write-Host "Error: $_" + Exit 1 +} diff --git a/ee/maintained-apps/inputs/winget/scripts/resharper_uninstall.ps1 b/ee/maintained-apps/inputs/winget/scripts/resharper_uninstall.ps1 new file mode 100644 index 0000000000..dae40585c9 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/scripts/resharper_uninstall.ps1 @@ -0,0 +1,93 @@ +# Removes JetBrains ReSharper via its registry uninstall entries. ReSharper +# registers one per Visual Studio instance, so every match is removed, and the +# JetBrains uninstaller takes /Silent=True rather than the NSIS /S. +# https://resharper-support.jetbrains.com/hc/en-us/articles/207241485 + +$softwareNameLike = "JetBrains ReSharper*" +$publisherLike = "*JetBrains*" + +$paths = @( + 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall' +) + +$exitCode = 0 + +try { + +[array]$uninstallKeys = Get-ChildItem ` + -Path $paths ` + -ErrorAction SilentlyContinue | + ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } + +# ReSharper C++ and the ReSharper SDK are separate products. +[array]$selected = $uninstallKeys | Where-Object { + $_.DisplayName -and + $_.DisplayName -like $softwareNameLike -and + $_.DisplayName -notlike 'JetBrains ReSharper C++*' -and + $_.DisplayName -notlike 'JetBrains ReSharper SDK*' -and + $_.Publisher -like $publisherLike -and + $_.UninstallString +} + +if ($selected.Count -eq 0) { + Write-Host "Uninstall entry not found for $softwareNameLike; nothing to do." + Exit 0 +} + +Stop-Process -Name "devenv" -Force -ErrorAction SilentlyContinue +Stop-Process -Name "JetBrains.Etw.Collector.Host" -Force -ErrorAction SilentlyContinue +Stop-Process -Name "JetBrains.Platform.Satellite" -Force -ErrorAction SilentlyContinue + +foreach ($entry in $selected) { + $uninstallCommand = $entry.UninstallString + + # JetBrains stores unquoted paths that contain spaces. + $exePath = "" + $existingArgs = "" + if ($uninstallCommand -match '^\s*"([^"]+)"\s*(.*)$') { + $exePath = $matches[1] + $existingArgs = $matches[2].Trim() + } elseif ($uninstallCommand -match '(?i)^\s*(.+?\.exe)\s*(.*)$') { + $exePath = $matches[1] + $existingArgs = $matches[2].Trim() + } elseif ($uninstallCommand -match '^\s*(\S+)\s*(.*)$') { + $exePath = $matches[1] + $existingArgs = $matches[2].Trim() + } else { + Write-Host "Could not parse uninstall string: $uninstallCommand" + $exitCode = 1 + continue + } + + if ($existingArgs -notmatch '(?i)/Silent=') { + $existingArgs = ("$existingArgs /Silent=True").Trim() + } + + Write-Host "Selected entry DisplayName: $($entry.DisplayName)" + Write-Host "Uninstall command: $exePath" + Write-Host "Uninstall args: $existingArgs" + + $processOptions = @{ + FilePath = $exePath + PassThru = $true + Wait = $true + } + + if ($existingArgs -ne '') { + $processOptions.ArgumentList = $existingArgs + } + + $process = Start-Process @processOptions + Write-Host "Uninstall exit code: $($process.ExitCode)" + if ($process.ExitCode -ne 0) { + $exitCode = $process.ExitCode + } +} + +Exit $exitCode + +} catch { + Write-Host "Error: $_" + Exit 1 +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index f16afc315a..2c1867ae48 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -7295,6 +7295,13 @@ "unique_identifier": "Requestly", "description": "Requestly is an app for intercepting and modifying HTTP requests." }, + { + "name": "ReSharper", + "slug": "resharper/windows", + "platform": "windows", + "unique_identifier": "JetBrains ReSharper", + "description": "ReSharper is a Visual Studio extension for .NET developers." + }, { "name": "Retcon", "slug": "retcon/darwin", diff --git a/ee/maintained-apps/outputs/resharper/windows.json b/ee/maintained-apps/outputs/resharper/windows.json new file mode 100644 index 0000000000..40350109d1 --- /dev/null +++ b/ee/maintained-apps/outputs/resharper/windows.json @@ -0,0 +1,22 @@ +{ + "versions": [ + { + "version": "2026.2.0.2", + "queries": { + "exists": "SELECT 1 FROM programs WHERE name LIKE 'JetBrains ReSharper%' AND name NOT LIKE 'JetBrains ReSharper C++%' AND name NOT LIKE 'JetBrains ReSharper SDK%' AND publisher = 'JetBrains s.r.o.';", + "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name LIKE 'JetBrains ReSharper%' AND name NOT LIKE 'JetBrains ReSharper C++%' AND name NOT LIKE 'JetBrains ReSharper SDK%' AND publisher = 'JetBrains s.r.o.' AND version_compare(version, '2026.2.0.2') < 0);" + }, + "installer_url": "https://download.jetbrains.com/resharper/dotUltimate.2026.2.0.2/JetBrains.ReSharper.2026.2.0.2.web.exe", + "install_script_ref": "97fe5e08", + "uninstall_script_ref": "78b5f38c", + "sha256": "a34ea0d4878cc88bd726ba5e1ce024fb4e62225fb76e2869c3b8ee1891a2c25b", + "default_categories": [ + "Developer tools" + ] + } + ], + "refs": { + "78b5f38c": "# Removes JetBrains ReSharper via its registry uninstall entries. ReSharper\n# registers one per Visual Studio instance, so every match is removed, and the\n# JetBrains uninstaller takes /Silent=True rather than the NSIS /S.\n# https://resharper-support.jetbrains.com/hc/en-us/articles/207241485\n\n$softwareNameLike = \"JetBrains ReSharper*\"\n$publisherLike = \"*JetBrains*\"\n\n$paths = @(\n 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'\n)\n\n$exitCode = 0\n\ntry {\n\n[array]$uninstallKeys = Get-ChildItem `\n -Path $paths `\n -ErrorAction SilentlyContinue |\n ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue }\n\n# ReSharper C++ and the ReSharper SDK are separate products.\n[array]$selected = $uninstallKeys | Where-Object {\n $_.DisplayName -and\n $_.DisplayName -like $softwareNameLike -and\n $_.DisplayName -notlike 'JetBrains ReSharper C++*' -and\n $_.DisplayName -notlike 'JetBrains ReSharper SDK*' -and\n $_.Publisher -like $publisherLike -and\n $_.UninstallString\n}\n\nif ($selected.Count -eq 0) {\n Write-Host \"Uninstall entry not found for $softwareNameLike; nothing to do.\"\n Exit 0\n}\n\nStop-Process -Name \"devenv\" -Force -ErrorAction SilentlyContinue\nStop-Process -Name \"JetBrains.Etw.Collector.Host\" -Force -ErrorAction SilentlyContinue\nStop-Process -Name \"JetBrains.Platform.Satellite\" -Force -ErrorAction SilentlyContinue\n\nforeach ($entry in $selected) {\n $uninstallCommand = $entry.UninstallString\n\n # JetBrains stores unquoted paths that contain spaces.\n $exePath = \"\"\n $existingArgs = \"\"\n if ($uninstallCommand -match '^\\s*\"([^\"]+)\"\\s*(.*)$') {\n $exePath = $matches[1]\n $existingArgs = $matches[2].Trim()\n } elseif ($uninstallCommand -match '(?i)^\\s*(.+?\\.exe)\\s*(.*)$') {\n $exePath = $matches[1]\n $existingArgs = $matches[2].Trim()\n } elseif ($uninstallCommand -match '^\\s*(\\S+)\\s*(.*)$') {\n $exePath = $matches[1]\n $existingArgs = $matches[2].Trim()\n } else {\n Write-Host \"Could not parse uninstall string: $uninstallCommand\"\n $exitCode = 1\n continue\n }\n\n if ($existingArgs -notmatch '(?i)/Silent=') {\n $existingArgs = (\"$existingArgs /Silent=True\").Trim()\n }\n\n Write-Host \"Selected entry DisplayName: $($entry.DisplayName)\"\n Write-Host \"Uninstall command: $exePath\"\n Write-Host \"Uninstall args: $existingArgs\"\n\n $processOptions = @{\n FilePath = $exePath\n PassThru = $true\n Wait = $true\n }\n\n if ($existingArgs -ne '') {\n $processOptions.ArgumentList = $existingArgs\n }\n\n $process = Start-Process @processOptions\n Write-Host \"Uninstall exit code: $($process.ExitCode)\"\n if ($process.ExitCode -ne 0) {\n $exitCode = $process.ExitCode\n }\n}\n\nExit $exitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n", + "97fe5e08": "# Learn more about .exe install scripts:\n# http://fleetdm.com/learn-more-about/exe-install-scripts\n#\n# ReSharper is a Visual Studio extension installed by a web bootstrapper.\n# /PerMachine=True keeps it out of the SYSTEM profile, /SkipEtwService=True avoids\n# an unavoidable UAC prompt, /VsVersion picks the VS instances to integrate into.\n# https://resharper-support.jetbrains.com/hc/en-us/articles/207241485\n\n$exeFilePath = \"${env:INSTALLER_PATH}\"\n\n# Under production's 1 hour cap; the wait below also gives up early.\n$registryTimeoutSeconds = 3300\n\n$uninstallPaths = @(\n 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'\n)\n\nfunction Get-ReSharperEntries {\n Get-ChildItem -Path $uninstallPaths -ErrorAction SilentlyContinue |\n ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } |\n Where-Object {\n $_.DisplayName -like 'JetBrains ReSharper*' -and\n $_.DisplayName -notlike 'JetBrains ReSharper C++*' -and\n $_.DisplayName -notlike 'JetBrains ReSharper SDK*' -and\n $_.Publisher -like '*JetBrains*'\n }\n}\n\ntry {\n\n$vsWhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\\Installer\\vswhere.exe'\nif (-not (Test-Path $vsWhere)) {\n Write-Host \"Visual Studio Installer not found at $vsWhere.\"\n Write-Host \"ReSharper is a Visual Studio extension and cannot be installed without Visual Studio.\"\n Exit 1\n}\n\n$installationVersions = & $vsWhere -all -prerelease -products '*' -property installationVersion 2>$null\n$vsMajors = @(\n $installationVersions |\n Where-Object { $_ -match '^\\d+' } |\n ForEach-Object { [int](($_ -split '\\.')[0]) } |\n Sort-Object -Unique -Descending\n)\n\nif ($vsMajors.Count -eq 0) {\n Write-Host \"No Visual Studio instances were reported by vswhere.\"\n Write-Host \"ReSharper is a Visual Studio extension and cannot be installed without Visual Studio.\"\n Exit 1\n}\n\n$vsVersions = ($vsMajors | ForEach-Object { \"$_.0\" }) -join ';'\nWrite-Host \"Visual Studio instances detected: $vsVersions\"\n\n$logFile = Join-Path $env:TEMP 'resharper-install.log'\n\n$processOptions = @{\n FilePath = \"$exeFilePath\"\n ArgumentList = \"/Silent=True /PerMachine=True /SkipEtwService=True /VsVersion=$vsVersions /LogFile=`\"$logFile`\"\"\n PassThru = $true\n Wait = $true\n}\n\n$process = Start-Process @processOptions\n$exitCode = $process.ExitCode\nWrite-Host \"Installer exit code: $exitCode\"\n\n# The bootstrapper can outlive its own exit code, so poll for the uninstall\n# registry entry (what osquery reports). The installer process is sampled at both\n# ends of the interval so neither one starting nor one exiting mid-sleep is\n# mistaken for nothing left to wait for.\n$deadline = (Get-Date).AddSeconds($registryTimeoutSeconds)\n$entries = @(Get-ReSharperEntries)\nwhile ($entries.Count -eq 0 -and (Get-Date) -lt $deadline) {\n $installerWasRunning = @(Get-Process -Name 'JetBrains.Platform.Installer*' -ErrorAction SilentlyContinue).Count -gt 0\n Start-Sleep -Seconds 10\n $entries = @(Get-ReSharperEntries)\n $installerIsRunning = @(Get-Process -Name 'JetBrains.Platform.Installer*' -ErrorAction SilentlyContinue).Count -gt 0\n if ($entries.Count -eq 0 -and -not $installerWasRunning -and -not $installerIsRunning) { break }\n}\n\nif ($entries.Count -eq 0) {\n Write-Host \"The ReSharper uninstall registry entry did not appear.\"\n if (Test-Path $logFile) {\n Write-Host \"--- last 50 lines of $logFile ---\"\n Get-Content $logFile -Tail 50 | ForEach-Object { Write-Host $_ }\n }\n if ($exitCode -eq 0) { Exit 1 }\n Exit $exitCode\n}\n\nforeach ($entry in $entries) {\n Write-Host \"Installed: DisplayName='$($entry.DisplayName)' DisplayVersion='$($entry.DisplayVersion)' Publisher='$($entry.Publisher)'\"\n}\n\nExit $exitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/Resharper.tsx b/frontend/pages/SoftwarePage/components/icons/Resharper.tsx new file mode 100644 index 0000000000..75dc545d88 --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/Resharper.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const Resharper = (props: SVGProps) => ( + + + +); +export default Resharper; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index 611f026362..07f53a910f 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -857,6 +857,7 @@ import RemoteBuddy from "./RemoteBuddy"; import RemoteDesktopManager from "./RemoteDesktopManager"; import Reqable from "./Reqable"; import Requestly from "./Requestly"; +import Resharper from "./Resharper"; import Retcon from "./Retcon"; import Retroarch from "./Retroarch"; import Retrobatch from "./Retrobatch"; @@ -2028,6 +2029,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { "remote desktop manager": RemoteDesktopManager, reqable: Reqable, requestly: Requestly, + resharper: Resharper, retcon: Retcon, retroarch: Retroarch, retrobatch: Retrobatch, diff --git a/website/assets/images/app-icon-resharper-60x60@2x.png b/website/assets/images/app-icon-resharper-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..00ed2eeb5fb63d032ad5290d30a1e9ae90bba236 GIT binary patch literal 9137 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 VFnqLowEzCH$V)4KtdcYi`5y{sxD5aR literal 0 HcmV?d00001