Files
at-container-registry/pkg/appview/installscript/templates/install.ps1.tmpl
T
Evan JarrettandClaude Opus 5 9d8bd513da appview: render the install scripts from config instead of shipping ATCR's
seamark.dev's /install and /settings/devices told users to pipe
seamark.dev/static/install.sh into bash. That file was the unmodified ATCR
script: it announced itself as the "ATCR Credential Helper Installer",
installed docker-credential-atcr, and finished by telling the user to configure
credHelpers for atcr.io, the wrong registry for that deployment. Anyone
following the documented setup ended up pointed at another service. The
templates hardcoded docker-credential-atcr, "atcr" and ~/.atcr/device.json
alongside a correctly themed {{ .RegistryURL }}.

The scripts are now rendered from config by a handler, rather than forked per
brand. A theme overlay was the alternative and was worse: it needed a full copy
of both install.sh and install.ps1 per brand, four scripts to keep in sync, and
the operator asked for these values to come from config.

credential_helper.name is the single knob. Docker resolves a credHelpers value
x by exec'ing docker-credential-x, so the credHelpers value, the binary suffix
and the config directory are genuinely one word, not three that can drift. It
is validated against a strict pattern because it is interpolated into a shell
script.

install.sh renders byte-identical to the deleted static file under the atcr
default, so existing installs are unaffected. install.ps1 differs by one line,
where a stale usage comment named a path the script is not served at.

Two behaviour changes worth noting: these two URLs drop from a one-year
Cache-Control to five minutes, since the body now depends on deployment config;
and credential_helper.tangled_repo becomes a real overridable default. It was
previously assigned over unconditionally and read by nothing, while the shipped
script used a different URL form.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PDqoCE1j3njokkZ9b1C5n9
2026-09-02 21:38:10 -05:00

195 lines
6.7 KiB
Cheetah

# {{ .DisplayName }} Credential Helper Installation Script for Windows
# Usage: iwr -useb https://{{ .SiteHost }}/static/install.ps1 | iex
$ErrorActionPreference = "Stop"
# Configuration
$BinaryName = "{{ .BinaryName }}.exe"
$InstallDir = if ($env:{{ .EnvPrefix }}_INSTALL_DIR) { $env:{{ .EnvPrefix }}_INSTALL_DIR } else { "$env:ProgramFiles\{{ .DisplayName }}" }
$TangledRepo = if ($env:{{ .EnvPrefix }}_TANGLED_REPO) { $env:{{ .EnvPrefix }}_TANGLED_REPO } else { "{{ .ReleasesBaseURL }}" }
Write-Host "{{ .DisplayName }} Credential Helper Installer for Windows" -ForegroundColor Green
Write-Host ""
# Detect architecture
function Get-Architecture {
$arch = (Get-WmiObject Win32_Processor).Architecture
switch ($arch) {
9 { return "x86_64" } # x64
12 { return "arm64" } # ARM64
default {
Write-Host "Unsupported architecture: $arch" -ForegroundColor Red
exit 1
}
}
}
$Arch = Get-Architecture
Write-Host "Detected: Windows $Arch" -ForegroundColor Green
# Resolve the latest version by following the tangled /tags/latest redirect
# chain. Tangled redirects DID→handle first, then handle/tags/latest→handle/tags/vX.Y.Z,
# so we follow all redirects and read the final effective URL.
function Get-LatestVersion {
Write-Host "Resolving latest version..." -ForegroundColor Yellow
try {
$response = Invoke-WebRequest -Uri "$TangledRepo/tags/latest" -UseBasicParsing -Method Head
} catch {
Write-Host "Failed to resolve latest version from $TangledRepo/tags/latest" -ForegroundColor Red
exit 1
}
# PowerShell 7+ exposes the final URI via RequestMessage; PowerShell 5 via ResponseUri.
if ($response.BaseResponse.RequestMessage) {
$finalUrl = $response.BaseResponse.RequestMessage.RequestUri.ToString()
} else {
$finalUrl = $response.BaseResponse.ResponseUri.ToString()
}
if (-not $finalUrl) {
Write-Host "Failed to resolve latest version from $TangledRepo/tags/latest" -ForegroundColor Red
exit 1
}
$tag = $finalUrl.TrimEnd('/').Split('/')[-1]
if (-not $tag.StartsWith('v')) {
Write-Host "Unexpected redirect location: $finalUrl" -ForegroundColor Red
exit 1
}
Write-Host "Found latest version: $tag" -ForegroundColor Green
return $tag
}
# Build the download URL from version and platform
function Get-DownloadUrl {
param([string]$Version, [string]$Arch)
$versionClean = $Version.TrimStart('v')
$fileName = "{{ .BinaryName }}_${versionClean}_Windows_${Arch}.tar.gz"
return "$TangledRepo/tags/$Version/download/$fileName"
}
# Determine version and download URL
if ($env:{{ .EnvPrefix }}_VERSION) {
$Version = $env:{{ .EnvPrefix }}_VERSION
Write-Host "Using specified version: $Version" -ForegroundColor Yellow
} else {
$Version = Get-LatestVersion
}
$DownloadUrl = Get-DownloadUrl -Version $Version -Arch $Arch
Write-Host "Installing version: $Version" -ForegroundColor Green
# Download and install binary
function Install-Binary {
param (
[string]$DownloadUrl
)
Write-Host "Downloading from: $DownloadUrl" -ForegroundColor Yellow
$tempDir = New-Item -ItemType Directory -Path "$env:TEMP\{{ .Name }}-install-$(Get-Random)" -Force
$archivePath = Join-Path $tempDir "{{ .BinaryName }}.tar.gz"
try {
Invoke-WebRequest -Uri $DownloadUrl -OutFile $archivePath -UseBasicParsing
} catch {
Write-Host "Failed to download release: $_" -ForegroundColor Red
exit 1
}
Write-Host "Extracting..." -ForegroundColor Yellow
# Modern Windows ships tar.exe; use it to handle .tar.gz produced by goreleaser.
& tar.exe -xzf $archivePath -C $tempDir
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to extract archive" -ForegroundColor Red
exit 1
}
# Create install directory
if (-not (Test-Path $InstallDir)) {
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
}
# Install binary
$binaryPath = Join-Path $tempDir $BinaryName
$destPath = Join-Path $InstallDir $BinaryName
Copy-Item -Path $binaryPath -Destination $destPath -Force
# Clean up
Remove-Item -Path $tempDir -Recurse -Force
Write-Host "Installed $BinaryName to $InstallDir" -ForegroundColor Green
}
# Add to PATH if not already present
function Add-ToPath {
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentPath -notlike "*$InstallDir*") {
Write-Host "Adding $InstallDir to system PATH..." -ForegroundColor Yellow
# Check if running as administrator
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if ($isAdmin) {
[Environment]::SetEnvironmentVariable("Path", "$currentPath;$InstallDir", "Machine")
$env:Path = "$env:Path;$InstallDir"
Write-Host "Added to PATH successfully" -ForegroundColor Green
} else {
Write-Host "WARNING: Not running as administrator. Cannot update system PATH." -ForegroundColor Yellow
Write-Host "Please add $InstallDir to your PATH manually or re-run as administrator." -ForegroundColor Yellow
}
}
}
# Verify installation
function Test-Installation {
$binaryPath = Join-Path $InstallDir $BinaryName
if (Test-Path $binaryPath) {
Write-Host "Verification successful!" -ForegroundColor Green
# Try to run version command
try {
& $binaryPath --version
} catch {
Write-Host "Binary installed but PATH may need to be refreshed." -ForegroundColor Yellow
Write-Host "Please restart your terminal or run: refreshenv" -ForegroundColor Yellow
}
} else {
Write-Host "Installation failed: binary not found at $binaryPath" -ForegroundColor Red
exit 1
}
}
# Show configuration instructions
function Show-Configuration {
Write-Host ""
Write-Host "Installation complete!" -ForegroundColor Green
Write-Host ""
Write-Host "To use {{ .DisplayName }} with Docker, configure Docker to use this credential helper:" -ForegroundColor Yellow
Write-Host ' Edit %USERPROFILE%\.docker\config.json and add:'
Write-Host ' {
"credHelpers": {
"{{ .RegistryHost }}": "{{ .Name }}"
}
}'
Write-Host ""
Write-Host "Note: You may need to restart your terminal for PATH changes to take effect." -ForegroundColor Yellow
}
# Main installation flow
try {
Install-Binary -DownloadUrl $DownloadUrl
Add-ToPath
Test-Installation
Show-Configuration
} catch {
Write-Host "Installation failed: $_" -ForegroundColor Red
exit 1
}