#!/usr/bin/env bash # Search Codeberg, GitHub, and GitLab for an Odin dependency and add it # as a git submodule under gui/lib/local/. Run via `make -C gui add-dep`. # # Usage: add_dep.sh # # Requires: curl, jq # Optional: fzf (interactive selector; falls back to numbered menu) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" GUI_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" ROOT_DIR="$(cd "${GUI_DIR}/.." && pwd)" LIB_DIR="${GUI_DIR}/lib/local" # ---- Args ------------------------------------------------------------------ if [[ $# -lt 1 ]]; then echo "Usage: $0 " >&2 exit 1 fi QUERY="$1" # ---- Helpers --------------------------------------------------------------- TMPDIR_WORK="$(mktemp -d)" trap 'rm -rf "${TMPDIR_WORK}"' EXIT RESULTS="${TMPDIR_WORK}/results" touch "$RESULTS" search_codeberg() { local url url="https://codeberg.org/api/v1/repos/search?q=$(printf '%s' "$QUERY" | jq -sRr @uri)&limit=10" local data data=$(curl -sf --max-time 10 "$url" 2>/dev/null) || return 0 printf '%s' "$data" | jq -r '.data[]? | [.owner.username, .name, "codeberg", (.description // "")] | @tsv' 2>/dev/null \ | while IFS=$'\t' read -r owner name _ desc; do printf '%s\t%s\t%s\t%s\n' "$owner" "$name" "codeberg" "$desc" done >> "$RESULTS" } search_github() { local url url="https://api.github.com/search/repositories?q=$(printf '%s' "$QUERY" | jq -sRr @uri)&per_page=10" local data data=$(curl -sf --max-time 10 -H "Accept: application/vnd.github+json" "$url" 2>/dev/null) || return 0 printf '%s' "$data" | jq -r '.items[]? | [.owner.login, .name, "github", (.description // "")] | @tsv' 2>/dev/null \ | while IFS=$'\t' read -r owner name _ desc; do printf '%s\t%s\t%s\t%s\n' "$owner" "$name" "github" "$desc" done >> "$RESULTS" } search_gitlab() { local url url="https://gitlab.com/api/v4/projects?search=$(printf '%s' "$QUERY" | jq -sRr @uri)&per_page=10" local data data=$(curl -sf --max-time 10 "$url" 2>/dev/null) || return 0 printf '%s' "$data" | jq -r '.[]? | [.namespace.full_path, .name, "gitlab", (.description // "")] | @tsv' 2>/dev/null \ | while IFS=$'\t' read -r owner name _ desc; do printf '%s\t%s\t%s\t%s\n' "$owner" "$name" "gitlab" "$desc" done >> "$RESULTS" } # ---- Search (Codeberg → GitHub → GitLab) ----------------------------------- echo "Searching for '${QUERY}'..." >&2 search_codeberg search_github search_gitlab TOTAL=$(wc -l < "$RESULTS" | tr -d ' ') if [[ "$TOTAL" -eq 0 ]]; then echo "No results found for '${QUERY}' on Codeberg, GitHub, or GitLab." >&2 exit 1 fi # ---- Select ---------------------------------------------------------------- SELECTED="" if command -v fzf >/dev/null 2>&1; then SELECTED=$(while IFS=$'\t' read -r owner name forge desc; do printf '%-20s %-30s [%s] %s\n' "$owner" "$name" "$forge" "$desc" done < "$RESULTS" \ | fzf --prompt="Select dependency > " \ --header="${TOTAL} result(s) for '${QUERY}'" \ --height=40% \ --reverse) else echo "" >&2 echo "Results for '${QUERY}':" >&2 echo "---" >&2 IDX=0 while IFS=$'\t' read -r owner name forge desc; do IDX=$((IDX + 1)) printf ' %2d) %-20s %-30s [%s] %s\n' "$IDX" "$owner" "$name" "$forge" "$desc" >&2 done < "$RESULTS" echo "---" >&2 printf 'Select [1-%d]: ' "$TOTAL" >&2 read -r CHOICE if ! [[ "$CHOICE" =~ ^[0-9]+$ ]] || [[ "$CHOICE" -lt 1 ]] || [[ "$CHOICE" -gt "$TOTAL" ]]; then echo "Invalid selection." >&2 exit 1 fi SELECTED=$(sed -n "${CHOICE}p" "$RESULTS") fi if [[ -z "$SELECTED" ]]; then echo "No selection made." >&2 exit 1 fi # ---- Parse & Add ----------------------------------------------------------- ENTRYowner=$(printf '%s' "$SELECTED" | cut -f1) ENTRYname=$(printf '%s' "$SELECTED" | cut -f2) ENTRYforge=$(printf '%s' "$SELECTED" | cut -f3) case "$ENTRYforge" in codeberg) URL="https://codeberg.org/${ENTRYowner}/${ENTRYname}.git" ;; github) URL="https://github.com/${ENTRYowner}/${ENTRYname}.git" ;; gitlab) URL="https://gitlab.com/${ENTRYowner}/${ENTRYname}.git" ;; esac MODULE_PATH="lib/local/${ENTRYname}" # Check for name collisions (same dir, different repo). if [[ -d "${GUI_DIR}/${MODULE_PATH}" ]]; then EXISTING=$(git -C "${ROOT_DIR}" config --get "submodule.${MODULE_PATH}.url" 2>/dev/null || true) if [[ -n "$EXISTING" ]]; then echo "Dependency '${ENTRYname}' is already installed from ${EXISTING}." >&2 exit 1 fi echo "Error: '${MODULE_PATH}' already exists but is not a submodule." >&2 exit 1 fi echo "Adding ${ENTRYowner}/${ENTRYname} (${ENTRYforge}) as submodule..." >&2 mkdir -p "${LIB_DIR}" git -C "${ROOT_DIR}" submodule add "$URL" "$MODULE_PATH" echo "" >&2 echo "Done — added ${ENTRYowner}/${ENTRYname} from ${ENTRYforge}" >&2 echo " path: ${MODULE_PATH}" >&2 echo " The Makefile's -collection:lib=lib/local will pick it up automatically." >&2