Files
librarian/scripts/install-ripgrep.ps1

200 lines
7.3 KiB
PowerShell

<#
.SYNOPSIS
Ensures ripgrep (rg.exe) is installed, for Librarian's recursive search.
.DESCRIPTION
Librarian uses ripgrep as its search engine. This script provisions it on
machines that don't already have it. It is a standalone helper intended to be
bundled and run by the SEPARATE Librarian installer project as a pre-install
step; it is NOT part of the application build and the app does not invoke it.
Behaviour:
1. If rg.exe is already discoverable (on PATH, or in the install dir, or in
a known winget/scoop/choco location), it does nothing unless -Force.
2. Otherwise it resolves the latest ripgrep release from GitHub (falling
back to a pinned version if the GitHub API is unreachable), downloads the
x86_64-pc-windows-msvc archive, extracts rg.exe into -InstallDir, and
(unless -NoPath) adds that directory to the current user's PATH.
The default install directory matches the first location Librarian probes at
runtime (%LOCALAPPDATA%\Programs\ripgrep), so search works immediately.
.PARAMETER InstallDir
Directory to install rg.exe into. Default: %LOCALAPPDATA%\Programs\ripgrep.
.PARAMETER Force
Reinstall even if ripgrep is already present.
.PARAMETER NoPath
Don't modify the user PATH (e.g. when the installer adds it itself, or when
placing rg.exe next to the Librarian executable instead).
.PARAMETER Version
Install a specific ripgrep version (e.g. "15.1.0") instead of the latest.
.EXAMPLE
powershell -ExecutionPolicy Bypass -File .\install-ripgrep.ps1
.NOTES
Requires Windows PowerShell 5.1+ or PowerShell 7+. No admin rights needed for
the default per-user install location.
#>
[CmdletBinding()]
param(
[string]$InstallDir = (Join-Path $env:LOCALAPPDATA 'Programs\ripgrep'),
[switch]$Force,
[switch]$NoPath,
[string]$Version
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# ripgrep target triple this script provisions, and a pinned fallback used only
# if the GitHub "latest release" lookup fails (offline, rate-limited, etc.).
$Triple = 'x86_64-pc-windows-msvc'
$FallbackVersion = '15.1.0'
$Repo = 'BurntSushi/ripgrep'
function Write-Step([string]$Message) { Write-Host "==> $Message" -ForegroundColor Cyan }
function Write-Ok([string]$Message) { Write-Host " $Message" -ForegroundColor Green }
function Write-Warn2([string]$Message) { Write-Host " $Message" -ForegroundColor Yellow }
# Return the path to an existing rg.exe if one is discoverable, else $null. Mirrors
# the locations Librarian itself probes at runtime.
function Find-Ripgrep {
param([string]$InstallDir)
$onPath = Get-Command rg.exe -ErrorAction SilentlyContinue
if ($onPath) { return $onPath.Source }
$candidates = @(
(Join-Path $InstallDir 'rg.exe'),
(Join-Path $env:LOCALAPPDATA 'Microsoft\WinGet\Links\rg.exe'),
(Join-Path $env:USERPROFILE 'scoop\shims\rg.exe'),
'C:\ProgramData\chocolatey\bin\rg.exe'
)
foreach ($c in $candidates) {
if ($c -and (Test-Path -LiteralPath $c)) { return $c }
}
return $null
}
# Resolve the download URL + version for the requested (or latest) release.
function Resolve-Release {
param([string]$Version)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
if ($Version) {
$tag = $Version
}
else {
try {
Write-Step "Querying GitHub for the latest ripgrep release..."
$headers = @{ 'User-Agent' = 'librarian-install-ripgrep' }
$rel = Invoke-RestMethod -Headers $headers -Uri "https://api.github.com/repos/$Repo/releases/latest"
$tag = $rel.tag_name
# Prefer the asset URL straight from the release metadata when present.
$asset = $rel.assets | Where-Object { $_.name -like "*$Triple.zip" } | Select-Object -First 1
if ($asset) {
return [pscustomobject]@{ Version = $tag; Url = $asset.browser_download_url }
}
}
catch {
Write-Warn2 "GitHub lookup failed ($($_.Exception.Message)); using pinned v$FallbackVersion."
$tag = $FallbackVersion
}
}
$name = "ripgrep-$tag-$Triple.zip"
$url = "https://github.com/$Repo/releases/download/$tag/$name"
return [pscustomobject]@{ Version = $tag; Url = $url }
}
# Append $Dir to the current user's PATH (persisted + this session), if missing.
function Add-ToUserPath {
param([string]$Dir)
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
$parts = @()
if ($userPath) { $parts = $userPath -split ';' | Where-Object { $_ -ne '' } }
if ($parts -contains $Dir) {
Write-Ok "Already on the user PATH."
return
}
$newPath = (($parts + $Dir) -join ';')
[Environment]::SetEnvironmentVariable('Path', $newPath, 'User')
# Reflect it in the current session too, so a follow-on step sees rg.
$env:Path = "$env:Path;$Dir"
Write-Ok "Added to the user PATH (open a new terminal to pick it up globally)."
}
# --- main ---------------------------------------------------------------------
$existing = Find-Ripgrep -InstallDir $InstallDir
if ($existing -and -not $Force) {
$rgVersion = (& $existing --version | Select-Object -First 1)
Write-Step "ripgrep is already installed: $existing"
Write-Ok $rgVersion
Write-Ok "Nothing to do. Re-run with -Force to reinstall."
return
}
$release = Resolve-Release -Version $Version
Write-Step "Installing ripgrep $($release.Version) ($Triple)"
$tempDir = Join-Path ([IO.Path]::GetTempPath()) ("librarian-rg-" + [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
$zipPath = Join-Path $tempDir 'ripgrep.zip'
try {
Write-Step "Downloading $($release.Url)"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Progress rendering makes Invoke-WebRequest dramatically slower; suppress it.
$oldProgress = $ProgressPreference
$ProgressPreference = 'SilentlyContinue'
try {
Invoke-WebRequest -Uri $release.Url -OutFile $zipPath -UseBasicParsing
}
finally {
$ProgressPreference = $oldProgress
}
Write-Step "Extracting..."
Expand-Archive -Path $zipPath -DestinationPath $tempDir -Force
$rg = Get-ChildItem -Path $tempDir -Recurse -Filter 'rg.exe' | Select-Object -First 1
if (-not $rg) {
throw "rg.exe not found in the downloaded archive."
}
if (-not (Test-Path -LiteralPath $InstallDir)) {
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
}
$destExe = Join-Path $InstallDir 'rg.exe'
Copy-Item -LiteralPath $rg.FullName -Destination $destExe -Force
# Bring along the license/readme that ship alongside, where present.
foreach ($extra in @('LICENSE-MIT', 'UNLICENSE', 'COPYING', 'README.md')) {
$src = Get-ChildItem -Path $tempDir -Recurse -Filter $extra -ErrorAction SilentlyContinue | Select-Object -First 1
if ($src) { Copy-Item -LiteralPath $src.FullName -Destination $InstallDir -Force }
}
Write-Ok "Installed: $destExe"
$rgVersion = (& $destExe --version | Select-Object -First 1)
Write-Ok $rgVersion
if (-not $NoPath) {
Add-ToUserPath -Dir $InstallDir
}
Write-Step "ripgrep is ready for Librarian search."
}
finally {
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}