Files
at-container-registry/pkg/appview/installscript/templates/install.sh.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

163 lines
4.4 KiB
Cheetah

#!/bin/bash
# {{ .DisplayName }} Credential Helper Installation Script
# Usage: curl -fsSL https://{{ .SiteHost }}/static/install.sh | bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
BINARY_NAME="{{ .BinaryName }}"
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
TANGLED_REPO="${ {{- .EnvPrefix }}_TANGLED_REPO:-{{ .ReleasesBaseURL }}}"
# Detect OS and architecture
detect_platform() {
local os=$(uname -s | tr '[:upper:]' '[:lower:]')
local arch=$(uname -m)
case "$os" in
linux*)
OS="Linux"
;;
darwin*)
OS="Darwin"
;;
*)
echo -e "${RED}Unsupported OS: $os${NC}"
exit 1
;;
esac
case "$arch" in
x86_64|amd64)
ARCH="x86_64"
;;
aarch64|arm64)
ARCH="arm64"
;;
*)
echo -e "${RED}Unsupported architecture: $arch${NC}"
exit 1
;;
esac
}
# 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 need -L to follow both hops and read the final effective URL.
fetch_latest_version() {
echo -e "${YELLOW}Resolving latest version...${NC}"
local final_url
final_url=$(curl -sL --max-time 10 -o /dev/null -w '%{url_effective}' "${TANGLED_REPO}/tags/latest")
if [ -z "$final_url" ]; then
echo -e "${RED}Failed to resolve latest version from ${TANGLED_REPO}/tags/latest${NC}"
exit 1
fi
VERSION="${final_url##*/}"
if [ -z "$VERSION" ] || [ "${VERSION#v}" = "$VERSION" ]; then
echo -e "${RED}Unexpected redirect location: ${final_url}${NC}"
exit 1
fi
echo -e "${GREEN}Found latest version: ${VERSION}${NC}"
}
# Build the download URL from version and platform
build_download_url() {
local version_without_v="${VERSION#v}"
DOWNLOAD_URL="${TANGLED_REPO}/tags/${VERSION}/download/{{ .BinaryName }}_${version_without_v}_${OS}_${ARCH}.tar.gz"
}
# Download and install binary
install_binary() {
echo -e "${YELLOW}Downloading from: ${DOWNLOAD_URL}${NC}"
local tmp_dir=$(mktemp -d)
trap "rm -rf $tmp_dir" EXIT
if ! curl -fsSL "$DOWNLOAD_URL" -o "$tmp_dir/{{ .BinaryName }}.tar.gz"; then
echo -e "${RED}Failed to download release${NC}"
exit 1
fi
echo -e "${YELLOW}Extracting...${NC}"
tar -xzf "$tmp_dir/{{ .BinaryName }}.tar.gz" -C "$tmp_dir"
# Check if we need sudo
if [ -w "$INSTALL_DIR" ]; then
SUDO=""
else
SUDO="sudo"
echo -e "${YELLOW}Installing to ${INSTALL_DIR} (requires sudo)${NC}"
fi
$SUDO mkdir -p "$INSTALL_DIR"
$SUDO install -m 755 "$tmp_dir/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME"
echo -e "${GREEN}Installed ${BINARY_NAME} to ${INSTALL_DIR}${NC}"
}
# Verify installation
verify_installation() {
if ! command -v "$BINARY_NAME" &> /dev/null; then
echo -e "${RED}${BINARY_NAME} not found in PATH${NC}"
echo -e "${YELLOW}You may need to add ${INSTALL_DIR} to your PATH${NC}"
echo -e "${YELLOW}Add this to your ~/.bashrc or ~/.zshrc:${NC}"
echo -e " export PATH=\"${INSTALL_DIR}:\$PATH\""
return 1
fi
echo -e "${GREEN}Verification successful!${NC}"
"$BINARY_NAME" --version
}
# Configure Docker
configure_docker() {
echo ""
echo -e "${GREEN}Installation complete!${NC}"
echo ""
echo -e "${YELLOW}To use {{ .DisplayName }} with Docker, configure Docker to use this credential helper:${NC}"
echo -e ' echo '\''{"credHelpers": {"{{ .RegistryHost }}": "{{ .Name }}"}}'\'' > ~/.docker/config.json'
echo ""
echo -e "${YELLOW}Or add to existing config.json:${NC}"
echo -e ' {
"credHelpers": {
"{{ .RegistryHost }}": "{{ .Name }}"
}
}'
}
# Main
main() {
echo -e "${GREEN}{{ .DisplayName }} Credential Helper Installer${NC}"
echo ""
detect_platform
echo -e "Detected: ${GREEN}${OS} ${ARCH}${NC}"
if [ -n "${{ .EnvPrefix }}_VERSION" ]; then
VERSION="${{ .EnvPrefix }}_VERSION"
echo -e "Using specified version: ${GREEN}${VERSION}${NC}"
else
fetch_latest_version
fi
build_download_url
echo -e "Installing version: ${GREEN}${VERSION}${NC}"
install_binary
verify_installation
configure_docker
}
main "$@"