b765cca0ed
---ci--- project: orca phase: 17 milestone: v0.12 status: execute ---/ci--- install.sh now fetches SHA256SUMS from the release and verifies the tarball checksum before extraction. Fail closed on mismatch. Warns if SHA256SUMS is absent (insecure). Build green.
227 lines
8.3 KiB
Bash
Executable File
227 lines
8.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# install.sh — 1-liner installer for orca
|
|
#
|
|
# Usage:
|
|
# curl -fsSL https://git.cloudinit.dev/coreci/orca/raw/branch/main/scripts/install.sh | bash
|
|
# curl -fsSL https://git.cloudinit.dev/coreci/orca/raw/branch/main/scripts/install.sh | bash -s -- --system
|
|
# curl -fsSL https://git.cloudinit.dev/coreci/orca/raw/branch/main/scripts/install.sh | bash -s -- --version v0.4.2
|
|
#
|
|
# Options:
|
|
# --system Install at system level (/usr/local/bin/orca, namespace /root/.orca). Requires root.
|
|
# --version <tag> Pin a specific version (e.g. v0.4.2). Default: latest release.
|
|
# --check Dry-run: print the version + asset URL + install path without writing.
|
|
# --help, -h Show this help.
|
|
#
|
|
# Behavior:
|
|
# - Downloads the release tarball from the public Gitea release URL.
|
|
# - Extracts the orca binary to the install path.
|
|
# - If the resolved release (latest or pinned) has no matching binary
|
|
# asset, walks backward through recent releases to find one that does,
|
|
# and prints a warning. (REQ-098 — the v0.8.x releases shipped with
|
|
# zero binary assets, causing install to resolve to v0.4.5.)
|
|
# - If an existing orca binary is found, reads its version and prints
|
|
# "updated from X to Y" (in-place update; preserves config/db/certs).
|
|
# - Idempotent: re-running with the same version reinstalls the binary.
|
|
# - Never touches the namespace dir (~/.orca or /root/.orca) — that's user state.
|
|
|
|
set -euo pipefail
|
|
|
|
GITEA_URL="${GITEA_URL:-https://git.cloudinit.dev}"
|
|
GITEA_OWNER="${GITEA_OWNER:-coreci}"
|
|
GITEA_REPO="${GITEA_REPO:-orca}"
|
|
|
|
SYSTEM=false
|
|
VERSION=""
|
|
CHECK=false
|
|
INSTALL_BIN=""
|
|
NAMESPACE_DIR=""
|
|
|
|
warn() {
|
|
printf " \033[1;33m!\033[0m %s\n" "$*" >&2
|
|
}
|
|
|
|
err() { echo "install: error: $*" >&2; exit 1; }
|
|
info() { echo "install: $*"; }
|
|
|
|
usage() {
|
|
sed -n '2,/^$/p' "$0" | sed 's/^# \?//' >&2
|
|
exit 0
|
|
}
|
|
|
|
# --- parse args ------------------------------------------------------------
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--system) SYSTEM=true; shift ;;
|
|
--version) VERSION="${2:-}"; shift 2 ;;
|
|
--version=*) VERSION="${1#*=}"; shift ;;
|
|
--check) CHECK=true; shift ;;
|
|
--help|-h) usage ;;
|
|
*) err "unknown argument: $1 (try --help)" ;;
|
|
esac
|
|
done
|
|
|
|
# --- determine install paths ----------------------------------------------
|
|
|
|
if [ "$SYSTEM" = "true" ]; then
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
err "--system requires root (uid 0). Re-run with sudo or drop --system for user-level install."
|
|
fi
|
|
INSTALL_BIN="/usr/local/bin/orca"
|
|
NAMESPACE_DIR="/root/.orca"
|
|
else
|
|
INSTALL_BIN="${HOME}/.local/bin/orca"
|
|
NAMESPACE_DIR="${HOME}/.orca"
|
|
fi
|
|
|
|
INSTALL_DIR="$(dirname "$INSTALL_BIN")"
|
|
|
|
# --- determine version ----------------------------------------------------
|
|
|
|
if [ -z "$VERSION" ]; then
|
|
info "querying latest release from ${GITEA_URL}..."
|
|
VERSION="$(curl -fsSL "${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/releases/latest" \
|
|
| sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
|
|
| head -1)"
|
|
if [ -z "$VERSION" ]; then
|
|
err "could not determine latest release version from Gitea API"
|
|
fi
|
|
fi
|
|
info "version: ${VERSION}"
|
|
|
|
# --- detect arch ----------------------------------------------------------
|
|
|
|
ARCH="$(uname -m)"
|
|
case "$ARCH" in
|
|
x86_64) ARCH=amd64 ;;
|
|
aarch64|arm64) ARCH=arm64 ;;
|
|
armv7l) ARCH=armv7 ;;
|
|
*) err "unsupported architecture: ${ARCH} (supported: amd64, arm64, armv7)" ;;
|
|
esac
|
|
|
|
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
TARBALL="orca-${VERSION}-${OS}-${ARCH}.tar.gz"
|
|
|
|
# --- find asset download URL (with fallback walk — REQ-098) --------------
|
|
#
|
|
# The v0.8.x releases shipped with zero binary assets attached, causing
|
|
# install to error out on the latest release. If the resolved release
|
|
# (latest or --version) lacks the matching tarball, walk backward through
|
|
# recent releases to find one that carries it, and print a warning.
|
|
|
|
find_asset_url() {
|
|
# $1 = tag. Prints the browser_download_url for the matching tarball, or empty.
|
|
# The `|| true` prevents set -e + pipefail from exiting the script when
|
|
# grep finds no match (exit 1) — an empty result is a valid outcome.
|
|
local tag="$1"
|
|
curl -fsSL "${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/releases/tags/${tag}" \
|
|
| sed -n 's/.*"browser_download_url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
|
|
| grep "/${TARBALL}\$" \
|
|
| head -1 || true
|
|
}
|
|
|
|
info "locating asset ${TARBALL} in release ${VERSION}..."
|
|
ASSET_URL="$(find_asset_url "$VERSION")"
|
|
|
|
if [ -z "$ASSET_URL" ]; then
|
|
info "WARNING: release ${VERSION} has no ${TARBALL} asset. Walking back through recent releases..."
|
|
# The /releases list endpoint returns assets inline (browser_download_url
|
|
# appears within each release's assets array). Extract all download URLs
|
|
# from the list response and find the first (newest) one matching our
|
|
# OS+arch tarball pattern (any version). This avoids per-release API calls.
|
|
ASSET_URL="$(curl -fsSL "${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/releases?limit=50" \
|
|
| grep -oE '"browser_download_url"[[:space:]]*:[[:space:]]*"[^"]*"' \
|
|
| sed -n 's/.*"browser_download_url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
|
|
| grep -E "/orca-[^/]*-${OS}-${ARCH}\.tar\.gz$" \
|
|
| head -1 || true)"
|
|
if [ -n "$ASSET_URL" ]; then
|
|
# Extract the version from the URL (e.g. .../download/v0.4.5/orca-...)
|
|
FALLBACK_VERSION="$(echo "$ASSET_URL" | sed -n 's|.*/download/\([^/]*\)/.*|\1|p')"
|
|
info "WARNING: latest release ${VERSION} has no binary asset; falling back to ${FALLBACK_VERSION} which has orca-${FALLBACK_VERSION}-${OS}-${ARCH}.tar.gz."
|
|
VERSION="$FALLBACK_VERSION"
|
|
else
|
|
err "could not find any release with a ${OS}-${ARCH} tarball in the last 50 releases. Check that a release exists with a linux-${ARCH} binary."
|
|
fi
|
|
fi
|
|
info "asset: ${ASSET_URL}"
|
|
|
|
# --- in-place update detection -------------------------------------------
|
|
|
|
OLD_VERSION=""
|
|
if [ -x "$INSTALL_BIN" ]; then
|
|
OLD_VERSION="$("$INSTALL_BIN" version --json 2>/dev/null | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1 || echo "")"
|
|
fi
|
|
|
|
# --- --check dry-run (D-194) ---------------------------------------------
|
|
# Print what would be installed without writing anything.
|
|
|
|
if [ "$CHECK" = "true" ]; then
|
|
info "dry-run (--check): no files will be written"
|
|
info " would install: orca ${VERSION}"
|
|
info " asset: ${ASSET_URL}"
|
|
info " binary path: ${INSTALL_BIN}"
|
|
info " namespace root: ${NAMESPACE_DIR}"
|
|
if [ -n "$OLD_VERSION" ]; then
|
|
info " current: ${OLD_VERSION} (would update to ${VERSION})"
|
|
else
|
|
info " current: (not installed)"
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
# --- download + extract ---------------------------------------------------
|
|
|
|
TMPDIR="$(mktemp -d)"
|
|
trap 'rm -rf "$TMPDIR"' EXIT
|
|
|
|
info "downloading..."
|
|
curl -fsSL -o "${TMPDIR}/${TARBALL}" "$ASSET_URL"
|
|
|
|
# REQ-132 / F14: verify tarball checksum before extraction.
|
|
# Fetch SHA256SUMS from the same release; fail closed on mismatch.
|
|
SHA256SUMS_URL="$(dirname "$ASSET_URL")/SHA256SUMS"
|
|
if curl -fsSL -o "${TMPDIR}/SHA256SUMS" "$SHA256SUMS_URL" 2>/dev/null; then
|
|
info "verifying checksum..."
|
|
(cd "$TMPDIR" && grep -F "$TARBALL" SHA256SUMS | sha256sum -c -) || {
|
|
err "checksum verification failed (REQ-132); refusing to install"
|
|
exit 1
|
|
}
|
|
else
|
|
warn "no SHA256SUMS found at $SHA256SUMS_URL; skipping checksum (insecure)"
|
|
fi
|
|
|
|
info "extracting..."
|
|
tar -xzf "${TMPDIR}/${TARBALL}" -C "$TMPDIR"
|
|
|
|
if [ ! -f "${TMPDIR}/orca" ]; then
|
|
err "tarball did not contain an 'orca' binary"
|
|
fi
|
|
|
|
# --- install --------------------------------------------------------------
|
|
|
|
mkdir -p "$INSTALL_DIR"
|
|
install -m 0755 "${TMPDIR}/orca" "$INSTALL_BIN"
|
|
|
|
# --- report ---------------------------------------------------------------
|
|
|
|
if [ -n "$OLD_VERSION" ]; then
|
|
if [ "$OLD_VERSION" = "$VERSION" ]; then
|
|
info "✓ reinstalled orca ${VERSION} at ${INSTALL_BIN}"
|
|
else
|
|
info "✓ updated orca from ${OLD_VERSION} to ${VERSION} at ${INSTALL_BIN}"
|
|
fi
|
|
else
|
|
info "✓ installed orca ${VERSION} to ${INSTALL_BIN}"
|
|
fi
|
|
|
|
if [ "$SYSTEM" = "true" ]; then
|
|
info " namespace root: ${NAMESPACE_DIR} (use 'orca --system init' to initialize)"
|
|
else
|
|
info " namespace root: ${NAMESPACE_DIR} (use 'orca init' to initialize)"
|
|
if ! echo "$PATH" | grep -q "$INSTALL_DIR"; then
|
|
info " NOTE: $INSTALL_DIR is not on your PATH. Add it:"
|
|
info " export PATH=\"\$PATH:$INSTALL_DIR\""
|
|
fi
|
|
fi
|
|
|
|
info " verify: ${INSTALL_BIN} version" |