297 lines
9.7 KiB
Bash
Executable File
297 lines
9.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||
|
||
set -euo pipefail
|
||
|
||
IQ_RESET='\033[0m'
|
||
IQ_BOLD='\033[1m'
|
||
IQ_MUTED='\033[38;5;245m'
|
||
IQ_RED='\033[38;5;204m'
|
||
IQ_GREEN='\033[38;5;120m'
|
||
IQ_CYAN='\033[38;2;38;145;184m'
|
||
IQ_BLUE='\033[38;2;51;112;176m'
|
||
IQ_PURPLE='\033[38;5;141m'
|
||
IQ_PINK='\033[38;5;211m'
|
||
IQ_ROOT=''
|
||
IQ_DRY=0
|
||
IQ_NO_VERIFY=0
|
||
IQ_LOG_FILE=''
|
||
IQ_REBOOT=0
|
||
|
||
if [[ ! -t 1 || "${NO_COLOR:-}" != '' ]]; then
|
||
IQ_RESET='' IQ_BOLD='' IQ_MUTED='' IQ_RED='' IQ_GREEN='' IQ_CYAN='' IQ_BLUE='' IQ_PURPLE='' IQ_PINK=''
|
||
fi
|
||
|
||
iq_line() {
|
||
printf '%b%s%b\n' "$IQ_CYAN" '━━━━━━━━━━━━' "$IQ_RESET"
|
||
}
|
||
|
||
iq_title() {
|
||
printf '%bI%b%bQ%b%b.%b%bP%b%bi%b%bl%b%bo%b%bt%b %b%s%b\n' "$IQ_CYAN" "$IQ_RESET" "$IQ_BLUE" "$IQ_RESET" "$IQ_PURPLE" "$IQ_RESET" "$IQ_PINK" "$IQ_RESET" "$IQ_PURPLE" "$IQ_RESET" "$IQ_BLUE" "$IQ_RESET" "$IQ_CYAN" "$IQ_RESET" "$IQ_CYAN" "$IQ_RESET" "$IQ_MUTED" "$1" "$IQ_RESET"
|
||
}
|
||
|
||
iq_ok() {
|
||
printf ' %b●%b %s\n' "$IQ_GREEN" "$IQ_RESET" "$1"
|
||
}
|
||
|
||
iq_fail() {
|
||
printf ' %b●%b %s\n' "$IQ_RED" "$IQ_RESET" "$1" >&2
|
||
}
|
||
|
||
iq_note() {
|
||
printf ' %b●%b %s\n' "$IQ_CYAN" "$IQ_RESET" "$1"
|
||
}
|
||
|
||
iq_find_root() {
|
||
local candidate="${IQ_ROOT:-$PWD}"
|
||
while [[ "$candidate" != / ]]; do
|
||
if [[ ( -f "$candidate/launch_iqpilot.sh" || -f "$candidate/launch_openpilot.sh" ) && -d "$candidate/iqpilot" ]]; then
|
||
IQ_ROOT="$candidate"
|
||
return 0
|
||
fi
|
||
candidate="$(cd "$candidate/.." && pwd)"
|
||
done
|
||
for candidate in "$HOME/iqpilot" "$HOME/openpilot" /data/iqpilot /data/openpilot; do
|
||
if [[ ( -f "$candidate/launch_iqpilot.sh" || -f "$candidate/launch_openpilot.sh" ) && -d "$candidate/iqpilot" ]]; then
|
||
IQ_ROOT="$candidate"
|
||
return 0
|
||
fi
|
||
done
|
||
return 1
|
||
}
|
||
|
||
iq_require_root() {
|
||
if ! iq_find_root; then
|
||
iq_fail 'IQ.Pilot checkout not found. Run this inside the checkout or use --dir PATH.'
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
iq_run() {
|
||
local rendered
|
||
printf -v rendered '%q ' "$@"
|
||
rendered="${rendered% }"
|
||
printf '%b›%b %b%s%b\n' "$IQ_PINK" "$IQ_RESET" "$IQ_MUTED" "$rendered" "$IQ_RESET"
|
||
[[ "$IQ_DRY" = 1 ]] || "$@"
|
||
}
|
||
|
||
iq_check() {
|
||
iq_title 'environment check'
|
||
iq_require_root
|
||
iq_ok "checkout $IQ_ROOT"
|
||
command -v git >/dev/null 2>&1 || { iq_fail 'git is not installed'; return 1; }
|
||
iq_ok "git $(git --version | sed 's/git version //')"
|
||
command -v python3 >/dev/null 2>&1 || { iq_fail 'python3 is not installed'; return 1; }
|
||
iq_ok "python $(python3 --version | sed 's/Python //')"
|
||
if [[ -x "$IQ_ROOT/.venv/bin/python3" ]]; then
|
||
iq_ok 'venv ready'
|
||
else
|
||
iq_note 'venv not created yet — run iq setup'
|
||
fi
|
||
}
|
||
|
||
iq_install() {
|
||
local shell_name rc_file iq_script command
|
||
shell_name="$(basename "${SHELL:-bash}")"
|
||
rc_file="$HOME/.${shell_name}rc"
|
||
[[ "$(uname)" = Darwin && "$shell_name" = bash ]] && rc_file="$HOME/.bash_profile"
|
||
iq_script="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/iq.sh"
|
||
command="alias iq='${iq_script} \"\$@\"'"
|
||
touch "$rc_file"
|
||
grep -Fqx "$command" "$rc_file" 2>/dev/null || printf '\n%s\n' "$command" >> "$rc_file"
|
||
iq_title 'command installed'
|
||
iq_ok "restart your shell, then use iq ${rc_file}"
|
||
}
|
||
|
||
iq_setup() {
|
||
local script
|
||
iq_require_root
|
||
if [[ -f /AGNOS ]]; then
|
||
iq_title 'setup'
|
||
iq_note 'IQ.OS manages system dependencies and the base Python runtime'
|
||
iq_note 'use iq pkg to synchronize private packages when needed'
|
||
iq_check
|
||
return 0
|
||
fi
|
||
case "$OSTYPE" in
|
||
linux-gnu*) script="$IQ_ROOT/iqpilot/tools/ubuntu_setup.sh" ;;
|
||
darwin*) script="$IQ_ROOT/iqpilot/tools/mac_setup.sh" ;;
|
||
*) iq_fail "unsupported platform: $OSTYPE"; return 1 ;;
|
||
esac
|
||
iq_title 'setup'
|
||
iq_run "$script"
|
||
if command -v git-lfs >/dev/null 2>&1; then
|
||
iq_run git -C "$IQ_ROOT" lfs pull
|
||
fi
|
||
iq_ok 'setup complete'
|
||
}
|
||
|
||
iq_venv() {
|
||
iq_require_root
|
||
[[ -f "$IQ_ROOT/.venv/bin/activate" ]] || { iq_fail 'venv not found — run iq setup first'; return 1; }
|
||
case "$(basename "${SHELL:-bash}")" in
|
||
zsh) ZDOTDIR="$(mktemp -d)"; printf 'source %q\nsource %q\n' "$HOME/.zshrc" "$IQ_ROOT/.venv/bin/activate" > "$ZDOTDIR/.zshrc"; zsh ;;
|
||
*) bash --rcfile <(printf 'source %q\nsource %q\n' "$HOME/.bashrc" "$IQ_ROOT/.venv/bin/activate") ;;
|
||
esac
|
||
}
|
||
|
||
iq_build() {
|
||
iq_require_root
|
||
if [[ -f /AGNOS ]]; then
|
||
iq_run "$IQ_ROOT/iqpilot/system/manager/build.py"
|
||
else
|
||
(cd "$IQ_ROOT" && iq_run scons "$@")
|
||
fi
|
||
}
|
||
|
||
iq_quality() {
|
||
iq_require_root
|
||
(cd "$IQ_ROOT" && iq_run scripts/lint/lint.sh "$@")
|
||
}
|
||
|
||
iq_pkg() {
|
||
iq_require_root
|
||
# the venv python has the component packages (iqdbc etc.); bare python3 does not
|
||
local py=python3
|
||
[[ -x "$IQ_ROOT/.venv/bin/python3" ]] && py="$IQ_ROOT/.venv/bin/python3"
|
||
(cd "$IQ_ROOT" && iq_run "$py" iqpilot/tools/scripts/setup_private_packages.py "$@")
|
||
if [[ -f "$IQ_ROOT/artifacts/runtime/ensure_private_installed.sh" ]]; then
|
||
(cd "$IQ_ROOT" && iq_run bash artifacts/runtime/ensure_private_installed.sh)
|
||
fi
|
||
}
|
||
|
||
iq_update() {
|
||
local fast=0
|
||
iq_require_root
|
||
if [[ $# -gt 0 ]]; then
|
||
[[ $# = 1 && "$1" = f ]] || { iq_fail 'usage: iq update [f] [r]'; return 1; }
|
||
fast=1
|
||
fi
|
||
iq_title 'updating IQ.Pilot'
|
||
iq_run git -C "$IQ_ROOT" pull
|
||
iq_pkg
|
||
[[ "$fast" = 1 ]] && iq_fast_restart
|
||
}
|
||
|
||
iq_status() {
|
||
local branch commit tree
|
||
iq_require_root
|
||
branch="$(git -C "$IQ_ROOT" branch --show-current 2>/dev/null || printf detached)"
|
||
commit="$(git -C "$IQ_ROOT" rev-parse --short HEAD 2>/dev/null || printf unknown)"
|
||
tree=clean
|
||
[[ -n "$(git -C "$IQ_ROOT" status --porcelain 2>/dev/null)" ]] && tree=modified
|
||
iq_title 'status'
|
||
iq_note "root $IQ_ROOT"
|
||
iq_note "branch $branch"
|
||
iq_note "commit $commit"
|
||
[[ "$tree" = clean ]] && iq_ok "tree $tree" || iq_note "tree $tree"
|
||
}
|
||
|
||
iq_switch() {
|
||
local remote=origin branch
|
||
iq_require_root
|
||
[[ $# -ge 1 ]] || { iq_fail 'usage: iq switch [REMOTE] BRANCH'; return 1; }
|
||
[[ $# -ge 2 ]] && { remote="$1"; shift; }
|
||
branch="$1"
|
||
iq_title "switching to $remote/$branch"
|
||
iq_note 'this discards uncommitted changes and untracked files'
|
||
iq_run git -C "$IQ_ROOT" fetch "$remote" "$branch:refs/remotes/$remote/$branch"
|
||
iq_run git -C "$IQ_ROOT" checkout -B "$branch" --track "$remote/$branch"
|
||
iq_run git -C "$IQ_ROOT" reset --hard "$remote/$branch"
|
||
iq_run git -C "$IQ_ROOT" clean -df
|
||
}
|
||
|
||
iq_service() {
|
||
local action="$1"
|
||
[[ -f /AGNOS ]] || { iq_note "${action} is available on IQ.OS devices only"; return 0; }
|
||
iq_run sudo systemctl "$action" iq
|
||
}
|
||
|
||
iq_wait_for_tmux() {
|
||
local expected="$1" attempt active
|
||
[[ "$IQ_DRY" = 1 ]] && { iq_note "would verify IQ tmux session ${expected}"; return 0; }
|
||
for ((attempt = 0; attempt < 15; attempt++)); do
|
||
if tmux has-session -t iq 2>/dev/null; then active=running; else active=stopped; fi
|
||
[[ "$active" = "$expected" ]] && return 0
|
||
sleep 1
|
||
done
|
||
return 1
|
||
}
|
||
|
||
iq_fast_restart() {
|
||
[[ -f /AGNOS || "$IQ_DRY" = 1 ]] || { iq_fail 'fast update restart is available on IQ.OS devices only'; return 1; }
|
||
iq_title 'fast restarting IQ.Pilot'
|
||
iq_run sudo systemctl stop iq
|
||
if ! iq_wait_for_tmux stopped; then
|
||
iq_fail 'IQ tmux session did not stop; attempt a reboot.'
|
||
return 1
|
||
fi
|
||
iq_run sudo systemctl start iq
|
||
if ! iq_wait_for_tmux running; then
|
||
iq_fail 'IQ tmux session did not start; attempt a reboot.'
|
||
return 1
|
||
fi
|
||
iq_ok 'IQ.Pilot restarted'
|
||
}
|
||
|
||
iq_help() {
|
||
iq_title 'command center'
|
||
printf '%bUsage%b iq [--dir PATH] [--dry] COMMAND [ARGS] [r]\n\n' "$IQ_BOLD" "$IQ_RESET"
|
||
printf '%bCOMMAND%b\n' "$IQ_PURPLE" "$IQ_RESET"
|
||
printf ' %bsetup%b install development dependencies\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bcheck%b verify checkout, Git, Python, and venv\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bbuild%b build IQ.Pilot\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bquality%b run code-quality checks\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bpkg%b authenticate and synchronize private packages\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bupdate%b pull IQ.Pilot, synchronize packages, optionally fast restart\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bstatus%b show checkout, branch, commit, and tree state\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bswitch%b replace the checkout with another branch\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bvenv%b open a shell with the project venv active\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bstart%b start IQ.Pilot on IQ.OS\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %bstop%b stop IQ.Pilot on IQ.OS\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf ' %binstall%b install the iq shell command\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf '\n %br%b reboot after a successful command\n' "$IQ_CYAN" "$IQ_RESET"
|
||
printf '\n%bExamples%b\n' "$IQ_PURPLE" "$IQ_RESET"
|
||
printf ' iq setup iq update iq update f iq update f r\n'
|
||
iq_line
|
||
}
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
-d|--dir) [[ $# -ge 2 ]] || { iq_fail 'missing path after --dir'; exit 2; }; IQ_ROOT="$2"; shift 2 ;;
|
||
--dry) IQ_DRY=1; shift ;;
|
||
-n|--no-verify) IQ_NO_VERIFY=1; shift ;;
|
||
-l|--log) [[ $# -ge 2 ]] || { iq_fail 'missing file after --log'; exit 2; }; IQ_LOG_FILE="$2"; shift 2 ;;
|
||
-h|--help|help) iq_help; exit 0 ;;
|
||
*) break ;;
|
||
esac
|
||
done
|
||
|
||
command="${1:-help}"
|
||
[[ $# -gt 0 ]] && shift
|
||
if [[ $# -gt 0 && "${!#}" = r ]]; then
|
||
IQ_REBOOT=1
|
||
set -- "${@:1:$#-1}"
|
||
fi
|
||
case "$command" in
|
||
help) iq_help ;;
|
||
setup) iq_setup "$@" ;;
|
||
check) iq_check "$@" ;;
|
||
build) iq_build "$@" ;;
|
||
quality) iq_quality "$@" ;;
|
||
pkg) iq_pkg "$@" ;;
|
||
update) iq_update "$@" ;;
|
||
status) iq_status "$@" ;;
|
||
switch) iq_switch "$@" ;;
|
||
venv) iq_venv "$@" ;;
|
||
start) iq_service start "$@" ;;
|
||
stop) iq_service stop "$@" ;;
|
||
install) iq_install "$@" ;;
|
||
*) iq_fail "unknown command: $command"; iq_help; exit 2 ;;
|
||
esac
|
||
|
||
if [[ "$IQ_REBOOT" = 1 ]]; then
|
||
iq_title 'restarting IQ.OS'
|
||
iq_run sudo reboot
|
||
fi
|