From 9e6cb03b8a9dbbe3a57c80a39c00280df5d781c6 Mon Sep 17 00:00:00 2001 From: Samuel O'Neal Date: Wed, 2 Sep 2026 11:37:29 -0600 Subject: [PATCH] updated makefiles and added dependency installation for ODIN --- AGENTS.md | 5 +- Makefile | 23 +++++-- api/Makefile | 5 +- gui/Makefile | 11 ++- gui/scripts/add_dep.sh | 151 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 186 insertions(+), 9 deletions(-) create mode 100755 gui/scripts/add_dep.sh diff --git a/AGENTS.md b/AGENTS.md index a0ab0c2..1e11b77 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,10 +4,11 @@ Odin + raylib interactive 3D universe map (DESI data), a Rust + axum API, with G ## Commands The root `Makefile` is a lean delegator: base commands (`run`, `build`, `test`, ...) forward into the per-project Makefiles in `gui/`, `api/`, and `infra/`. Project-specific commands are reached via `make -C `. -- Run: `make run`; Build: `make build` (`-> bin/desi_explorer`); Web: `make build-web` (Emscripten/WASM -> `build/web`); Debug: `make build-debug` +- Run: `make run`; Build: `make build` (`-> bin/desi_explorer`); Web: `make build-web` (Emscripten/WASM -> `build/web`); Debug: `make build-debug`; Run Web: `make run-web` (WASM build + API server) - Test: `make test` (runs `odin test` in gui/, `cargo test` in api/, `go test` in infra/); Clean: `make clean`; Format: `make fmt` - API: `make api-run`, `make api-test`, `make api-check` (thin passthroughs to `make -C api `) -- Setup: `make setup` (submodules + `go mod tidy` in infra/) +- Setup: `make setup` (submodules + gui lib/local + `cargo fetch` + `go mod tidy` in infra/) +- Add dependency: `make -C gui add-dep DEP=` (searches Codeberg/GitHub/GitLab, adds as submodule to `gui/lib/local/`) - Infra: `make infra-preview`, `make infra-up`, `make infra-down`, `make infra-refresh` (passthroughs to `make -C infra `) - Renovate: `make renovate-validate` (npx renovate-config-validator) - Format Odin with `odinfmt` (config `gui/odinfmt.json`: tabs, width 80); format Rust with `cargo fmt` diff --git a/Makefile b/Makefile index efc5735..f4c2f0b 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ GUI := gui API := api INFRA := infra -.PHONY: help setup run build build-debug build-web test clean fmt \ +.PHONY: help setup run run-web build build-debug build-web test clean fmt \ renovate-validate help: ## List available targets @@ -23,24 +23,37 @@ help: ## List available targets ## ---- Setup --------------------------------------------------------------- -setup: ## Pull submodules + tidy Go deps +setup: ## Setup all sub-projects (submodules, gui deps, api deps, infra deps) @git submodule update --init --recursive + @$(MAKE) -C $(GUI) setup + @$(MAKE) -C $(API) setup @$(MAKE) -C $(INFRA) tidy ## ---- Renderer (Odin) ----------------------------------------------------- run: ## Run the native app (gui/) - @$(MAKE) -C $(GUI) run + @$(MAKE) -C $(API) run & api_pid=$$!; \ + trap 'kill $$api_pid 2>/dev/null' INT TERM EXIT; \ + $(MAKE) -C $(GUI) run; \ + kill $$api_pid 2>/dev/null -build: ## Release build -> bin/desi_explorer (gui/) +build: ## Release build (gui/ + api/) @$(MAKE) -C $(GUI) build + @$(MAKE) -C $(API) build -build-debug: ## Debug build -> bin/desi_explorer (gui/) +build-debug: ## Debug build (gui/ + api/) @$(MAKE) -C $(GUI) build-debug + @$(MAKE) -C $(API) build build-web: ## WebAssembly build -> build/web (gui/, needs emscripten) @$(MAKE) -C $(GUI) build-web +run-web: ## Start WASM build + API server for web dev + @$(MAKE) -C $(API) run & api_pid=$$!; \ + trap 'kill $$api_pid 2>/dev/null' INT TERM EXIT; \ + $(MAKE) -C $(GUI) build-web; \ + kill $$api_pid 2>/dev/null + ## ---- Aggregates ---------------------------------------------------------- test: ## Test all projects (odin + cargo + go) diff --git a/api/Makefile b/api/Makefile index 87ad652..36d94a7 100644 --- a/api/Makefile +++ b/api/Makefile @@ -5,12 +5,15 @@ CARGO ?= cargo -.PHONY: help run build test check fmt clean +.PHONY: help setup run build test check fmt clean help: ## List available targets @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}' +setup: ## Fetch dependencies + $(CARGO) fetch + run: ## Run the API server (dev) $(CARGO) run diff --git a/gui/Makefile b/gui/Makefile index c90d7de..84951f2 100644 --- a/gui/Makefile +++ b/gui/Makefile @@ -14,12 +14,21 @@ BINARY := $(BIN)/desi_explorer ODIN_FLAGS := -collection:lib=lib/local WASM_DEFINE := RAYLIB_WASM_LIB=env.o -.PHONY: help run build build-debug build-web test clean fmt +.PHONY: help setup add-dep run build build-debug build-web test clean fmt help: ## List available targets @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}' +setup: ## Prepare build environment (lib/local) + @mkdir -p lib/local + +add-dep: ## Add a dependency from Codeberg/GitHub/GitLab as a submodule (DEP=name) +ifndef DEP + $(error Usage: make add-dep DEP=) +endif + @scripts/add_dep.sh "$(DEP)" + run: ## Run the native app @mkdir -p lib/local $(ODIN) run src $(ODIN_FLAGS) diff --git a/gui/scripts/add_dep.sh b/gui/scripts/add_dep.sh new file mode 100755 index 0000000..799a0eb --- /dev/null +++ b/gui/scripts/add_dep.sh @@ -0,0 +1,151 @@ +#!/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