From 0502d52b50d9fd96e5523e8ce8158da4aa112326 Mon Sep 17 00:00:00 2001 From: fmar Date: Wed, 18 Mar 2026 20:51:17 +0100 Subject: [PATCH 1/4] feat: install.sh --- install.sh | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100755 install.sh diff --git a/install.sh b/install.sh new file mode 100755 index 000000000..4df6b8656 --- /dev/null +++ b/install.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +set -euo pipefail + +# --- config --- +REPO="relaystr/ndk" +BIN_NAME="ndk" +INSTALL_DIR="${NDK_INSTALL_DIR:-$HOME/.local/bin}" +VERSION="${NDK_VERSION:-latest}" + +# --- helpers --- +info() { echo -e "\033[1;34minfo\033[0m $*"; } +ok() { echo -e "\033[1;32m ok \033[0m $*"; } +err() { echo -e "\033[1;31merror\033[0m $*" >&2; exit 1; } + +need() { + command -v "$1" &>/dev/null || err "required tool not found: $1" +} + +# --- detect platform --- +detect_target() { + local os arch + os="$(uname -s)" + arch="$(uname -m)" + + case "$os" in + Linux) os="linux" ;; + Darwin) os="macos" ;; + *) err "unsupported OS: $os" ;; + esac + + case "$arch" in + x86_64) arch="x64" ;; + aarch64 | arm64) arch="arm64" ;; + *) err "unsupported architecture: $arch" ;; + esac + + echo "${os}-${arch}" +} + +# --- resolve version --- +resolve_version() { + if [ "$VERSION" = "latest" ]; then + need curl + VERSION=$( + curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" \ + | grep '"tag_name"' \ + | sed -E 's/.*"v?([^"]+)".*/\1/' + ) + [ -n "$VERSION" ] || err "could not determine latest version" + fi + echo "$VERSION" +} + +# --- main --- +main() { + need curl + + local target version download_url tmp_file + + target="$(detect_target)" + version="$(resolve_version)" + download_url="https://github.com/${REPO}/releases/download/v${version}/${BIN_NAME}-${target}" + + info "installing ${BIN_NAME} ${version} (${target})" + info "from ${download_url}" + + # download to temp file + tmp_file="$(mktemp)" + trap 'rm -f "$tmp_file"' EXIT + + curl -fsSL --progress-bar "$download_url" -o "$tmp_file" \ + || err "download failed — check that release asset exists: ${download_url}" + + chmod +x "$tmp_file" + + # quick sanity check + "$tmp_file" --version &>/dev/null \ + || err "downloaded binary failed to run — wrong platform?" + + # install + mkdir -p "$INSTALL_DIR" + mv "$tmp_file" "${INSTALL_DIR}/${BIN_NAME}" + + ok "${BIN_NAME} installed to ${INSTALL_DIR}/${BIN_NAME}" + + # PATH hint + if ! echo "$PATH" | grep -q "$INSTALL_DIR"; then + echo "" + echo " Add this to your shell profile (~/.bashrc / ~/.zshrc):" + echo "" + echo " export PATH=\"\$PATH:${INSTALL_DIR}\"" + echo "" + fi +} + +main "$@" \ No newline at end of file From 6bd944d50809858b7848bc921ac02407264dc909 Mon Sep 17 00:00:00 2001 From: fmar Date: Thu, 19 Mar 2026 00:42:38 +0100 Subject: [PATCH 2/4] fix install.sh --- install.sh | 269 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 243 insertions(+), 26 deletions(-) diff --git a/install.sh b/install.sh index 4df6b8656..292f0d45d 100755 --- a/install.sh +++ b/install.sh @@ -6,6 +6,11 @@ REPO="relaystr/ndk" BIN_NAME="ndk" INSTALL_DIR="${NDK_INSTALL_DIR:-$HOME/.local/bin}" VERSION="${NDK_VERSION:-latest}" +PRERELEASE="${NDK_PRERELEASE:-0}" +INSTALL_MODE="${NDK_INSTALL_MODE:-user}" +USER_LIB_DIR="${NDK_USER_LIB_DIR:-$HOME/.local/lib}" +SYSTEM_BIN_DIR="${NDK_SYSTEM_BIN_DIR:-/usr/bin}" +SYSTEM_LIB_DIR="${NDK_SYSTEM_LIB_DIR:-/usr/lib}" # --- helpers --- info() { echo -e "\033[1;34minfo\033[0m $*"; } @@ -16,6 +21,13 @@ need() { command -v "$1" &>/dev/null || err "required tool not found: $1" } +is_truthy() { + case "$1" in + 1 | true | TRUE | yes | YES) return 0 ;; + *) return 1 ;; + esac +} + # --- detect platform --- detect_target() { local os arch @@ -37,51 +49,248 @@ detect_target() { echo "${os}-${arch}" } -# --- resolve version --- -resolve_version() { - if [ "$VERSION" = "latest" ]; then +# --- resolve version/tag --- +resolve_tag() { + local use_prerelease="0" + local tag + + case "$VERSION" in + latest-pre | latest-prerelease) use_prerelease="1" ;; + latest) is_truthy "$PRERELEASE" && use_prerelease="1" ;; + esac + + if [ "$VERSION" = "latest" ] || [ "$VERSION" = "latest-pre" ] || [ "$VERSION" = "latest-prerelease" ]; then need curl - VERSION=$( - curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" \ - | grep '"tag_name"' \ - | sed -E 's/.*"v?([^"]+)".*/\1/' - ) - [ -n "$VERSION" ] || err "could not determine latest version" + + if [ "$use_prerelease" = "1" ]; then + tag=$( + curl -fsSL "https://api.github.com/repos/${REPO}/releases" \ + | awk ' + BEGIN { tag=""; draft=0; found="" } + /"tag_name":/ { + if (tag == "") { + if (match($0, /"tag_name":[[:space:]]*"([^"]+)"/, m)) { + tag=m[1] + } + } + } + /"draft":/ { + draft = ($0 ~ /true/) ? 1 : 0 + } + /"prerelease":/ { + if ($0 ~ /true/ && draft == 0 && tag != "") { + if (found == "") { + found = tag + } + } + tag="" + draft=0 + } + END { + if (found != "") { + print found + } + } + ' + ) + [ -n "$tag" ] || err "could not determine latest pre-release version" + else + tag=$( + curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" \ + | grep '"tag_name"' \ + | sed -E 's/.*"([^"]+)".*/\1/' + ) + [ -n "$tag" ] || err "could not determine latest version" + fi + else + if [[ "$VERSION" =~ ^v[0-9] ]]; then + tag="$VERSION" + else + tag="v${VERSION}" + fi fi - echo "$VERSION" + + echo "$tag" +} + +get_release_json() { + local tag="$1" + + if curl -fsSL "https://api.github.com/repos/${REPO}/releases/tags/${tag}"; then + return 0 + fi + + if [[ "$tag" =~ ^v ]]; then + curl -fsSL "https://api.github.com/repos/${REPO}/releases/tags/${tag#v}" \ + || err "could not fetch release metadata for tag: ${tag}" + else + curl -fsSL "https://api.github.com/repos/${REPO}/releases/tags/v${tag}" \ + || err "could not fetch release metadata for tag: ${tag}" + fi +} + +resolve_release_asset() { + local release_json="$1" + local target="$2" + + printf "%s\n" "$release_json" | awk -v target="$target" ' + BEGIN { name="" } + /"name":/ { + if (name == "" && match($0, /"name":[[:space:]]*"([^"]+)"/, m)) { + name = m[1] + } + next + } + /"browser_download_url":/ { + if (name != "" && match($0, /"browser_download_url":[[:space:]]*"([^"]+)"/, u)) { + if (name ~ ("-" target "\\.(tar\\.gz|tgz)$")) { + print name "\t" u[1] + exit + } + } + name = "" + } + ' +} + +check_sudo_needed() { + local path parent + for path in "$@"; do + if [ -e "$path" ]; then + [ -w "$path" ] || return 0 + else + parent="$(dirname "$path")" + [ -w "$parent" ] || return 0 + fi + done + return 1 +} + +run_priv() { + if [ "${USE_SUDO:-0}" = "1" ]; then + sudo "$@" + else + "$@" + fi +} + +parse_args() { + while [ $# -gt 0 ]; do + case "$1" in + --system) INSTALL_MODE="system" ;; + --user) INSTALL_MODE="user" ;; + -h | --help) + cat </dev/null; then + if ! LD_LIBRARY_PATH="${extract_dir}/lib:${LD_LIBRARY_PATH:-}" "$bin_src" --help &>/dev/null; then + LD_LIBRARY_PATH="${extract_dir}/lib:${LD_LIBRARY_PATH:-}" "$bin_src" &>/dev/null \ + || err "downloaded binary failed to run — wrong platform?" + fi + fi + + if [ "${target#linux-}" != "$target" ] && [ "$INSTALL_MODE" = "system" ]; then + USE_SUDO="0" + if [ "$(id -u)" -ne 0 ] && check_sudo_needed "$SYSTEM_BIN_DIR" "$SYSTEM_LIB_DIR"; then + need sudo + USE_SUDO="1" + fi + + run_priv mkdir -p "$SYSTEM_BIN_DIR" "$SYSTEM_LIB_DIR" + run_priv install -m 0755 "$bin_src" "${SYSTEM_BIN_DIR}/${BIN_NAME}" - # quick sanity check - "$tmp_file" --version &>/dev/null \ - || err "downloaded binary failed to run — wrong platform?" + lib_installed="0" + for lib_src in "${extract_dir}/lib"/*; do + [ -f "$lib_src" ] || continue + run_priv install -m 0644 "$lib_src" "${SYSTEM_LIB_DIR}/$(basename "$lib_src")" + lib_installed="1" + done - # install + [ "$lib_installed" = "1" ] || err "archive does not contain shared libraries under lib/" + run_priv ldconfig || true + + ok "${BIN_NAME} installed to ${SYSTEM_BIN_DIR}/${BIN_NAME}" + ok "shared libraries installed to ${SYSTEM_LIB_DIR}" + return + fi + + # user install (default) mkdir -p "$INSTALL_DIR" - mv "$tmp_file" "${INSTALL_DIR}/${BIN_NAME}" + mkdir -p "$USER_LIB_DIR" + install -m 0755 "$bin_src" "${INSTALL_DIR}/${BIN_NAME}" + + lib_installed="0" + for lib_src in "${extract_dir}/lib"/*; do + [ -f "$lib_src" ] || continue + install -m 0644 "$lib_src" "${USER_LIB_DIR}/$(basename "$lib_src")" + lib_installed="1" + done + + [ "$lib_installed" = "1" ] || err "archive does not contain shared libraries under lib/" ok "${BIN_NAME} installed to ${INSTALL_DIR}/${BIN_NAME}" + ok "shared libraries installed to ${USER_LIB_DIR}" # PATH hint if ! echo "$PATH" | grep -q "$INSTALL_DIR"; then @@ -91,6 +300,14 @@ main() { echo " export PATH=\"\$PATH:${INSTALL_DIR}\"" echo "" fi + + if ! echo "${LD_LIBRARY_PATH:-}" | grep -q "$USER_LIB_DIR"; then + echo "" + echo " Add this to your shell profile (~/.bashrc / ~/.zshrc):" + echo "" + echo " export LD_LIBRARY_PATH=\"${USER_LIB_DIR}:\$LD_LIBRARY_PATH\"" + echo "" + fi } -main "$@" \ No newline at end of file +main "$@" From a2ee325f2e8026d696037f20146c673f33c2c986 Mon Sep 17 00:00:00 2001 From: fmar Date: Thu, 19 Mar 2026 00:54:50 +0100 Subject: [PATCH 3/4] fix install.sh --- install.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/install.sh b/install.sh index 292f0d45d..84aa0fd75 100755 --- a/install.sh +++ b/install.sh @@ -301,13 +301,6 @@ main() { echo "" fi - if ! echo "${LD_LIBRARY_PATH:-}" | grep -q "$USER_LIB_DIR"; then - echo "" - echo " Add this to your shell profile (~/.bashrc / ~/.zshrc):" - echo "" - echo " export LD_LIBRARY_PATH=\"${USER_LIB_DIR}:\$LD_LIBRARY_PATH\"" - echo "" - fi } main "$@" From 84922ecb08aaa05fc796366aef195b6e3bb6721a Mon Sep 17 00:00:00 2001 From: fmar Date: Wed, 25 Mar 2026 15:21:03 +0100 Subject: [PATCH 4/4] fix awk on macos --- install.sh | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/install.sh b/install.sh index 84aa0fd75..049c5bb4d 100755 --- a/install.sh +++ b/install.sh @@ -69,9 +69,9 @@ resolve_tag() { BEGIN { tag=""; draft=0; found="" } /"tag_name":/ { if (tag == "") { - if (match($0, /"tag_name":[[:space:]]*"([^"]+)"/, m)) { - tag=m[1] - } + tag = $0 + sub(/^.*"tag_name":[[:space:]]*"/, "", tag) + sub(/".*$/, "", tag) } } /"draft":/ { @@ -136,15 +136,20 @@ resolve_release_asset() { printf "%s\n" "$release_json" | awk -v target="$target" ' BEGIN { name="" } /"name":/ { - if (name == "" && match($0, /"name":[[:space:]]*"([^"]+)"/, m)) { - name = m[1] + if (name == "") { + name = $0 + sub(/^.*"name":[[:space:]]*"/, "", name) + sub(/".*$/, "", name) } next } /"browser_download_url":/ { - if (name != "" && match($0, /"browser_download_url":[[:space:]]*"([^"]+)"/, u)) { + if (name != "") { + url = $0 + sub(/^.*"browser_download_url":[[:space:]]*"/, "", url) + sub(/".*$/, "", url) if (name ~ ("-" target "\\.(tar\\.gz|tgz)$")) { - print name "\t" u[1] + print name "\t" url exit } }