Files
StarPilot/tools/op.sh
T
firestar5683 22a8575199 Vendor dependencies and resources for standalone builds
Keep official openpilot ancestry and dependency licenses. Store resources directly in Git, remove LFS setup requirements, and leave the downloadable big model outside the source tree.
2026-10-03 21:01:18 +00:00

523 lines
16 KiB
Bash
Executable File

#!/usr/bin/env bash
if [[ ! "${BASH_SOURCE[0]}" = "${0}" ]]; then
echo "Invalid invocation! This script must not be sourced."
echo "Run 'op.sh' directly or check your .bashrc for a valid alias"
return 0
fi
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
UNDERLINE='\033[4m'
BOLD='\033[1m'
NC='\033[0m'
SHELL_NAME="$(basename "${SHELL}")"
RC_FILE="${HOME}/.$(basename "${SHELL}")rc"
if [ "$(uname)" == "Darwin" ] && [ "$SHELL" == "/bin/bash" ]; then
RC_FILE="$HOME/.bash_profile"
fi
function retry() {
local attempts=$1
shift
for i in $(seq 1 "$attempts"); do
if "$@"; then
return 0
fi
if [ "$i" -lt "$attempts" ]; then
echo " Attempt $i/$attempts failed, retrying in 5s..."
sleep 5
fi
done
return 1
}
function op_run_command() {
CMD="$*"
echo -e "${BOLD}Running command →${NC} $CMD │"
for ((i=0; i<$((19 + ${#CMD})); i++)); do
echo -n "─"
done
echo -e "┘\n"
if [[ -z "$DRY" ]]; then
"$@"
fi
}
# be default, assume openpilot dir is in current directory
OPENPILOT_ROOT=$(pwd)
function op_get_openpilot_dir() {
# First try traversing up the directory tree
while [[ "$OPENPILOT_ROOT" != '/' ]];
do
if find "$OPENPILOT_ROOT/launch_openpilot.sh" -maxdepth 1 -mindepth 1 &> /dev/null; then
return 0
fi
OPENPILOT_ROOT="$(readlink -f "$OPENPILOT_ROOT/"..)"
done
# Fallback to hardcoded directories if not found
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
for dir in "$(readlink -f "$SCRIPT_DIR/../..")" "$HOME/openpilot" "/data/openpilot"; do
if [[ -f "$dir/launch_openpilot.sh" ]]; then
OPENPILOT_ROOT="$dir"
return 0
fi
done
}
function op_install_post_commit() {
op_get_openpilot_dir
op_check_openpilot_dir
local git_dir common_dir hooks_dir hook_source
git_dir="$(git -C "$OPENPILOT_ROOT" rev-parse --absolute-git-dir)"
common_dir="$(git -C "$OPENPILOT_ROOT" rev-parse --path-format=absolute --git-common-dir)"
if [[ "$git_dir" != "$common_dir" || -d "$common_dir/worktrees" ]]; then
echo "Hook installation requires a standalone checkout without linked worktrees; shared hooks were not changed."
return 1
fi
hooks_dir="$(git -C "$OPENPILOT_ROOT" rev-parse --path-format=absolute --git-path hooks)"
if [[ "$hooks_dir" != "$git_dir/hooks" ]]; then
echo "A custom hooks directory is configured; install the linter there manually. Existing hooks were not changed."
return 1
fi
hook_source="$(cd "$OPENPILOT_ROOT" && pwd)/scripts/post-commit"
mkdir -p "$hooks_dir/post-commit.d"
if [[ ( -e "$hooks_dir/post-commit" || -L "$hooks_dir/post-commit" ) && ! "$hooks_dir/post-commit" -ef "$hook_source" ]]; then
if [[ -e "$hooks_dir/post-commit.d/post-commit" || -L "$hooks_dir/post-commit.d/post-commit" ]]; then
echo "An earlier post-commit hook is already saved; resolve the two existing hooks before installing."
return 1
fi
mv "$hooks_dir/post-commit" "$hooks_dir/post-commit.d/post-commit"
fi
ln -sf "$hook_source" "$hooks_dir/post-commit"
}
function op_check_openpilot_dir() {
echo "Checking for openpilot directory..."
if [[ -f "$OPENPILOT_ROOT/launch_openpilot.sh" ]]; then
echo -e " ↳ [${GREEN}✔${NC}] openpilot found."
return 0
fi
echo -e " ↳ [${RED}✗${NC}] openpilot directory not found! Make sure that you are"
echo " inside the openpilot directory or specify one with the"
echo " --dir option!"
return 1
}
function op_check_git() {
echo "Checking for git..."
if ! command -v "git" > /dev/null 2>&1; then
echo -e " ↳ [${RED}✗${NC}] git not found on your system!"
return 1
else
echo -e " ↳ [${GREEN}✔${NC}] git found."
fi
echo "Checking checked-in model resources..."
if [[ $(file -b "$OPENPILOT_ROOT/openpilot/selfdrive/modeld/models/dmonitoring_model.onnx") == "data" ]]; then
echo -e " ↳ [${GREEN}✔${NC}] model resources found."
else
echo -e " ↳ [${RED}✗${NC}] model resources missing or invalid; restore the ordinary checkout"
return 1
fi
echo "Checking tracked dependency sources..."
op_check_dependencies
}
function op_check_dependencies() {
local python_bin="$OPENPILOT_ROOT/.venv/bin/python3"
if [[ ! -x "$python_bin" ]]; then
python_bin=python3
fi
"$python_bin" "$OPENPILOT_ROOT/tools/vendor/check.py" "$@"
}
function op_check_os() {
echo "Checking for compatible os version..."
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo -e " ↳ [${GREEN}✔${NC}] Linux detected."
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo -e " ↳ [${GREEN}✔${NC}] macOS detected."
else
echo -e " ↳ [${RED}✗${NC}] OS type $OSTYPE not supported!"
return 1
fi
}
function op_check_venv() {
echo "Checking for venv..."
if [[ -f "$OPENPILOT_ROOT/.venv/bin/activate" ]]; then
echo -e " ↳ [${GREEN}✔${NC}] venv detected."
else
echo -e " ↳ [${RED}✗${NC}] Can't activate venv in '$OPENPILOT_ROOT'. Assuming global env!"
fi
}
function op_before_cmd() {
if [[ ! -z "$NO_VERIFY" ]]; then
return 0
fi
op_get_openpilot_dir
cd "$OPENPILOT_ROOT"
result="$((op_check_openpilot_dir ) 2>&1)" || (echo -e "$result" && return 1)
result="${result}\n$(( op_check_git ) 2>&1)" || (echo -e "$result" && return 1)
result="${result}\n$(( op_check_os ) 2>&1)" || (echo -e "$result" && return 1)
result="${result}\n$(( op_check_venv ) 2>&1)" || (echo -e "$result" && return 1)
op_activate_venv
if [[ -z $VERBOSE ]]; then
echo -e "${BOLD}Checking system →${NC} [${GREEN}✔${NC}]"
else
echo -e "$result"
fi
}
function op_setup() {
echo "Installing op system-wide..."
OP_SH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )/op.sh"
CMD=$(cat <<EOF
alias op='$OP_SH "\$@"'
_op_completions() { [ "\$COMP_CWORD" -eq 1 ] && COMPREPLY=(\$(compgen -W "\$(awk '/shift 1; op_/{print \$1}' $OP_SH)" -- "\${COMP_WORDS[1]}")); }
[ -n "\$BASH_VERSION" ] && complete -F _op_completions -o default op
EOF
)
grep -q "alias op=" "$RC_FILE" 2>/dev/null || printf '\n%s\n' "$CMD" >> "$RC_FILE"
echo -e " ↳ [${GREEN}✔${NC}] op installed successfully. Open a new shell to use it."
op_get_openpilot_dir
cd "$OPENPILOT_ROOT"
op_check_openpilot_dir
op_check_os
# Local Python package sources are shipped in the checkout. Bootstrap Python
# before running the full manifest validator on machines without Python yet.
for path in upstream-sync.json panda/pyproject.toml opendbc_repo/pyproject.toml msgq_repo/pyproject.toml rednose_repo/pyproject.toml teleoprtc_repo/pyproject.toml tinygrad_repo/pyproject.toml; do
if [[ ! -f "$OPENPILOT_ROOT/$path" ]]; then
echo "Missing tracked dependency source: $path. Restore the complete checkout."
return 1
fi
done
echo "Installing dependencies..."
st="$(date +%s)"
SETUP_SCRIPT="tools/setup_dependencies.sh"
if ! "$OPENPILOT_ROOT/$SETUP_SCRIPT"; then
echo -e " ↳ [${RED}✗${NC}] Dependencies installation failed!"
return 1
fi
et="$(date +%s)"
echo -e " ↳ [${GREEN}✔${NC}] Dependencies installed successfully in $((et - st)) seconds."
op_activate_venv
op_check_dependencies
op_check
}
function op_auth() {
op_before_cmd
op_run_command openpilot/tools/lib/auth.py "$@"
}
function op_activate_venv() {
# bash 3.2 can't handle this without the 'set +e'
set +e
source "$OPENPILOT_ROOT/.venv/bin/activate" &> /dev/null || true
set -e
# persist venv on PATH across GitHub Actions steps
if [ -n "$GITHUB_PATH" ]; then
echo "$OPENPILOT_ROOT/.venv/bin" >> "$GITHUB_PATH"
fi
}
function op_venv() {
op_before_cmd
if [[ ! -f "$OPENPILOT_ROOT/.venv/bin/activate" ]]; then
echo -e "No venv found in '$OPENPILOT_ROOT'"
return 1
fi
case $SHELL_NAME in
"zsh")
ZSHRC_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'tmp_zsh')
echo "source \"$RC_FILE\"; source \"$OPENPILOT_ROOT/.venv/bin/activate\"" >> "$ZSHRC_DIR/.zshrc"
ZDOTDIR=$ZSHRC_DIR zsh ;;
*)
bash --rcfile <(echo "source \"$RC_FILE\"; source \"$OPENPILOT_ROOT/.venv/bin/activate\"") ;;
esac
}
function op_adb() {
op_before_cmd
op_run_command tools/scripts/adb_ssh.sh "$@"
}
function op_ssh() {
op_before_cmd
op_run_command tools/scripts/ssh.py "$@"
}
function op_script() {
op_before_cmd
case $1 in
som-debug ) op_run_command panda/scripts/som_debug.sh "${@:2}" ;;
* )
echo -e "Unknown script '$1'. Available scripts:"
echo -e " ${BOLD}som-debug${NC} SOM serial debug console via panda"
return 1
;;
esac
}
function op_check() {
VERBOSE=1
op_before_cmd
unset VERBOSE
}
function op_esim() {
op_before_cmd
op_run_command openpilot/common/esim/esim.py "$@"
}
function op_build() {
CDIR=$(pwd)
op_before_cmd
cd "$CDIR"
if [[ -f "/AGNOS" ]]; then
# needed on AGNOS to not run out of memory
op_run_command openpilot/system/manager/build.py
else
op_run_command scons -u "$@"
fi
}
function op_juggle() {
op_before_cmd
op_run_command openpilot/tools/plotjuggler/juggle.py "$@"
}
function op_lint() {
op_before_cmd
op_run_command scripts/lint/lint.sh "$@"
}
function op_test() {
op_before_cmd
op_run_command tools/test_runner.py "$@"
}
function op_replay() {
op_before_cmd
op_run_command openpilot/tools/replay/replay "$@"
}
function op_cabana() {
op_before_cmd
op_run_command openpilot/tools/cabana/cabana "$@"
}
function op_sim() {
op_before_cmd
op_run_command exec openpilot/tools/sim/run_bridge.py &
op_run_command exec openpilot/tools/sim/launch_openpilot.sh
}
function op_clip() {
op_before_cmd
op_run_command openpilot/tools/clip/run.py "$@"
}
function op_docs() {
op_before_cmd
op_run_command python docs/serve.py "$@"
}
function op_check_agnos_update() {
if [[ ! -f "/AGNOS" ]]; then
return 0
fi
local choice current_version target_version
current_version="$(< /VERSION)"
target_version="$(unset AGNOS_VERSION; source "$OPENPILOT_ROOT/launch_env.sh"; echo "$AGNOS_VERSION")"
if [[ "$current_version" == "$target_version" ]]; then
return 0
fi
echo -e "${BOLD}AGNOS update available:${NC} $current_version → $target_version"
if read -r -p "Install it now? [y/N] " choice && [[ "$choice" =~ ^[Yy]$ ]]; then
op_run_command "$OPENPILOT_ROOT/openpilot/common/hardware/comma/agnos.py" --swap \
"$OPENPILOT_ROOT/openpilot/common/hardware/comma/agnos.json"
if read -r -p "Reboot now to apply the update? [y/N] " choice && [[ "$choice" =~ ^[Yy]$ ]]; then
op_run_command sudo reboot
else
echo "Reboot before starting openpilot to apply the AGNOS update."
fi
fi
}
function op_switch() {
op_get_openpilot_dir
op_check_openpilot_dir
cd "$OPENPILOT_ROOT"
REMOTE="origin"
if [ "$#" -gt 1 ]; then
REMOTE="$1"
shift
fi
if [ -z "$1" ]; then
echo -e "${BOLD}${UNDERLINE}Usage:${NC} op switch [REMOTE] <BRANCH>"
return 1
fi
BRANCH="$1"
git config --replace-all "remote.${REMOTE}.fetch" "+refs/heads/*:refs/remotes/${REMOTE}/*"
git -c submodule.recurse=false fetch --no-recurse-submodules "$REMOTE" "$BRANCH"
local target_commit
target_commit="$(git rev-parse 'FETCH_HEAD^{commit}')"
# Validate the target before discarding the current checkout's changes.
op_check_dependencies --revision "$target_commit"
git -c submodule.recurse=false checkout --force --no-recurse-submodules -B "$BRANCH" "$target_commit"
git branch --set-upstream-to="${REMOTE}/${BRANCH}" "$BRANCH"
git -c submodule.recurse=false reset --hard --no-recurse-submodules "$target_commit"
git clean -df
op_check_dependencies
# remove openpilot update flag if present
rm -f .overlay_init
op_check_agnos_update
}
function op_start() {
if [[ -f "/AGNOS" ]]; then
op_before_cmd
op_check_agnos_update
op_run_command sudo systemctl restart comma "$@"
fi
}
function op_stop() {
if [[ -f "/AGNOS" ]]; then
op_before_cmd
op_run_command sudo systemctl stop comma "$@"
fi
}
function op_default() {
echo "An openpilot helper"
echo ""
echo -e "${BOLD}${UNDERLINE}Description:${NC}"
echo " op is your entry point for all things related to openpilot development."
echo " op is only a wrapper for existing scripts, tools, and commands."
echo " op will always show you what it will run on your system."
echo ""
echo -e "${BOLD}${UNDERLINE}Usage:${NC} op [OPTIONS] <COMMAND>"
echo ""
echo -e "${BOLD}${UNDERLINE}Commands [System]:${NC}"
echo -e " ${BOLD}auth${NC} Authenticate yourself for API use"
echo -e " ${BOLD}check${NC} Check the development environment (git, os) to start using openpilot"
echo -e " ${BOLD}esim${NC} Manage eSIM profiles on your comma device"
echo -e " ${BOLD}venv${NC} Activate the python virtual environment"
echo -e " ${BOLD}setup${NC} Install the 'op' tool and openpilot dependencies"
echo -e " ${BOLD}build${NC} Run the openpilot build system in the current working directory"
echo -e " ${BOLD}switch${NC} Switch to a different git branch with a clean slate (nukes any changes)"
echo -e " ${BOLD}start${NC} Starts (or restarts) openpilot"
echo -e " ${BOLD}stop${NC} Stops openpilot"
echo ""
echo -e "${BOLD}${UNDERLINE}Commands [Tooling]:${NC}"
echo -e " ${BOLD}juggle${NC} Run PlotJuggler"
echo -e " ${BOLD}replay${NC} Run Replay"
echo -e " ${BOLD}cabana${NC} Run Cabana"
echo -e " ${BOLD}clip${NC} Run clip (linux only)"
echo -e " ${BOLD}docs${NC} Build or serve the openpilot documentation"
echo -e " ${BOLD}adb${NC} Run adb shell"
echo -e " ${BOLD}ssh${NC} comma prime SSH helper"
echo ""
echo -e "${BOLD}${UNDERLINE}Commands [Scripts]:${NC}"
echo -e " ${BOLD}script${NC} Run a script (e.g. op script som-debug)"
echo ""
echo -e "${BOLD}${UNDERLINE}Commands [Testing]:${NC}"
echo -e " ${BOLD}sim${NC} Run openpilot in a simulator"
echo -e " ${BOLD}lint${NC} Run the linter"
echo -e " ${BOLD}post-commit${NC} Install the linter as a post-commit hook"
echo -e " ${BOLD}test${NC} Run all unit tests"
echo ""
echo -e "${BOLD}${UNDERLINE}Options:${NC}"
echo -e " ${BOLD}-d, --dir${NC}"
echo " Specify the openpilot directory you want to use"
echo -e " ${BOLD}--dry${NC}"
echo " Don't actually run anything, just print what would be run"
echo -e " ${BOLD}-n, --no-verify${NC}"
echo " Skip environment check before running commands"
echo ""
echo -e "${BOLD}${UNDERLINE}Examples:${NC}"
echo " op setup"
echo " Run the setup script to install"
echo " openpilot's dependencies."
echo ""
echo " op build -j4"
echo " Compile openpilot using 4 cores"
echo ""
echo " op juggle --demo"
echo " Run PlotJuggler on the demo route"
}
function _op() {
# parse Options
case $1 in
-d | --dir ) shift 1; OPENPILOT_ROOT="$1"; shift 1 ;;
--dry ) shift 1; DRY="1" ;;
-n | --no-verify ) shift 1; NO_VERIFY="1" ;;
esac
# parse Commands
case $1 in
auth ) shift 1; op_auth "$@" ;;
venv ) shift 1; op_venv "$@" ;;
check ) shift 1; op_check "$@" ;;
esim ) shift 1; op_esim "$@" ;;
setup ) shift 1; op_setup "$@" ;;
build ) shift 1; op_build "$@" ;;
juggle ) shift 1; op_juggle "$@" ;;
cabana ) shift 1; op_cabana "$@" ;;
lint ) shift 1; op_lint "$@" ;;
test ) shift 1; op_test "$@" ;;
replay ) shift 1; op_replay "$@" ;;
clip ) shift 1; op_clip "$@" ;;
docs ) shift 1; op_docs "$@" ;;
sim ) shift 1; op_sim "$@" ;;
switch ) shift 1; op_switch "$@" ;;
start ) shift 1; op_start "$@" ;;
stop ) shift 1; op_stop "$@" ;;
restart ) shift 1; op_restart "$@" ;;
post-commit ) shift 1; op_install_post_commit "$@" ;;
adb ) shift 1; op_adb "$@" ;;
ssh ) shift 1; op_ssh "$@" ;;
script ) shift 1; op_script "$@" ;;
* ) op_default "$@" ;;
esac
}
_op "$@"