#!/bin/sh set -eu INK_VERSION=${INK_VERSION:-main} TOOLSET_VERSION=${TOOLSET_VERSION:-main} PREFIX=${INK_PREFIX:-$HOME/.ink} RELEASE_BASE=${INK_RELEASE_BASE:-https://git.tmk241.com/tmk241/ink-releases/raw/branch/main} TARGET=x86_64-linux-musl SETS= TOOLS= ASSUME_YES=0 LIST_ONLY=0 usage() { cat <<'HELP' Usage: install.sh [OPTIONS] Install the static Ink core and optionally selected permission-sized tools. With a terminal and no selection flags, a small set menu is shown. Without a terminal, the safe default installs only Ink. Options: --set NAME Add text, filesystem, web, git, or all (repeatable) --tool NAME Add one exact executable (repeatable) --version VERSION Use immutable VERSION for Ink and Ink Toolset --ink-version VERSION Select main or an immutable Ink release --toolset-version VER Select main or an immutable Toolset release --prefix DIR Install executable files under DIR/bin --list List available tool assets; do not install -y, --yes Do not ask for confirmation -h, --help Show this help Environment: INK_VERSION, TOOLSET_VERSION, INK_PREFIX (versions default to main) INK_RELEASE_BASE (public ink-releases raw root) Examples: curl -fsSL URL/install.sh | sh curl -fsSL URL/install.sh | sh -s -- --set text --set web -y curl -fsSL URL/install.sh | sh -s -- --tool lines --tool httpget -y Effects: Writes executable files only under PREFIX/bin. It never edits PATH, shell configuration, Ink policy, credentials, or unrelated state. HELP } append_word() { if [ -z "$1" ]; then printf '%s' "$2"; else printf '%s %s' "$1" "$2"; fi } while [ $# -gt 0 ]; do case $1 in --set) [ $# -ge 2 ] || { echo 'install: --set needs a value' >&2; exit 2; }; SETS=$(append_word "$SETS" "$2"); shift 2 ;; --tool) [ $# -ge 2 ] || { echo 'install: --tool needs a value' >&2; exit 2; }; TOOLS=$(append_word "$TOOLS" "$2"); shift 2 ;; --version) [ $# -ge 2 ] || { echo 'install: --version needs a value' >&2; exit 2; }; INK_VERSION=$2; TOOLSET_VERSION=$2; shift 2 ;; --ink-version) [ $# -ge 2 ] || { echo 'install: --ink-version needs a value' >&2; exit 2; }; INK_VERSION=$2; shift 2 ;; --toolset-version) [ $# -ge 2 ] || { echo 'install: --toolset-version needs a value' >&2; exit 2; }; TOOLSET_VERSION=$2; shift 2 ;; --prefix) [ $# -ge 2 ] || { echo 'install: --prefix needs a value' >&2; exit 2; }; PREFIX=$2; shift 2 ;; --list) LIST_ONLY=1; shift ;; -y|--yes) ASSUME_YES=1; shift ;; -h|--help) usage; exit 0 ;; --) shift; break ;; *) echo "install: unknown option: $1" >&2; exit 2 ;; esac done [ $# -eq 0 ] || { echo 'install: trailing arguments' >&2; exit 2; } case $(uname -m) in x86_64|amd64) ;; *) echo 'install: this release supports x86_64 Linux only' >&2; exit 1 ;; esac case $PREFIX in ''|/) echo 'install: unsafe empty/root prefix' >&2; exit 2 ;; esac TTY=0 if [ -r /dev/tty ] && [ -w /dev/tty ]; then TTY=1; fi if [ "$TTY" -eq 1 ]; then cyan='\033[36m'; bold='\033[1m'; dim='\033[2m'; reset='\033[0m' else cyan= bold= dim= reset= fi say() { printf '%s\n' "$*" >&2; } fetch() { url=$1 destination=$2 if command -v curl >/dev/null 2>&1; then curl -fsSL "$url" -o "$destination" elif command -v wget >/dev/null 2>&1; then wget -qO "$destination" "$url" else echo 'install: curl or wget is required' >&2; exit 1 fi } digest() { if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | awk '{print $1}' elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | awk '{print $1}' else echo 'install: sha256sum or shasum is required' >&2; exit 1 fi } if [ -z "$SETS$TOOLS" ] && [ "$TTY" -eq 1 ] && [ "$LIST_ONLY" -eq 0 ]; then printf '%bInk%b %bsmall tools, explicit authority%b\n\n' "$bold$cyan" "$reset" "$dim" "$reset" >/dev/tty printf ' 0 Ink only\n 1 text\n 2 filesystem\n 3 web\n 4 git\n 5 all tools\n\nSelect sets [0]: ' >/dev/tty IFS= read -r answer &2; exit 2 ;; esac done fi work=${TMPDIR:-/tmp}/ink-install.$$ trap 'rm -rf "$work"' EXIT HUP INT TERM mkdir -p "$work" release_path() { version=$1 kind=$2 if [ "$version" = main ]; then printf 'channels/main/%s' "$kind" else printf 'releases/%s/%s' "$version" "$kind" fi } toolset_url=$RELEASE_BASE/$(release_path "$TOOLSET_VERSION" toolset) manifest_name=ink-toolset-$TOOLSET_VERSION-$TARGET.manifest.tsv if [ -n "$SETS$TOOLS" ] || [ "$LIST_ONLY" -eq 1 ]; then fetch "$toolset_url/$manifest_name" "$work/$manifest_name" fetch "$toolset_url/ink-toolset-SHA256SUMS" "$work/toolset-SHA256SUMS" expected=$(awk -v file="$manifest_name" '$2 == file || $2 == "*" file {print $1; exit}' "$work/toolset-SHA256SUMS") [ -n "$expected" ] && [ "$(digest "$work/$manifest_name")" = "$expected" ] || { echo 'install: toolset manifest checksum mismatch' >&2; exit 1; } fi if [ "$LIST_ONLY" -eq 1 ]; then cat "$work/$manifest_name" exit 0 fi selected=$work/selected.tsv : > "$selected" if [ -n "$SETS" ]; then awk -F '\t' -v sets="$SETS" 'NR > 1 { n=split(sets,a," "); for(i=1;i<=n;i++) if(a[i]=="all" || $4==a[i]) {print; break} }' "$work/$manifest_name" >> "$selected" fi for tool in $TOOLS; do row=$(awk -F '\t' -v tool="$tool" 'NR > 1 && $3==tool {print; exit}' "$work/$manifest_name") [ -n "$row" ] || { echo "install: unknown tool: $tool" >&2; exit 2; } printf '%s\n' "$row" >> "$selected" done sort -u "$selected" -o "$selected" say "${cyan}Ink installer${reset}" say " core $INK_VERSION" say " tools $TOOLSET_VERSION ($(wc -l < "$selected" | tr -d ' ') selected)" say " target $TARGET" say " location $PREFIX/bin" if [ "$ASSUME_YES" -eq 0 ] && [ "$TTY" -eq 1 ]; then printf 'Install? [y/N] ' >/dev/tty IFS= read -r answer &2; exit 1; } while IFS="$(printf '\t')" read -r source_repo source_commit name set target bytes expected asset; do [ -n "$name" ] || continue fetch "$toolset_url/$asset" "$work/$asset" [ "$(digest "$work/$asset")" = "$expected" ] || { echo "install: checksum mismatch: $asset" >&2; exit 1; } done < "$selected" mkdir -p "$PREFIX/bin" chmod 755 "$work/$ink_asset" mv "$work/$ink_asset" "$PREFIX/bin/ink" while IFS="$(printf '\t')" read -r source_repo source_commit name set target bytes expected asset; do [ -n "$name" ] || continue chmod 755 "$work/$asset" mv "$work/$asset" "$PREFIX/bin/$name" done < "$selected" say "${bold}Installed.${reset} Executables only; policy and PATH were not changed." say "Run: $PREFIX/bin/ink --help"