install-cli.ps1
Rama main · réplica de lectura del repositorio original.
<#
.SYNOPSIS
Installs only the cross-platform firecrab client for the current Windows user.
#>
[CmdletBinding()]
param(
[string]$Version = $(if ($env:FIRECRAB_VERSION) { $env:FIRECRAB_VERSION } else { "latest" }),
[string]$InstallDir = $(
if ($env:FIRECRAB_INSTALL_DIR) {
$env:FIRECRAB_INSTALL_DIR
} elseif ($env:LOCALAPPDATA) {
Join-Path $env:LOCALAPPDATA "Programs\firecrab\bin"
} else {
Join-Path $HOME "firecrab\bin"
}
),
[switch]$Check,
[switch]$PrintAsset,
[switch]$PrintUrl
)
$ErrorActionPreference = "Stop"
$releaseBase = if ($env:FIRECRAB_RELEASE_BASE) {
$env:FIRECRAB_RELEASE_BASE.TrimEnd("/")
} else {
"https://github.com/SteelCrab/firecrab/releases"
}
$releaseUri = $null
if (-not [System.Uri]::TryCreate($releaseBase, [System.UriKind]::Absolute, [ref]$releaseUri)) {
throw "FIRECRAB_RELEASE_BASE must be an absolute HTTPS URL"
}
if ($releaseUri.Scheme -eq "file") {
if ($env:FIRECRAB_TEST_ALLOW_FILE_URL -ne "1") {
throw "file:// release roots require FIRECRAB_TEST_ALLOW_FILE_URL=1"
}
} elseif ($releaseUri.Scheme -ne "https") {
throw "FIRECRAB_RELEASE_BASE must use HTTPS"
}
$rawArchitecture = if ($env:FIRECRAB_CLI_ARCH) {
$env:FIRECRAB_CLI_ARCH
} else {
[System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
}
$architecture = switch -Regex ($rawArchitecture) {
"^(X64|x86_64|amd64)$" { "x86_64"; break }
"^(Arm64|aarch64)$" { "aarch64"; break }
default { throw "unsupported Windows client architecture: $rawArchitecture" }
}
$asset = "firecrab-cli-$architecture-windows.zip"
$normalizedVersion = if ([string]::IsNullOrWhiteSpace($Version) -or $Version -eq "latest") {
"latest"
} elseif ($Version.StartsWith("v")) {
$Version
} else {
"v$Version"
}
$downloadRoot = if ($normalizedVersion -eq "latest") {
"$releaseBase/latest/download"
} else {
"$releaseBase/download/$normalizedVersion"
}
$assetUrl = "$downloadRoot/$asset"
if ($PrintAsset) {
Write-Output $asset
return
}
if ($PrintUrl) {
Write-Output $assetUrl
return
}
if ($Check) {
Write-Output "would install $assetUrl to $(Join-Path $InstallDir 'firecrab.exe')"
return
}
function Install-ClientBinary {
param(
[Parameter(Mandatory = $true)]
[string]$Source,
[Parameter(Mandatory = $true)]
[string]$DestinationDirectory
)
New-Item -ItemType Directory -Force -Path $DestinationDirectory | Out-Null
$DestinationDirectory = (Resolve-Path -LiteralPath $DestinationDirectory).ProviderPath
$destination = Join-Path $DestinationDirectory "firecrab.exe"
$temporaryOutput = Join-Path $DestinationDirectory (
".firecrab-install-" + [guid]::NewGuid().ToString("N")
)
try {
$sourceStream = [System.IO.File]::Open(
$Source,
[System.IO.FileMode]::Open,
[System.IO.FileAccess]::Read,
[System.IO.FileShare]::Read
)
try {
$outputStream = [System.IO.File]::Open(
$temporaryOutput,
[System.IO.FileMode]::CreateNew,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::None
)
try {
$sourceStream.CopyTo($outputStream)
} finally {
$outputStream.Dispose()
}
} finally {
$sourceStream.Dispose()
}
$existing = Get-Item -LiteralPath $destination -Force -ErrorAction SilentlyContinue
if ($null -ne $existing -and $existing.PSIsContainer) {
throw "install destination $destination is a directory"
}
if ($null -ne $existing -and
(($existing.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0)) {
Remove-Item -LiteralPath $destination -Force
$existing = $null
}
if ($null -ne $existing) {
[System.IO.File]::Replace($temporaryOutput, $destination, [System.Management.Automation.Language.NullString]::Value)
} else {
[System.IO.File]::Move($temporaryOutput, $destination)
}
} finally {
if (Test-Path -LiteralPath $temporaryOutput) {
Remove-Item -LiteralPath $temporaryOutput -Force -ErrorAction SilentlyContinue
}
}
}
$temporary = Join-Path ([System.IO.Path]::GetTempPath()) ("firecrab-cli-" + [guid]::NewGuid())
New-Item -ItemType Directory -Path $temporary | Out-Null
try {
$archive = Join-Path $temporary $asset
$sums = Join-Path $temporary "SHA256SUMS"
if ($releaseUri.Scheme -eq "file") {
Copy-Item -LiteralPath ([System.Uri]$assetUrl).LocalPath -Destination $archive
Copy-Item -LiteralPath ([System.Uri]"$downloadRoot/SHA256SUMS").LocalPath -Destination $sums
} else {
Invoke-WebRequest -UseBasicParsing -Uri $assetUrl -OutFile $archive
Invoke-WebRequest -UseBasicParsing -Uri "$downloadRoot/SHA256SUMS" -OutFile $sums
}
$checksumLine = Get-Content $sums | Where-Object {
$fields = $_ -split "\s+"
if ($fields.Count -lt 2) { return $false }
$listed = $fields[-1].TrimStart("*").Replace("\", "/").Split("/")[-1]
$listed -eq $asset
} | Select-Object -First 1
if (-not $checksumLine) {
throw "checksum missing for $asset"
}
$expected = ($checksumLine -split "\s+")[0].ToLowerInvariant()
$actual = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant()
if ($actual -ne $expected) {
throw "checksum mismatch for $asset"
}
$unpacked = Join-Path $temporary "unpacked"
Expand-Archive -Path $archive -DestinationPath $unpacked
$source = Join-Path $unpacked "firecrab.exe"
if (-not (Test-Path -LiteralPath $source -PathType Leaf)) {
throw "release archive does not contain firecrab.exe"
}
Install-ClientBinary -Source $source -DestinationDirectory $InstallDir
$InstallDir = (Resolve-Path -LiteralPath $InstallDir).ProviderPath
} finally {
Remove-Item -LiteralPath $temporary -Recurse -Force -ErrorAction SilentlyContinue
}
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
$pathEntries = @($userPath -split ";" | Where-Object { $_ })
if ($pathEntries -notcontains $InstallDir) {
$updatedPath = (@($pathEntries) + $InstallDir) -join ";"
[Environment]::SetEnvironmentVariable("Path", $updatedPath, "User")
$env:Path = "$InstallDir;$env:Path"
Write-Output "added $InstallDir to the user PATH; open a new terminal to use it"
}
Write-Output "installed firecrab to $(Join-Path $InstallDir 'firecrab.exe')"