F8: distribution and GitHub-Releases self-update
- .github/workflows/release.yml: on tag v* build all 5 targets (macOS
arm64/x86_64, Linux arm64/x86_64 incl. free arm runners, Windows
x86_64) with the release-dist profile, archive kigi-<version>-<triple>
with LICENSE/NOTICE/THIRD-PARTY-NOTICES, generate SHA256SUMS, publish
the release (prerelease for tags containing '-'), with a tag↔workspace
version guard.
- install.sh / install.ps1 (repo root): platform detection, latest or
--version download from GitHub Releases, SHA-256 verification against
SHA256SUMS, install into the kigi home's downloads/ + bin/kigi symlink
(the same layout the self-updater manages), smoke test, PATH guidance.
- kigi-update rewritten onto the GitHub Releases API (documented wire
shape; stable=/latest, alpha=semver-max across the list, pinned=/tags):
SHA-256 gate before any binary swap, tar.gz/zip extraction per
platform, atomic bin/kigi symlink swap, channel/rollback semantics and
the KIGI_AUTO_UPDATE gate preserved verbatim; every x.ai/GCS/npm
endpoint deleted, npm/gh-release installers removed, legacy grok/agent
links retired on install. kigi-env owns the update base URL with a
KIGI_UPDATE_BASE_URL override (this is what the test artifact server
injects).
- .cargo/config.toml: removed the non-portable neoverse-v2 CPU pin on
Linux arm64 (fleet-specific); RELRO/NX hardening link-args now apply
to the gnu targets too, matching the release-dist profile's contract.
- THIRD-PARTY-NOTICES regenerated via cargo-about (about.toml +
template); the M0 hand-built file is dropped and README points at the
generated one. docs/RELEASE.md carries the release checklist.
- Deleted xAI-era leftovers: kigi-tui/scripts/install*.{sh,ps1} (x.ai
CDN) and the @xai-official/grok npm skeleton (PRD F8: no npm).
Gates: fmt clean; workspace check/clippy 0/0 (--locked, -D warnings);
kigi-update 58 lib + 86 integration tests green; deny ok;
release-dist build of kigi-bin succeeds and reports 'kigi 0.1.0'.
This commit is contained in:
+149
@@ -0,0 +1,149 @@
|
||||
# Kigi installer (Windows x86_64) — PRD F8.
|
||||
#
|
||||
# Downloads the x86_64-pc-windows-msvc artifact from this repo's GitHub
|
||||
# Releases, verifies its SHA-256 against the release's SHA256SUMS manifest,
|
||||
# and installs the binary as %USERPROFILE%\.kigi\bin\kigi.exe.
|
||||
#
|
||||
# Usage:
|
||||
# irm https://raw.githubusercontent.com/ZacharyZhang-NY/Kigi-CLI/main/install.ps1 | iex
|
||||
# powershell -ExecutionPolicy Bypass -File install.ps1 -Version v0.1.0
|
||||
#
|
||||
# Environment:
|
||||
# KIGI_SHARE_DIR install root (default: %USERPROFILE%\.kigi)
|
||||
# KIGI_UPDATE_BASE_URL GitHub-Releases-shaped API base (default:
|
||||
# https://api.github.com/repos/ZacharyZhang-NY/Kigi-CLI/releases)
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Version = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
function Fail([string]$Message) {
|
||||
Write-Error "install.ps1: error: $Message"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$Repo = "ZacharyZhang-NY/Kigi-CLI"
|
||||
$ApiBase = if ($env:KIGI_UPDATE_BASE_URL) { $env:KIGI_UPDATE_BASE_URL } else { "https://api.github.com/repos/$Repo/releases" }
|
||||
$KigiHome = if ($env:KIGI_SHARE_DIR) { $env:KIGI_SHARE_DIR } else { Join-Path $env:USERPROFILE ".kigi" }
|
||||
$Triple = "x86_64-pc-windows-msvc"
|
||||
|
||||
# ── Platform gate ────────────────────────────────────────────────────────────
|
||||
if (-not [System.Environment]::Is64BitOperatingSystem) {
|
||||
Fail "kigi requires 64-bit Windows (x86_64)"
|
||||
}
|
||||
$arch = $env:PROCESSOR_ARCHITECTURE
|
||||
if ($arch -ne "AMD64") {
|
||||
Fail "unsupported architecture '$arch' (only x86_64/AMD64 Windows builds are published)"
|
||||
}
|
||||
|
||||
# ── Version argument ─────────────────────────────────────────────────────────
|
||||
$Version = $Version.TrimStart("v")
|
||||
if ($Version -and $Version -notmatch '^\d+\.\d+\.\d+([-.][0-9A-Za-z.-]+)?$') {
|
||||
Fail "invalid version '$Version' (expected X.Y.Z or vX.Y.Z)"
|
||||
}
|
||||
|
||||
# TLS 1.2 for older PowerShell 5.1 defaults.
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
$Headers = @{ "User-Agent" = "kigi-install"; "Accept" = "application/vnd.github+json" }
|
||||
|
||||
# ── Resolve the release ──────────────────────────────────────────────────────
|
||||
$ReleaseUrl = if ($Version) { "$ApiBase/tags/v$Version" } else { "$ApiBase/latest" }
|
||||
Write-Host "Resolving release from $ReleaseUrl"
|
||||
try {
|
||||
$Release = Invoke-RestMethod -Uri $ReleaseUrl -Headers $Headers
|
||||
} catch {
|
||||
Fail "could not fetch release metadata from ${ReleaseUrl}: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
$Tag = [string]$Release.tag_name
|
||||
if (-not $Tag) { Fail "release metadata has no tag_name (endpoint: $ReleaseUrl)" }
|
||||
$ResolvedVersion = $Tag.TrimStart("v")
|
||||
if ($Version -and $ResolvedVersion -ne $Version) {
|
||||
Fail "requested version $Version but release tag is $Tag"
|
||||
}
|
||||
|
||||
$Asset = "kigi-$ResolvedVersion-$Triple.zip"
|
||||
$ArchiveAsset = $Release.assets | Where-Object { $_.name -eq $Asset } | Select-Object -First 1
|
||||
$SumsAsset = $Release.assets | Where-Object { $_.name -eq "SHA256SUMS" } | Select-Object -First 1
|
||||
if (-not $ArchiveAsset) { Fail "release $Tag has no asset $Asset" }
|
||||
if (-not $SumsAsset) { Fail "release $Tag has no SHA256SUMS asset; refusing to install unverified binaries" }
|
||||
|
||||
# ── Download + verify ────────────────────────────────────────────────────────
|
||||
$TmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ("kigi-install-" + [System.IO.Path]::GetRandomFileName())
|
||||
New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null
|
||||
try {
|
||||
$ArchivePath = Join-Path $TmpDir $Asset
|
||||
$SumsPath = Join-Path $TmpDir "SHA256SUMS"
|
||||
|
||||
Write-Host "Downloading kigi v$ResolvedVersion ($Triple)..."
|
||||
Invoke-WebRequest -Uri $ArchiveAsset.browser_download_url -Headers $Headers -OutFile $ArchivePath
|
||||
Invoke-WebRequest -Uri $SumsAsset.browser_download_url -Headers $Headers -OutFile $SumsPath
|
||||
|
||||
$Expected = $null
|
||||
foreach ($line in Get-Content $SumsPath) {
|
||||
$parts = $line.Trim() -split '\s+', 2
|
||||
if ($parts.Count -eq 2 -and $parts[1].TrimStart('*') -eq $Asset) {
|
||||
$Expected = $parts[0].ToLowerInvariant()
|
||||
}
|
||||
}
|
||||
if (-not $Expected) { Fail "SHA256SUMS has no entry for $Asset" }
|
||||
|
||||
$Actual = (Get-FileHash -Algorithm SHA256 -Path $ArchivePath).Hash.ToLowerInvariant()
|
||||
if ($Actual -ne $Expected) {
|
||||
Fail "SHA256 mismatch for ${Asset}: expected $Expected, got $Actual"
|
||||
}
|
||||
Write-Host "Checksum verified."
|
||||
|
||||
# ── Extract + install ────────────────────────────────────────────────────
|
||||
$ExtractDir = Join-Path $TmpDir "extracted"
|
||||
Expand-Archive -Path $ArchivePath -DestinationPath $ExtractDir -Force
|
||||
$Binary = Get-ChildItem -Path $ExtractDir -Recurse -Filter "kigi.exe" | Select-Object -First 1
|
||||
if (-not $Binary) { Fail "archive $Asset does not contain kigi.exe" }
|
||||
|
||||
$BinDir = Join-Path $KigiHome "bin"
|
||||
New-Item -ItemType Directory -Path $BinDir -Force | Out-Null
|
||||
$Dest = Join-Path $BinDir "kigi.exe"
|
||||
|
||||
# A running kigi.exe blocks writes but allows renames — move it aside
|
||||
# first (mirrors the self-updater's windows_replace_exe strategy).
|
||||
if (Test-Path $Dest) {
|
||||
$Aside = "$Dest.old"
|
||||
Remove-Item -Path $Aside -Force -ErrorAction SilentlyContinue
|
||||
try {
|
||||
Move-Item -Path $Dest -Destination $Aside -Force
|
||||
} catch {
|
||||
Fail "cannot replace $Dest (close all running kigi sessions and retry): $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
Move-Item -Path $Binary.FullName -Destination $Dest -Force
|
||||
|
||||
# Smoke-test the installed binary.
|
||||
& $Dest --version *> $null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Fail "installed binary failed to run (exit $LASTEXITCODE)"
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "kigi v$ResolvedVersion installed to $Dest"
|
||||
|
||||
$UserPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
||||
$OnPath = ($UserPath -split ";" | Where-Object { $_ -eq $BinDir }).Count -gt 0 -or
|
||||
($env:Path -split ";" | Where-Object { $_ -eq $BinDir }).Count -gt 0
|
||||
if (-not $OnPath) {
|
||||
Write-Host ""
|
||||
Write-Host "$BinDir is not on your PATH. Add it for the current user with:"
|
||||
Write-Host ""
|
||||
Write-Host " [Environment]::SetEnvironmentVariable('Path', `"$BinDir;`" + [Environment]::GetEnvironmentVariable('Path', 'User'), 'User')"
|
||||
Write-Host ""
|
||||
Write-Host "Then open a new terminal and run 'kigi' to get started."
|
||||
} else {
|
||||
Write-Host "Run 'kigi' to get started."
|
||||
}
|
||||
} finally {
|
||||
Remove-Item -Path $TmpDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Reference in New Issue
Block a user