# Install an official DataTug CLI release without a language toolchain. # # irm https://datatug.app/get-cli.ps1 | iex # # DATATUG_INSTALL_DIR may override the default per-user destination (an absolute path). # DATATUG_VERSION may pin a release tag such as v0.32.0. # # Everything lives inside one function that is called on the last line, so # - a download that is cut short never runs a partial script, and # - `$ErrorActionPreference`, `$ProgressPreference`, the helper functions and every variable stay out of the session # that ran `irm ... | iex` (code that goes through iex runs in the caller's scope). function Install-DataTugCli { $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" function Fail([string]$Message) { throw "datatug install: $Message" } $Repo = "https://github.com/datatug/datatug" $WorkDir = $null $DestinationTemp = $null $SavedProtocol = [Net.ServicePointManager]::SecurityProtocol try { # Windows PowerShell 5.1 does not negotiate TLS 1.2 on its own on older systems; add it (never removed for the session # by this script: the previous setting is restored at the end). [Net.ServicePointManager]::SecurityProtocol = $SavedProtocol -bor [Net.SecurityProtocolType]::Tls12 $Architecture = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } if ($Architecture -ieq "ARM64") { Fail "Windows on ARM64 is not supported yet: the current release is built for amd64 only. Run this in an x64 PowerShell." } if ($Architecture -notin @("AMD64", "x86_64")) { Fail "unsupported Windows architecture: $Architecture (the current release supports amd64)" } $InstallDir = $env:DATATUG_INSTALL_DIR if ($InstallDir) { if ($InstallDir -notmatch '^([A-Za-z]:[\\/]|\\\\)') { Fail "DATATUG_INSTALL_DIR must be an absolute path, not `"$InstallDir`"" } } else { $InstallDir = Join-Path ([Environment]::GetFolderPath([Environment+SpecialFolder]::LocalApplicationData)) "DataTug\bin" } $Version = $env:DATATUG_VERSION if (-not $Version) { # The /releases/latest page redirects to /releases/tag/: no API call, so no API rate limit. try { $Request = [Net.HttpWebRequest]::Create("$Repo/releases/latest") $Request.Method = "HEAD" $Response = $Request.GetResponse() try { $FinalUrl = $Response.ResponseUri.AbsoluteUri } finally { $Response.Close() } $Version = $FinalUrl.Substring($FinalUrl.LastIndexOf("/") + 1) } catch { Fail "could not resolve the latest release: $($_.Exception.Message)" } } # A strict semantic-version tag, case-sensitive and anchored at both ends: nothing else can reach a URL. if ($Version -cnotmatch '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?\z') { Fail "release version must be a tag such as v0.32.0" } $AssetVersion = $Version.Substring(1) $Archive = "datatug_${AssetVersion}_windows_amd64.zip" $Checksums = "datatug_${AssetVersion}_checksums.txt" $ReleaseUrl = "$Repo/releases/download/$Version" $WorkDir = Join-Path ([IO.Path]::GetTempPath()) ("datatug-install." + [Guid]::NewGuid().ToString("N")) New-Item -ItemType Directory -Path $WorkDir | Out-Null $ChecksumsPath = Join-Path $WorkDir $Checksums $ArchivePath = Join-Path $WorkDir $Archive try { Invoke-WebRequest -UseBasicParsing -Uri "$ReleaseUrl/$Checksums" -OutFile $ChecksumsPath Invoke-WebRequest -UseBasicParsing -Uri "$ReleaseUrl/$Archive" -OutFile $ArchivePath } catch { Fail "could not download $Archive and its checksums: $($_.Exception.Message)" } $EscapedArchive = [Regex]::Escape($Archive) $ChecksumLine = Get-Content $ChecksumsPath | Where-Object { $_ -match "^([0-9a-fA-F]{64})\s+\*?$EscapedArchive$" } | Select-Object -First 1 if (-not $ChecksumLine) { Fail "$Archive is missing from $Checksums" } $Expected = ([Regex]::Match($ChecksumLine, '^([0-9a-fA-F]{64})')).Groups[1].Value.ToLowerInvariant() $Actual = (Get-FileHash -Algorithm SHA256 -Path $ArchivePath).Hash.ToLowerInvariant() if ($Actual -ne $Expected) { Fail "SHA-256 checksum verification failed" } $Expanded = Join-Path $WorkDir "expanded" Expand-Archive -Path $ArchivePath -DestinationPath $Expanded $Executable = Join-Path $Expanded "datatug.exe" if (-not (Test-Path -LiteralPath $Executable -PathType Leaf)) { Fail "release archive does not contain datatug.exe" } New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null $Destination = Join-Path $InstallDir "datatug.exe" $DestinationTemp = Join-Path $InstallDir (".datatug.install." + [Guid]::NewGuid().ToString("N") + ".exe") Copy-Item -LiteralPath $Executable -Destination $DestinationTemp Move-Item -LiteralPath $DestinationTemp -Destination $Destination -Force $DestinationTemp = $null Write-Host "Installed datatug $Version at $Destination" $PathEntries = $env:PATH -split ';' if ($InstallDir -notin $PathEntries) { Write-Host "Add DataTug to PATH for this PowerShell session:" Write-Host "`$env:PATH = `"$InstallDir;`$env:PATH`"" } } finally { [Net.ServicePointManager]::SecurityProtocol = $SavedProtocol if ($DestinationTemp -and (Test-Path -LiteralPath $DestinationTemp)) { Remove-Item -LiteralPath $DestinationTemp -Force } if ($WorkDir -and (Test-Path -LiteralPath $WorkDir)) { Remove-Item -LiteralPath $WorkDir -Recurse -Force } } } Install-DataTugCli