From 23da237942eaed16c64508bb54f1a712eddffa8d Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Sun, 7 Jun 2026 17:46:05 -0500 Subject: [PATCH] Add PostgreSQL 18 as a Windows FMA (#47045) Add support for PostgreSQL 18 on Windows: new winget input manifest and per-machine installer/uninstaller PowerShell scripts; outputs metadata for version 18.4-1 (installer URL, sha256, and script refs) and app registration in apps.json. Also add frontend icon component and 60x60 image asset, and update icons index to include the new icon. This enables silent install/uninstall via the EnterpriseDB (BitRock) installer and exposes the app in the software listing. --- .../inputs/winget/postgresql-18.json | 12 +++ .../winget/scripts/postgresql-18_install.ps1 | 25 ++++++ .../scripts/postgresql-18_uninstall.ps1 | 72 ++++++++++++++++++ ee/maintained-apps/outputs/apps.json | 7 ++ .../outputs/postgresql-18/windows.json | 22 ++++++ .../components/icons/Postgresql18.tsx | 14 ++++ .../SoftwarePage/components/icons/index.ts | 2 + .../app-icon-postgresql-18-60x60@2x.png | Bin 0 -> 13272 bytes 8 files changed, 154 insertions(+) create mode 100644 ee/maintained-apps/inputs/winget/postgresql-18.json create mode 100644 ee/maintained-apps/inputs/winget/scripts/postgresql-18_install.ps1 create mode 100644 ee/maintained-apps/inputs/winget/scripts/postgresql-18_uninstall.ps1 create mode 100644 ee/maintained-apps/outputs/postgresql-18/windows.json create mode 100644 frontend/pages/SoftwarePage/components/icons/Postgresql18.tsx create mode 100644 website/assets/images/app-icon-postgresql-18-60x60@2x.png diff --git a/ee/maintained-apps/inputs/winget/postgresql-18.json b/ee/maintained-apps/inputs/winget/postgresql-18.json new file mode 100644 index 0000000000..25e3d80f78 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/postgresql-18.json @@ -0,0 +1,12 @@ +{ + "name": "PostgreSQL 18", + "slug": "postgresql-18/windows", + "package_identifier": "PostgreSQL.PostgreSQL.18", + "unique_identifier": "PostgreSQL 18", + "installer_arch": "x64", + "installer_type": "exe", + "installer_scope": "machine", + "default_categories": ["Developer tools"], + "install_script_path": "ee/maintained-apps/inputs/winget/scripts/postgresql-18_install.ps1", + "uninstall_script_path": "ee/maintained-apps/inputs/winget/scripts/postgresql-18_uninstall.ps1" +} diff --git a/ee/maintained-apps/inputs/winget/scripts/postgresql-18_install.ps1 b/ee/maintained-apps/inputs/winget/scripts/postgresql-18_install.ps1 new file mode 100644 index 0000000000..4813894634 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/scripts/postgresql-18_install.ps1 @@ -0,0 +1,25 @@ +$exeFilePath = "${env:INSTALLER_PATH}" + +try { + +# EnterpriseDB (BitRock InstallBuilder) installer. Switches from the winget +# manifest: --mode unattended for a non-interactive install, --unattendedmodeui +# none to suppress all progress UI. Installs per-machine under +# C:\Program Files\PostgreSQL\18 and registers in HKLM Add/Remove Programs. +$processOptions = @{ + FilePath = "$exeFilePath" + ArgumentList = "--mode", "unattended", "--unattendedmodeui", "none" + PassThru = $true + Wait = $true +} + +$process = Start-Process @processOptions +$exitCode = $process.ExitCode + +Write-Host "Install exit code: $exitCode" +Exit $exitCode + +} catch { + Write-Host "Error: $_" + Exit 1 +} diff --git a/ee/maintained-apps/inputs/winget/scripts/postgresql-18_uninstall.ps1 b/ee/maintained-apps/inputs/winget/scripts/postgresql-18_uninstall.ps1 new file mode 100644 index 0000000000..6cef88cf63 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/scripts/postgresql-18_uninstall.ps1 @@ -0,0 +1,72 @@ +$displayNameLike = "PostgreSQL 18*" +$publisher = "PostgreSQL Global Development Group" + +$paths = @( + 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKCU:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall' +) + +$uninstall = $null +foreach ($p in $paths) { + $items = Get-ItemProperty "$p\*" -ErrorAction SilentlyContinue | Where-Object { + $_.DisplayName -like $displayNameLike -and $_.Publisher -like "$publisher*" + } + if ($items) { $uninstall = $items | Select-Object -First 1; break } +} + +if (-not $uninstall -or (-not $uninstall.UninstallString -and -not $uninstall.QuietUninstallString)) { + Write-Host "Uninstall entry not found" + Exit 0 +} + +$uninstallCommand = if ($uninstall.QuietUninstallString) { + $uninstall.QuietUninstallString +} else { + $uninstall.UninstallString +} + +# Registry uninstall strings come in three shapes; parse defensively. The EDB +# uninstaller (uninstall-postgresql.exe) lives under a path with spaces +# ("C:\Program Files\PostgreSQL\18\...") and is typically quoted. +$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 { + Throw "Could not parse uninstall string: $uninstallCommand" +} + +# BitRock uninstaller silent switch. +if ($existingArgs -notmatch '(?i)--mode\s+unattended') { + $existingArgs = ("$existingArgs --mode unattended").Trim() +} + +Write-Host "Uninstall command: $exePath" +Write-Host "Uninstall args: $existingArgs" + +try { + $processOptions = @{ + FilePath = $exePath + ArgumentList = $existingArgs + NoNewWindow = $true + PassThru = $true + Wait = $true + } + + $process = Start-Process @processOptions + $exitCode = $process.ExitCode + Write-Host "Uninstall exit code: $exitCode" + Exit $exitCode +} catch { + Write-Host "Error running uninstaller: $_" + Exit 1 +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index 6fd0fe196a..2c97f7e6f7 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -2129,6 +2129,13 @@ "unique_identifier": "io.podmandesktop.PodmanDesktop", "description": "Open source tool for developers to work with containers and Kubernetes." }, + { + "name": "PostgreSQL 18", + "slug": "postgresql-18/windows", + "platform": "windows", + "unique_identifier": "PostgreSQL 18", + "description": "PostgreSQL is a powerful, open source object-relational database system." + }, { "name": "Postman", "slug": "postman/darwin", diff --git a/ee/maintained-apps/outputs/postgresql-18/windows.json b/ee/maintained-apps/outputs/postgresql-18/windows.json new file mode 100644 index 0000000000..c2d8d8ed37 --- /dev/null +++ b/ee/maintained-apps/outputs/postgresql-18/windows.json @@ -0,0 +1,22 @@ +{ + "versions": [ + { + "version": "18.4-1", + "queries": { + "exists": "SELECT 1 FROM programs WHERE name = 'PostgreSQL 18' AND publisher = 'PostgreSQL Global Development Group';", + "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = 'PostgreSQL 18' AND publisher = 'PostgreSQL Global Development Group' AND version_compare(version, '18.4-1') < 0);" + }, + "installer_url": "https://get.enterprisedb.com/postgresql/postgresql-18.4-1-windows-x64.exe", + "install_script_ref": "bc68ad4d", + "uninstall_script_ref": "24e7779d", + "sha256": "44b8187d2db7e866495952d8260a1d7252cbb5125843142e1f0bf30115d23279", + "default_categories": [ + "Developer tools" + ] + } + ], + "refs": { + "24e7779d": "$displayNameLike = \"PostgreSQL 18*\"\n$publisher = \"PostgreSQL Global Development Group\"\n\n$paths = @(\n 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKCU:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'\n)\n\n$uninstall = $null\nforeach ($p in $paths) {\n $items = Get-ItemProperty \"$p\\*\" -ErrorAction SilentlyContinue | Where-Object {\n $_.DisplayName -like $displayNameLike -and $_.Publisher -like \"$publisher*\"\n }\n if ($items) { $uninstall = $items | Select-Object -First 1; break }\n}\n\nif (-not $uninstall -or (-not $uninstall.UninstallString -and -not $uninstall.QuietUninstallString)) {\n Write-Host \"Uninstall entry not found\"\n Exit 0\n}\n\n$uninstallCommand = if ($uninstall.QuietUninstallString) {\n $uninstall.QuietUninstallString\n} else {\n $uninstall.UninstallString\n}\n\n# Registry uninstall strings come in three shapes; parse defensively. The EDB\n# uninstaller (uninstall-postgresql.exe) lives under a path with spaces\n# (\"C:\\Program Files\\PostgreSQL\\18\\...\") and is typically quoted.\n$exePath = \"\"\n$existingArgs = \"\"\nif ($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 Throw \"Could not parse uninstall string: $uninstallCommand\"\n}\n\n# BitRock uninstaller silent switch.\nif ($existingArgs -notmatch '(?i)--mode\\s+unattended') {\n $existingArgs = (\"$existingArgs --mode unattended\").Trim()\n}\n\nWrite-Host \"Uninstall command: $exePath\"\nWrite-Host \"Uninstall args: $existingArgs\"\n\ntry {\n $processOptions = @{\n FilePath = $exePath\n ArgumentList = $existingArgs\n NoNewWindow = $true\n PassThru = $true\n Wait = $true\n }\n\n $process = Start-Process @processOptions\n $exitCode = $process.ExitCode\n Write-Host \"Uninstall exit code: $exitCode\"\n Exit $exitCode\n} catch {\n Write-Host \"Error running uninstaller: $_\"\n Exit 1\n}\n", + "bc68ad4d": "$exeFilePath = \"${env:INSTALLER_PATH}\"\n\ntry {\n\n# EnterpriseDB (BitRock InstallBuilder) installer. Switches from the winget\n# manifest: --mode unattended for a non-interactive install, --unattendedmodeui\n# none to suppress all progress UI. Installs per-machine under\n# C:\\Program Files\\PostgreSQL\\18 and registers in HKLM Add/Remove Programs.\n$processOptions = @{\n FilePath = \"$exeFilePath\"\n ArgumentList = \"--mode\", \"unattended\", \"--unattendedmodeui\", \"none\"\n PassThru = $true\n Wait = $true\n}\n\n$process = Start-Process @processOptions\n$exitCode = $process.ExitCode\n\nWrite-Host \"Install exit code: $exitCode\"\nExit $exitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Exit 1\n}\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/Postgresql18.tsx b/frontend/pages/SoftwarePage/components/icons/Postgresql18.tsx new file mode 100644 index 0000000000..d2b2c94c63 --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/Postgresql18.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const Postgresql18 = (props: SVGProps) => ( + + + +); +export default Postgresql18; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index b534a13f40..20c0d3a6e7 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -61,6 +61,7 @@ import OktaVerify from "./OktaVerify"; import Ollama from "./Ollama"; import OpenvpnConnect from "./OpenvpnConnect"; import PlantronicsHub from "./PlantronicsHub"; +import Postgresql18 from "./Postgresql18"; import PowerAutomate from "./PowerAutomate"; import PowerBi from "./PowerBi"; import Plugdata from "./Plugdata"; @@ -535,6 +536,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { "plantronics hub": PlantronicsHub, plugdata: Plugdata, "podman desktop": PodmanDesktop, + "postgresql 18": Postgresql18, postman: Postman, "power automate": PowerAutomate, "power bi": PowerBi, diff --git a/website/assets/images/app-icon-postgresql-18-60x60@2x.png b/website/assets/images/app-icon-postgresql-18-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..39a03f3611915b1beae522c22734fc71185b1a2f GIT binary patch literal 13272 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