feat(wrappers): canonical PowerShell bootstrap wrapper
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env pwsh
|
||||
# crucible - bootstrap wrapper (PowerShell). Prefers a crucible already on PATH;
|
||||
# otherwise downloads the latest released binary from Gitea into a per-version
|
||||
# cache and execs it.
|
||||
#
|
||||
# CANONICAL SOURCE: sow-tools/wrappers/crucible.ps1. Do not hand-edit copies in
|
||||
# consumer repos - kept in sync by sow-tools CI (see drift-check).
|
||||
#
|
||||
# Env: CRUCIBLE_HOME, CRUCIBLE_TOKEN, CRUCIBLE_GITEA, CRUCIBLE_REPO.
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# 0. Prefer an existing crucible.
|
||||
$existing = Get-Command crucible -ErrorAction SilentlyContinue
|
||||
if ($existing) { & $existing.Source @args; exit $LASTEXITCODE }
|
||||
|
||||
$gitea = if ($env:CRUCIBLE_GITEA) { $env:CRUCIBLE_GITEA } else { 'https://git.westgate.pw' }
|
||||
$repo = if ($env:CRUCIBLE_REPO) { $env:CRUCIBLE_REPO } else { 'ShadowsOverWestgate/sow-tools' }
|
||||
|
||||
# 1. OS/arch -> asset name (.exe on Windows).
|
||||
$os = if ($IsWindows) { 'windows' } elseif ($IsMacOS) { 'darwin' } else { 'linux' }
|
||||
$arch = switch ($env:PROCESSOR_ARCHITECTURE) {
|
||||
'AMD64' { 'amd64' } 'ARM64' { 'arm64' } default { 'amd64' }
|
||||
}
|
||||
$ext = if ($os -eq 'windows') { '.exe' } else { '' }
|
||||
$asset = "crucible-$os-$arch$ext"
|
||||
|
||||
# 2. Auth header (anon first, token fallback).
|
||||
$token = $env:CRUCIBLE_TOKEN
|
||||
$tokenFile = Join-Path $HOME '.config/crucible/token'
|
||||
if (-not $token -and (Test-Path $tokenFile)) { $token = (Get-Content $tokenFile -Raw).Trim() }
|
||||
function Invoke-Fetch($url, $outFile) {
|
||||
try { Invoke-WebRequest -Uri $url -OutFile $outFile -UseBasicParsing; return $true } catch {}
|
||||
if ($token) {
|
||||
Invoke-WebRequest -Uri $url -OutFile $outFile -UseBasicParsing -Headers @{ Authorization = "token $token" }
|
||||
return $true
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
# 3. Resolve latest tag.
|
||||
$tag = $null
|
||||
try {
|
||||
$hdr = if ($token) { @{ Authorization = "token $token" } } else { @{} }
|
||||
$rel = Invoke-RestMethod -Uri "$gitea/api/v1/repos/$repo/releases/latest" -Headers $hdr
|
||||
$tag = $rel.tag_name
|
||||
} catch {}
|
||||
|
||||
# 4. Cache dir.
|
||||
$cacheRoot = if ($env:CRUCIBLE_HOME) { $env:CRUCIBLE_HOME }
|
||||
elseif ($env:LOCALAPPDATA) { Join-Path $env:LOCALAPPDATA 'crucible' }
|
||||
else { Join-Path $HOME '.cache/crucible' }
|
||||
|
||||
# 5. Offline fallback.
|
||||
if (-not $tag) {
|
||||
if (Test-Path $cacheRoot) {
|
||||
$cached = Get-ChildItem $cacheRoot -Directory | Sort-Object Name | Select-Object -Last 1
|
||||
$cachedBin = if ($cached) { Join-Path $cached.FullName "crucible$ext" } else { $null }
|
||||
if ($cachedBin -and (Test-Path $cachedBin)) {
|
||||
Write-Error "crucible: offline - using cached $($cached.Name)" -ErrorAction Continue
|
||||
& $cachedBin @args; exit $LASTEXITCODE
|
||||
}
|
||||
}
|
||||
Write-Error "crucible: cannot reach $gitea and no cached binary found (set CRUCIBLE_TOKEN if private)."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$destDir = Join-Path $cacheRoot $tag
|
||||
$bin = Join-Path $destDir "crucible$ext"
|
||||
|
||||
# 6. Download + verify on cache miss.
|
||||
if (-not (Test-Path $bin)) {
|
||||
New-Item -ItemType Directory -Force -Path $destDir | Out-Null
|
||||
$base = "$gitea/$repo/releases/download/$tag"
|
||||
$tmp = New-Item -ItemType Directory -Path (Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid()))
|
||||
$tmpBin = Join-Path $tmp "crucible$ext"
|
||||
Write-Host "crucible: fetching $asset $tag..."
|
||||
if (-not (Invoke-Fetch "$base/$asset" $tmpBin)) { Write-Error "crucible: download failed for $asset"; exit 1 }
|
||||
$sums = Join-Path $tmp 'SHA256SUMS'
|
||||
if (Invoke-Fetch "$base/SHA256SUMS" $sums) {
|
||||
$want = (Select-String -Path $sums -Pattern ([regex]::Escape($asset) + '$') | Select-Object -First 1).Line -split '\s+' | Select-Object -First 1
|
||||
$got = (Get-FileHash $tmpBin -Algorithm SHA256).Hash.ToLower()
|
||||
if ($want -and ($want.ToLower() -ne $got)) { Write-Error "crucible: checksum mismatch for $asset"; exit 1 }
|
||||
}
|
||||
Move-Item -Force $tmpBin $bin
|
||||
Remove-Item -Recurse -Force $tmp
|
||||
}
|
||||
|
||||
& $bin @args
|
||||
exit $LASTEXITCODE
|
||||
Reference in New Issue
Block a user