176 lines
7.3 KiB
Bash
Executable File
176 lines
7.3 KiB
Bash
Executable File
#!/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 </dev/tty || answer=0
|
|
for choice in $answer; do
|
|
case $choice in 0|'') ;; 1) SETS=$(append_word "$SETS" text) ;; 2) SETS=$(append_word "$SETS" filesystem) ;; 3) SETS=$(append_word "$SETS" web) ;; 4) SETS=$(append_word "$SETS" git) ;; 5) SETS=all ;; *) echo "install: invalid selection: $choice" >&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 </dev/tty || answer=n
|
|
case $answer in y|Y|yes|YES) ;; *) say 'install: cancelled'; exit 1 ;; esac
|
|
fi
|
|
|
|
ink_asset=ink-$TARGET
|
|
ink_url=$RELEASE_BASE/$(release_path "$INK_VERSION" ink)
|
|
fetch "$ink_url/$ink_asset" "$work/$ink_asset"
|
|
fetch "$ink_url/ink-SHA256SUMS" "$work/ink-SHA256SUMS"
|
|
expected=$(awk -v file="$ink_asset" '$2 == file || $2 == "*" file {print $1; exit}' "$work/ink-SHA256SUMS")
|
|
[ -n "$expected" ] && [ "$(digest "$work/$ink_asset")" = "$expected" ] || { echo 'install: Ink checksum mismatch' >&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"
|