Forked from JoshuaKGoldberg/download-selenium-drivers.ps1
Last active
April 19, 2025 08:47
-
-
Save vavavr00m/c74f2039a9b97a0003eb4e1b694a05f9 to your computer and use it in GitHub Desktop.
PowerShell script to download major browser WebDriver drivers for Selenium.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Net.ServicePointManager]::SecurityProtocol = "Ssl3, Tls, Tls11, Tls12"; | |
| $currentDir = (Get-Item -Path "./").FullName; | |
| $driversDirName = Join-Path $currentDir ".drivers"; | |
| $isWindows = [System.Boolean](Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue); | |
| $webClient = New-Object System.Net.WebClient; | |
| function Get-ChromeInstalledVersion { | |
| $chromeRegistryKey = "HKCU:Software\Google\Chrome\BLBeacon" | |
| try { | |
| # Try to get the registry property for Chrome version | |
| $global:latest_chrome = (Get-ItemProperty -Path $chromeRegistryKey -ErrorAction Stop).version | |
| # Output the result | |
| if (-not [string]::IsNullOrWhiteSpace($global:latest_chrome)) { | |
| Write-Host "Chrome installed version: $global:latest_chrome" | |
| } else { | |
| Write-Error "Failed to retrieve a valid version." | |
| $global:latest_chrome = $null | |
| } | |
| } catch { | |
| Write-Error "Failed to retrieve Chrome version: $_" | |
| $global:latest_chrome = $null | |
| } | |
| } | |
| function Get-ChromeDriverLatestVersion { | |
| try { | |
| $jsonUrl = "https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json" | |
| $response = Invoke-RestMethod -Uri $jsonUrl -UseBasicParsing | |
| $global:latest_chromedriver = $response.channels.stable.version | |
| # Output the result | |
| if (-not [string]::IsNullOrWhiteSpace($global:latest_chrome)) { | |
| Write-Host "Latest ChromeDriver version: $global:latest_chromedriver" | |
| } else { | |
| Write-Error "Failed to retrieve a valid version." | |
| $global:latest_chromedriver = $null | |
| } | |
| } catch { | |
| Write-Error "Failed to get latest stable ChromeDriver version: $_" | |
| $global:latest_chromedriver = $null | |
| } | |
| } | |
| function Get-GeckoDriverLatestVersion { | |
| param () | |
| $url = "https://api.github.com/repos/mozilla/geckodriver/releases/latest" | |
| try { | |
| $response = Invoke-RestMethod -Uri $url -UseBasicParsing | |
| $global:latest_geckodriver = $response.tag_name | |
| # Output the result | |
| if (-not [string]::IsNullOrWhiteSpace($global:latest_geckodriver)) { | |
| Write-Host "Latest GeckoDriver version: $global:latest_geckodriver" | |
| } else { | |
| Write-Error "Failed to retrieve a valid version." | |
| $global:latest_geckodriver = $null | |
| } | |
| } catch { | |
| Write-Error "Failed to retrieve latest Geckodriver version: $_" | |
| $global:latest_geckodriver = $null | |
| } | |
| } | |
| Function Clean-NormalizeVersion { | |
| param ( | |
| [string]$versionString | |
| ) | |
| # Remove unwanted BOM or special characters (like "ÿþ") and any spaces | |
| $cleanedVersion = $versionString -replace '[^\d\.]', '' # Only keep digits and periods | |
| # Normalize multiple spaces (if any) between version numbers (though spaces should be mostly removed) | |
| $normalizedVersion = $cleanedVersion -replace '\s+', '.' # Replace any internal spaces with dots | |
| # Trim leading/trailing spaces | |
| $normalizedVersion = $normalizedVersion.Trim() | |
| # Return the cleaned and normalized version | |
| return $normalizedVersion | |
| } | |
| Function Get-MSEdgeDriverLatestVersion { | |
| param () | |
| $url = "https://msedgedriver.azureedge.net/LATEST_STABLE" | |
| try { | |
| # Retrieve the latest stable version from the URL | |
| $latestVersion = Invoke-RestMethod -Uri $url | |
| # Clean and normalize the retrieved version string | |
| $global:latest_msedge = Clean-NormalizeVersion -versionString $latestVersion | |
| # Ensure the version is cleaned and output the result | |
| if (-not [string]::IsNullOrWhiteSpace($global:latest_msedge)) { | |
| Write-Host "Latest MSEdgeDriver version: $global:latest_msedge" | |
| } else { | |
| Write-Error "Failed to retrieve a valid version." | |
| $global:latest_msedge = $null | |
| } | |
| } catch { | |
| Write-Error "Failed to retrieve the latest MSEdgeDriver version: $_" | |
| $global:latest_msedge = $null | |
| } | |
| } | |
| function Get-IEDriverServerLatestVersion { | |
| param ( | |
| [string]$RepoApiUrl = "https://api.github.com/repos/SeleniumHQ/selenium/releases", | |
| [string]$AssetPattern = "^IEDriverServer_Win32_(?<version>[\d\.]+)\.zip$" | |
| ) | |
| $headers = @{ 'User-Agent' = 'PowerShell' } | |
| $global:latest_iedriver = $null | |
| try { | |
| $releases = Invoke-RestMethod -Uri $RepoApiUrl -Headers $headers | |
| foreach ($release in $releases) { | |
| foreach ($asset in $release.assets) { | |
| if ($asset.name -match $AssetPattern) { | |
| $version = $Matches['version'] | |
| $global:latest_iedriver = $version | |
| $result = [PSCustomObject]@{ | |
| Name = $asset.name | |
| Version = $version | |
| Url = $asset.browser_download_url | |
| PublishedAt = $release.published_at | |
| } | |
| Write-Host "Latest IEDriverServer version: $global:latest_iedriver" | |
| return $result | Out-Null; return $result | |
| } | |
| } | |
| } | |
| Write-Warning "No matching IEDriver asset found." | |
| return $null | |
| } catch { | |
| Write-Error "Error fetching IEDriver version: $_" | |
| return $null | |
| } | |
| } | |
| function Ensure-Driver-Updates($browserName, $exeName, $download, $zipName) { | |
| $localExeName = Join-Path $driversDirName $exeName; | |
| if (Test-Path $localExeName) { | |
| Write-Host "${browserName}: driver already located at $localExeName." -f DarkGray | |
| Remove-Item $localExeName -Force | |
| if (-not (Test-Path $localExeName)) { | |
| Write-Host "${browserName}: existing driver deleted." -f DarkGray | |
| } else { | |
| Write-Warning "${browserName}: failed to delete $localExeName!" | |
| return | |
| } | |
| } | |
| if ($zipName -eq $null) { | |
| $localDownloadLocation = Join-Path $driversDirName $exeName; | |
| } else { | |
| $download = "$download/$zipName"; | |
| $localDownloadLocation = Join-Path $driversDirName $zipName; | |
| } | |
| Write-Host "${browserName}: downloading driver from $download." -f DarkGray; | |
| $webClient.downloadFile($download, $localDownloadLocation); | |
| if (!($zipName -eq $null)) { | |
| Write-Host "${browserName}: extracting driver from $localDownloadLocation." -f DarkGray; | |
| Expand-Archive -DestinationPath $driversDirName -Path $localDownloadLocation -Force; | |
| Remove-Item $localDownloadLocation; | |
| } | |
| Write-Host "${browserName}: driver placed in $localExeName." -f DarkGray; | |
| } | |
| function Ensure-Chrome-Driver() { | |
| Ensure-Driver-Updates "Chrome" "chromedriver.exe" "https://storage.googleapis.com/chrome-for-testing-public/$($global:latest_chromedriver)/win32" "chromedriver-win32.zip" | |
| } | |
| function Ensure-Gecko-Driver() { | |
| Ensure-Driver-Updates "Gecko (Firefox)" "geckodriver.exe" "https://github.com/mozilla/geckodriver/releases/download/$($global:latest_geckodriver)" "geckodriver-$($global:latest_geckodriver)-win32.zip"; | |
| } | |
| function Ensure-Internet-Explorer-Driver() { | |
| if ($isWindows) { | |
| Ensure-Driver-Updates "Internet Explorer" "IEDriverServer.exe" "https://github.com/SeleniumHQ/selenium/releases/download/selenium-$($global:latest_iedriver)" "IEDriverServer_Win32_$($global:latest_iedriver).zip" | |
| } | |
| } | |
| function Ensure-MSEdge-Driver() { | |
| if ($isWindows) { | |
| Ensure-Driver-Updates "MSEdge" "msedgedriver.exe" "https://msedgedriver.azureedge.net/$($global:latest_msedge)" "edgedriver_win32.zip" | |
| } | |
| } | |
| function Ensure-Edge-Driver() { | |
| if ($isWindows) { | |
| Ensure-Driver-Updates "Edge" "MicrosoftWebDriver.exe" "https://download.microsoft.com/download/F/8/A/F8AF50AB-3C3A-4BC4-8773-DC27B32988DD/MicrosoftWebDriver.exe" | |
| } | |
| } | |
| function Flatten-DriversDirectory { | |
| if (-not (Test-Path $driversDirName)) { | |
| Write-Error "Path does not exist: $driversDirName" | |
| return | |
| } | |
| $rootPath = (Get-Item $driversDirName).FullName | |
| # Move only files not already in root, overwriting any duplicates | |
| Get-ChildItem -Path $driversDirName -Recurse -File | Where-Object { | |
| $_.Directory.FullName -ne $rootPath | |
| } | ForEach-Object { | |
| $destination = Join-Path -Path $rootPath -ChildPath $_.Name | |
| if (Test-Path $destination) { | |
| Remove-Item $destination -Force | |
| } | |
| Move-Item -Path $_.FullName -Destination $destination -Force | |
| } | |
| # Remove all empty directories | |
| Get-ChildItem -Path $driversDirName -Recurse -Directory | Sort-Object FullName -Descending | ForEach-Object { | |
| if (-not (Get-ChildItem -Path $_.FullName -Force)) { | |
| Remove-Item -Path $_.FullName -Force | |
| } | |
| } | |
| } | |
| function Replace-SeleniumAssembliesExe { | |
| $sourceDir = $driversDirName | |
| $seleniumModulesPath = 'C:\Program Files\WindowsPowerShell\Modules\Selenium' | |
| try { | |
| if (-not (Test-Path $sourceDir)) { | |
| throw "Source directory not found: $sourceDir" | |
| } | |
| if (-not (Test-Path $seleniumModulesPath)) { | |
| throw "Selenium modules path not found: $seleniumModulesPath" | |
| } | |
| $exeSources = Get-ChildItem -Path $sourceDir -Filter *.exe -File -ErrorAction Stop | |
| if ($exeSources.Count -eq 0) { | |
| Write-Host "No .exe files found in $sourceDir" -ForegroundColor Yellow | |
| return | |
| } | |
| $assembliesDirs = Get-ChildItem -Path "$seleniumModulesPath\*\assemblies" -Directory -ErrorAction Stop | |
| if ($assembliesDirs.Count -eq 0) { | |
| throw "No assemblies directories found under $seleniumModulesPath" | |
| } | |
| foreach ($dir in $assembliesDirs) { | |
| try { | |
| $existingExes = Get-ChildItem -Path $dir.FullName -Filter *.exe -File -ErrorAction Stop | |
| foreach ($exe in $existingExes) { | |
| try { | |
| Remove-Item $exe.FullName -Force -ErrorAction Stop | |
| Write-Host "Deleted: $($exe.FullName)" -ForegroundColor DarkGray | |
| } catch { | |
| Write-Host "ERROR:" -ForegroundColor Red -NoNewline | |
| Write-Host " Failed to delete $($exe.FullName): $_" | |
| } | |
| } | |
| } catch { | |
| Write-Host "ERROR:" -ForegroundColor Red -NoNewline | |
| Write-Host " Error while scanning $($dir.FullName) for existing .exe files: $_" | |
| continue | |
| } | |
| foreach ($exe in $exeSources) { | |
| $targetPath = Join-Path $dir.FullName $exe.Name | |
| try { | |
| Move-Item -Path $exe.FullName -Destination $targetPath -Force -ErrorAction Stop | |
| Write-Host "Moved: $($exe.Name) → $($dir.FullName)" -ForegroundColor Cyan | |
| } catch { | |
| Write-Host "ERROR:" -ForegroundColor Red -NoNewline | |
| Write-Host " Failed to move $($exe.Name) to $($dir.FullName): $_" | |
| } | |
| } | |
| } | |
| } catch { | |
| Write-Host "ERROR:" -ForegroundColor Red -NoNewline | |
| Write-Host " Fatal error in Replace-SeleniumAssembliesExe: $_" | |
| } | |
| } | |
| function Ensure-Admin { | |
| if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(` | |
| [Security.Principal.WindowsBuiltInRole] "Administrator")) { | |
| Write-Host "Elevation required. Relaunching as administrator..." -ForegroundColor Yellow | |
| $psi = New-Object System.Diagnostics.ProcessStartInfo | |
| $psi.FileName = "powershell" | |
| $psi.Arguments = "-ExecutionPolicy Bypass -File `"$PSCommandPath`"" | |
| $psi.Verb = "runas" | |
| try { | |
| [System.Diagnostics.Process]::Start($psi) | Out-Null | |
| } catch { | |
| Write-Host "User canceled the UAC prompt or access was denied." -ForegroundColor Red | |
| } | |
| exit | |
| } | |
| } | |
| New-Item -Force -ItemType directory -Path $driversDirName > $null; | |
| Ensure-Admin | |
| Get-ChromeInstalledVersion | |
| Get-ChromeDriverLatestVersion | |
| Ensure-Chrome-Driver; | |
| Get-IEDriverServerLatestVersion | |
| Ensure-Internet-Explorer-Driver; | |
| Get-GeckoDriverLatestVersion | |
| Ensure-Gecko-Driver; | |
| Get-MSEdgeDriverLatestVersion | |
| Ensure-MSEdge-Driver; | |
| Ensure-Edge-Driver; | |
| Flatten-DriversDirectory | |
| Replace-SeleniumAssembliesExe |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment