install.ps1 RawHistoryFolder
1# zot installer for Windows (PowerShell).
2#
3# Usage (in PowerShell):
4# iwr -useb https://raw.githubusercontent.com/patriceckhart/zot/main/install.ps1 | iex
5#
6# Or with arguments:
7# $env:ZOT_VERSION = "v0.0.1"
8# $env:ZOT_PREFIX = "$HOME\bin"
9# iwr -useb https://raw.githubusercontent.com/patriceckhart/zot/main/install.ps1 | iex
10#
11# Detects architecture, downloads the matching .zip from the GitHub
12# release, verifies the sha256 against checksums.txt, extracts zot.exe,
13# and moves it into $ZOT_PREFIX (defaults to $HOME\bin, added to PATH
14# via the User environment if missing).
15#
16# While the repo is private, set $env:GITHUB_TOKEN to a PAT with
17# `contents:read` scope; the script uses it for every download. Once
18# the repo goes public, the token becomes optional.
19
20
21[CmdletBinding()]
22param(
23 [string]$Version = $env:ZOT_VERSION,
24 [string]$Prefix = $env:ZOT_PREFIX
25)
26
27$ErrorActionPreference = "Stop"
28
29$owner = "patriceckhart"
30$repo = "zot"
31$binary = "zot"
32
33# Build Authorization header list once; used on every HTTP call so the
34# script works against private repos when $env:GITHUB_TOKEN is set.
35$headers = @{}
36if ($env:GITHUB_TOKEN) { $headers["Authorization"] = "Bearer $($env:GITHUB_TOKEN)" }
37
38if (-not $Version) { $Version = "latest" }
39if (-not $Prefix) { $Prefix = Join-Path $HOME "bin" }
40
41function Msg($m) { Write-Host "==> $m" -ForegroundColor Cyan }
42function Warn($m) { Write-Warning $m }
43function Die($m) { Write-Error $m; exit 1 }
44
45# ---- detect architecture ----
46
47switch -wildcard ($env:PROCESSOR_ARCHITECTURE) {
48 "AMD64" { $arch = "amd64" }
49 "ARM64" { $arch = "arm64" }
50 default { Die "unsupported arch: $($env:PROCESSOR_ARCHITECTURE)" }
51}
52
53# ARM64 Windows isn't shipped (see .goreleaser.yaml ignore rule) — fall
54# back to amd64 which runs fine under ARM64 emulation.
55if ($arch -eq "arm64") {
56 Warn "windows/arm64 is not published; falling back to amd64"
57 $arch = "amd64"
58}
59
60# ---- resolve version ----
61
62if ($Version -eq "latest") {
63 if ($headers.ContainsKey("Authorization")) {
64 $api = Invoke-RestMethod -Headers $headers "https://api.github.com/repos/$owner/$repo/releases/latest"
65 $Version = $api.tag_name
66 } else {
67 $resp = Invoke-WebRequest -UseBasicParsing -MaximumRedirection 5 `
68 "https://github.com/$owner/$repo/releases/latest"
69 if ($resp.BaseResponse.ResponseUri -match '/tag/([^/]+)') {
70 $Version = $Matches[1]
71 } else {
72 Die "could not resolve latest version (set `$env:GITHUB_TOKEN if the repo is private)"
73 }
74 }
75}
76
77if (-not $Version.StartsWith("v")) { $Version = "v$Version" }
78$verNum = $Version.TrimStart("v")
79
80# ---- download + verify + extract ----
81
82$archive = "${binary}_${verNum}_windows_${arch}.zip"
83$baseUrl = "https://github.com/$owner/$repo/releases/download/$Version"
84$archiveUrl = "$baseUrl/$archive"
85$checksumUrl = "$baseUrl/checksums.txt"
86
87$tmp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("zot-install-" + [System.Guid]::NewGuid().ToString("N").Substring(0,8)))
88
89try {
90 Msg "downloading $archive"
91 Invoke-WebRequest -UseBasicParsing -Headers $headers -Uri $archiveUrl -OutFile (Join-Path $tmp $archive)
92
93 Msg "verifying checksum"
94 $checksums = Invoke-WebRequest -UseBasicParsing -Headers $headers -Uri $checksumUrl | Select-Object -ExpandProperty Content
95 $expected = ($checksums -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ -match ("\s" + [regex]::Escape($archive) + "$") } | Select-Object -First 1)
96 if (-not $expected) { Die "no checksum for $archive in checksums.txt" }
97 $expectedHash = ($expected -split "\s+")[0]
98
99 $actualHash = (Get-FileHash -Path (Join-Path $tmp $archive) -Algorithm SHA256).Hash.ToLower()
100 if ($expectedHash.ToLower() -ne $actualHash) {
101 Die "checksum mismatch: expected $expectedHash, got $actualHash"
102 }
103
104 Msg "extracting"
105 Expand-Archive -Path (Join-Path $tmp $archive) -DestinationPath $tmp -Force
106
107 $exe = Join-Path $tmp "$binary.exe"
108 if (-not (Test-Path $exe)) { Die "archive did not contain $binary.exe" }
109
110 Msg "installing to $Prefix\$binary.exe"
111 New-Item -ItemType Directory -Path $Prefix -Force | Out-Null
112 Copy-Item $exe (Join-Path $Prefix "$binary.exe") -Force
113
114 # ---- PATH hint ----
115
116 $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
117 $parts = $userPath -split ";" | Where-Object { $_ }
118 if (-not ($parts -contains $Prefix)) {
119 Warn "$Prefix is not on your user PATH"
120 Warn "adding it for future sessions..."
121 [Environment]::SetEnvironmentVariable("Path", ($userPath.TrimEnd(";") + ";" + $Prefix), "User")
122 Warn "open a new terminal to pick up the change, or run:"
123 Warn " `$env:Path = `"$Prefix;`$env:Path`""
124 }
125
126 Msg "installed. run: zot --help"
127}
128finally {
129 Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue
130}