#!/bin/sh # ontoref installer — fetch a per-platform OCI bundle and install the CLI + daemon. # # Generated by `ore workflow generate` from install/install.sh.tmpl. The registry # and version values below come from the distribution catalog in # .ontoref/ontology/workflow.ncl — do not edit this file; edit the .tmpl and # regenerate. # # Usage: # curl -fsSL @@INSTALLER_URL@@ | sh # curl -fsSL @@INSTALLER_URL@@ | sh -s -- --prefix ~/.local --version 0.1.6 # sh install.sh --uninstall [--purge] # # The daemon is an OPTIONAL accelerator (ADR-029). This installs a CLI + data # layer that work without it; run the daemon via the container image if wanted: # docker run --rm -p 7891:7891 @@REGISTRY@@/@@IMAGE_REPO@@:@@DEFAULT_VERSION@@ set -eu REGISTRY="@@REGISTRY@@" BUNDLE_REPO="@@BUNDLE_REPO@@" IMAGE_REPO="@@IMAGE_REPO@@" DEFAULT_VERSION="@@DEFAULT_VERSION@@" PREFIX="${HOME}/.local" DATA_DIR="${HOME}/.local/share/ontoref" CONFIG_DIR="${HOME}/.config/ontoref" VERSION="${DEFAULT_VERSION}" DO_UNINSTALL=0 DO_PURGE=0 log() { printf ' %s\n' "$*"; } ok() { printf '\033[32m✓\033[0m %s\n' "$*"; } warn() { printf '\033[33mwarn\033[0m %s\n' "$*" >&2; } die() { printf '\033[31merror\033[0m %s\n' "$*" >&2; exit 1; } usage() { cat </dev/null 2>&1; } need tar || die "tar is required" need curl || die "curl is required" WORK="$(mktemp -d)" trap 'rm -rf "$WORK"' EXIT INT TERM # ── Fetch the bundle: prefer oras, fall back to the OCI Distribution API ────── fetch_with_oras() { log "fetching via oras" ( cd "$WORK" && oras pull "$REF" ) >/dev/null 2>&1 } # Minimal anonymous-pull client over the OCI Distribution API (curl + grep/sed). # Handles the optional Bearer-token dance for public namespaces. registry_token() { _repo="$1" _chal="$(curl -fsSL -o /dev/null -D - "https://${REGISTRY}/v2/" 2>/dev/null \ | tr -d '\r' | grep -i '^www-authenticate:' || true)" [ -z "$_chal" ] && { echo ""; return 0; } _realm="$(printf '%s' "$_chal" | sed -n 's/.*realm="\([^"]*\)".*/\1/p')" _service="$(printf '%s' "$_chal" | sed -n 's/.*service="\([^"]*\)".*/\1/p')" [ -z "$_realm" ] && { echo ""; return 0; } curl -fsSL "${_realm}?service=${_service}&scope=repository:${_repo}:pull" 2>/dev/null \ | grep -o '"token":"[^"]*"' | head -1 | sed 's/"token":"\([^"]*\)"/\1/' } fetch_with_curl() { log "oras not found — fetching via OCI Distribution API (curl)" _tag="${VERSION}-linux-${arch}" _tok="$(registry_token "$BUNDLE_REPO")" _auth="" [ -n "$_tok" ] && _auth="Authorization: Bearer ${_tok}" _man="${WORK}/manifest.json" curl -fsSL ${_auth:+-H "$_auth"} \ -H "Accept: application/vnd.oci.image.manifest.v1+json" \ -H "Accept: application/vnd.oci.artifact.manifest.v1+json" \ "https://${REGISTRY}/v2/${BUNDLE_REPO}/manifests/${_tag}" -o "$_man" \ || die "manifest fetch failed for ${REF}" # Pull each layer blob, naming it by its title annotation when present. # Layers appear as {"mediaType":..,"digest":"sha256:..","annotations":{"org.opencontainers.image.title":"name"}} printf '%s' "$(cat "$_man")" | tr '}' '}\n' | grep '"digest"' | while IFS= read -r line; do _dig="$(printf '%s' "$line" | grep -o 'sha256:[0-9a-f]\{64\}' | head -1)" [ -z "$_dig" ] && continue _title="$(printf '%s' "$line" | sed -n 's/.*"org.opencontainers.image.title":"\([^"]*\)".*/\1/p')" [ -z "$_title" ] && _title="$_dig" curl -fsSL ${_auth:+-H "$_auth"} \ "https://${REGISTRY}/v2/${BUNDLE_REPO}/blobs/${_dig}" -o "${WORK}/${_title}" \ || die "blob fetch failed: ${_dig}" done } if need oras; then fetch_with_oras else fetch_with_curl fi TARBALL="${WORK}/${TARBALL_NAME}" [ -f "$TARBALL" ] || die "bundle ${TARBALL_NAME} not present after fetch" # ── Verify integrity ────────────────────────────────────────────────────────── if [ -f "${TARBALL}.sha256" ]; then expected="$(awk '{print $1}' "${TARBALL}.sha256")" if need sha256sum; then actual="$(sha256sum "$TARBALL" | awk '{print $1}')" elif need shasum; then actual="$(shasum -a 256 "$TARBALL" | awk '{print $1}')" else actual="" warn "no sha256 tool — skipping checksum verification" fi if [ -n "$actual" ]; then [ "$actual" = "$expected" ] || die "checksum mismatch: expected ${expected}, got ${actual}" ok "checksum verified" fi else warn "no .sha256 sidecar — skipping checksum verification" fi # Optional signature verification (best-effort; needs the public key out of band). if need cosign && [ -f "${WORK}/cosign.pub" ]; then cosign verify-blob --key "${WORK}/cosign.pub" --signature "${TARBALL}.sig" "$TARBALL" \ >/dev/null 2>&1 && ok "cosign signature verified" || warn "cosign verification failed (continuing)" fi # ── Extract ─────────────────────────────────────────────────────────────────── STAGE="${WORK}/stage" mkdir -p "$STAGE" tar -xzf "$TARBALL" -C "$STAGE" [ -f "${STAGE}/bin/ontoref-daemon.bin" ] || die "bundle missing bin/ontoref-daemon.bin" # ── Upgrade-safe: stop a running daemon before replacing its binary ─────────── if need pkill; then pkill -x ontoref-daemon.bin 2>/dev/null || true pkill -x ontoref-daemon 2>/dev/null || true fi # ── Place binaries + wrappers ───────────────────────────────────────────────── mkdir -p "$BIN_DIR" install -m 0755 "${STAGE}/bin/ontoref-daemon.bin" "${BIN_DIR}/ontoref-daemon.bin" install -m 0755 "${STAGE}/bin/ontoref-daemon-boot" "${BIN_DIR}/ontoref-daemon" install -m 0755 "${STAGE}/bin/nickel" "${BIN_DIR}/nickel" [ -f "${STAGE}/bin/ontoref-tier" ] && install -m 0755 "${STAGE}/bin/ontoref-tier" "${BIN_DIR}/ontoref-tier" # Bake the data dir as ONTOREF_ROOT so the wrapper is self-contained (mirrors # install.nu §3): replace the placeholder default with the resolved DATA_DIR. sed "s|ONTOREF_ROOT=\"\${ONTOREF_ROOT:-ontoref}\"|ONTOREF_ROOT=\"\${ONTOREF_ROOT:-${DATA_DIR}}\"|" \ "${STAGE}/bin/ontoref-global" > "${BIN_DIR}/ontoref" chmod 0755 "${BIN_DIR}/ontoref" ok "binaries → ${BIN_DIR}" # ── Place the data layer ────────────────────────────────────────────────────── mkdir -p "$DATA_DIR" # cp -a preserves the tree; we replace reflection/ontology/etc wholesale so an # upgrade never leaves stale files behind in those subtrees. for sub in reflection ontology domains templates public install; do if [ -d "${STAGE}/data/${sub}" ]; then rm -rf "${DATA_DIR:?}/${sub}" cp -a "${STAGE}/data/${sub}" "${DATA_DIR}/${sub}" fi done ok "data → ${DATA_DIR}" # ── Seed the config skeleton (idempotent — never clobber existing config) ───── mkdir -p "$CONFIG_DIR" if [ -f "${STAGE}/config/config.ncl" ]; then cp "${STAGE}/config/config.ncl" "${CONFIG_DIR}/config.ncl.example" [ -f "${CONFIG_DIR}/config.ncl" ] || cp "${STAGE}/config/config.ncl" "${CONFIG_DIR}/config.ncl" fi for f in projects.ncl remote-projects.ncl streams.json; do [ -f "${STAGE}/config/${f}" ] && [ ! -f "${CONFIG_DIR}/${f}" ] && cp "${STAGE}/config/${f}" "${CONFIG_DIR}/${f}" done if [ -d "${STAGE}/config/schemas" ]; then mkdir -p "${CONFIG_DIR}/schemas" for f in "${STAGE}/config/schemas/"*; do [ -e "$f" ] || continue bn="$(basename "$f")" [ -f "${CONFIG_DIR}/schemas/${bn}" ] || cp "$f" "${CONFIG_DIR}/schemas/${bn}" done fi ok "config → ${CONFIG_DIR}" # ── Runtime dependency: nushell (required by the CLI wrapper) ───────────────── if ! need nu; then warn "Nushell (nu) >= 0.110 is required by the 'ontoref' CLI but was not found." warn "Install it: https://www.nushell.sh/book/installation.html (the daemon itself does not need it)" fi case ":${PATH}:" in *":${BIN_DIR}:"*) ;; *) warn "${BIN_DIR} is not on PATH — add it: export PATH=\"${BIN_DIR}:\$PATH\"" ;; esac printf '\n' ok "ontoref ${VERSION} installed" log "next: ontoref describe project (run inside an ontoref-onboarded project)" log " ontoref-daemon (optional accelerator — caches NCL export)"