diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b4f3fc0..b569999 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,6 +64,7 @@ jobs: commands: | chmod +x ${{matrix.script}} ${{ matrix.script }} + echo "${{ github.ref_name }}" > /opt/photonvision/image-version - name: Compress built image run: | diff --git a/install.sh b/install.sh index 43b9eee..3431189 100755 --- a/install.sh +++ b/install.sh @@ -1,159 +1,292 @@ #!/bin/bash +needs_arg() { + if [ -z "$OPTARG" ]; then + die "Argument is required for --$OPT option" \ + "See './install.sh -h' for more information." + fi; +} + +die() { + for arg in "$@"; do + echo "$arg" 1>&2 + done + exit 1 +} + +debug() { + if [ -z "$QUIET" ] ; then + for arg in "$@"; do + echo "$arg" + done + fi +} + package_is_installed(){ dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -q "ok installed" } +install_if_missing() { + if package_is_installed "$1" ; then + debug "Found existing $1. Skipping..." + return + fi + + debug "Installing $1..." + apt-get install --yes "$1" + debug "$1 installation complete." +} + +get_photonvision_releases() { + # Return cached input + if [ -n "$PHOTON_VISION_RELEASES" ] ; then + echo "$PHOTON_VISION_RELEASES" + return + fi + + # Use curl if available, otherwise fallback to wget + if command -v curl > /dev/null 2>&1 ; then + PHOTON_VISION_RELEASES="$(curl -sk https://api.github.com/repos/photonvision/photonvision/releases)" + else + PHOTON_VISION_RELEASES="$(wget -qO- https://api.github.com/repos/photonvision/photonvision/releases)" + fi + + echo "$PHOTON_VISION_RELEASES" +} + +get_versions() { + if [ -z "$PHOTON_VISION_VERSIONS" ] ; then + PHOTON_VISION_VERSIONS=$(get_photonvision_releases | \ + sed -En 's/\"tag_name\": \"v([0-9]+\.[0-9]+\.[0-9]+)(-(beta|alpha)(-[0-9])?(\.[0-9]+)?)?\",/\1\2/p' | \ + sed 's/^[[:space:]]*//') + fi + + echo "$PHOTON_VISION_VERSIONS" +} + +is_version_available() { + local target_version="$1" + + # latest is a special case + if [ "$target_version" = "latest" ]; then + return 0 + fi + + # Check if multiple lines are match. You can only match 1. + if [ "$(get_versions | grep -cFx "$target_version")" -ne 1 ] ; then + return 1 + fi + + return 0 +} + help() { - echo "This script installs Photonvision." - echo "It must be run as root." - echo - echo "Syntax: sudo ./install.sh [-h|m|n|q]" - echo " options:" - echo " -h Display this help message." - echo " -m Install and configure NetworkManager (Ubuntu only)." - echo " -n Disable networking. This will also prevent installation of NetworkManager." - echo " -q Silent install, automatically accepts all defaults. For non-interactive use." - echo + cat << EOF +This script installs Photonvision. +It must be run as root. + +Syntax: sudo ./install.sh [options] + options: + -h, --help + Display this help message. + -l, --list-versions + Lists all available versions of PhotonVision. + -v , --version= + Specifies which version of PhotonVision to install. + If not specified, the latest stable release is installed. + Ignores leading 'v's. + -a , --arch= + Install PhotonVision for the specified architecture. + Supported values: aarch64, x86_64 + -m [option], --install-nm=[option] + Controls NetworkManager installation (Ubuntu only). + Options: "yes", "no", "ask". + Default: "ask" (unless -q or --quiet is specified, then "no"). + "ask" prompts for installation. Ignored on other distros. + -n, --no-networking + Disable networking. This will also prevent installation of + NetworkManager, overriding -m,--install-nm. + -q, --quiet + Silent install, automatically accepts all defaults. For + non-interactive use. Makes -m,--install-nm default to "no". + +EOF } -INSTALL_NETWORK_MANAGER="false" +INSTALL_NETWORK_MANAGER="ask" +VERSION="latest" + +while getopts "hlv:a:mnq-:" OPT; do + if [ "$OPT" = "-" ]; then + OPT="${OPTARG%%=*}" # extract long option name + OPTARG="${OPTARG#"$OPT"}" # extract long option argument (may be empty) + OPTARG="${OPTARG#=}" # if long option argument, remove assigning `=` + fi -while getopts ":hmnq" name; do - case "$name" in - h) + case "$OPT" in + h | help) help exit 0 ;; - m) INSTALL_NETWORK_MANAGER="true" + l | list-versions) + get_versions + exit 0 + ;; + v | version) + needs_arg + VERSION=${OPTARG#v} # drop leading 'v's + ;; + a | arch) needs_arg; ARCH=$OPTARG + ;; + m | install-nm) + INSTALL_NETWORK_MANAGER="$(echo "${OPTARG:-'yes'}" | tr '[:upper:]' '[:lower:]')" + case "$INSTALL_NETWORK_MANAGER" in + yes) + ;; + no) + ;; + ask) + ;; + * ) + die "Valid options for -m, --install-nm are: 'yes', 'no', and 'ask'" + ;; + esac ;; - n) DISABLE_NETWORKING="true" + n | no-networking) DISABLE_NETWORKING="true" ;; - q) QUIET="true" + q | quiet) QUIET="true" + ;; + \?) # Handle invalid short options + die "Error: Invalid option -$OPTARG" \ + "See './install.sh -h' for more information." + ;; + * ) # Handle invalid long options + die "Error: Invalid option --$OPT" \ + "See './install.sh -h' for more information." ;; - \?) - echo "Error: Invalid option -- '$OPTARG'" - echo "Try './install.sh -h' for more information." - exit 1 esac done -shift $(($OPTIND -1)) - if [ "$(id -u)" != "0" ]; then - echo "This script must be run as root" 1>&2 - exit 1 + die "This script must be run as root" +fi + +if [[ -z "$ARCH" ]]; then + debug "Arch was not specified. Inferring..." + ARCH=$(uname -m) + debug "Arch was inferred to be $ARCH" fi -ARCH=$(uname -m) ARCH_NAME="" if [ "$ARCH" = "aarch64" ]; then ARCH_NAME="linuxarm64" elif [ "$ARCH" = "armv7l" ]; then - echo "ARM32 is not supported by PhotonVision. Exiting." - exit 1 + die "ARM32 is not supported by PhotonVision. Exiting." elif [ "$ARCH" = "x86_64" ]; then ARCH_NAME="linuxx64" else - if [ "$#" -ne 1 ]; then - echo "Can't determine current arch; please provide it (one of):" - echo "" - echo "- linuxarm64 (64-bit Linux ARM)" - echo "- linuxx64 (64-bit Linux)" - exit 1 - else - echo "Can't detect arch (got $ARCH) -- using user-provided $1" - ARCH_NAME=$1 - fi + die "Unsupported or unknown architecture: '$ARCH'." \ + "Please specify your architecture using: ./install.sh -a " \ + "Run './install.sh -h' for more information." fi -echo "This is the installation script for PhotonVision." -echo "Installing for platform $ARCH_NAME" +debug "This is the installation script for PhotonVision." +debug "Installing for platform $ARCH" DISTRO=$(lsb_release -is) -if [[ "$DISTRO" = "Ubuntu" && "$INSTALL_NETWORK_MANAGER" != "true" && -z "$QUIET" && -z "$DISABLE_NETWORKING" ]]; then - echo "" - echo "Photonvision uses NetworkManager to control networking on your device." + +# Only ask if it makes sense to do so. +# i.e. the distro is Ubuntu, you haven't requested disabling networking, +# and you have requested a quiet install. +if [[ "$INSTALL_NETWORK_MANAGER" == "ask" ]]; then + if [[ "$DISTRO" != "Ubuntu" || -n "$DISABLE_NETWORKING" || -n "$QUIET" ]] ; then + INSTALL_NETWORK_MANAGER="no" + fi +fi + +if [[ "$INSTALL_NETWORK_MANAGER" == "ask" ]]; then + debug "" + debug "Photonvision uses NetworkManager to control networking on your device." + debug "This could possibly mess up the network configuration in Ubuntu." read -p "Do you want this script to install and configure NetworkManager? [y/N]: " response if [[ $response == [yY] || $response == [yY][eE][sS] ]]; then - INSTALL_NETWORK_MANAGER="true" + INSTALL_NETWORK_MANAGER="yes" fi fi -echo "Update package list" +debug "Updating package list..." apt-get update +debug "Updated package list." -echo "Installing curl..." -apt-get install --yes curl -echo "curl installation complete." - -echo "Installing avahi-daemon..." -apt-get install --yes avahi-daemon -echo "avahi-daemon installation complete." - -echo "Installing cpufrequtils..." -apt-get install --yes cpufrequtils -echo "cpufrequtils installation complete." +install_if_missing curl +install_if_missing avahi-daemon +install_if_missing cpufrequtils +install_if_missing libatomic1 +install_if_missing v4l-utils +install_if_missing sqlite3 +install_if_missing openjdk-17-jre-headless -echo "Setting cpufrequtils to performance mode" +debug "Setting cpufrequtils to performance mode" if [ -f /etc/default/cpufrequtils ]; then sed -i -e 's/^#\?GOVERNOR=.*$/GOVERNOR=performance/' /etc/default/cpufrequtils else echo 'GOVERNOR=performance' > /etc/default/cpufrequtils fi -echo "Installing libatomic" -apt-get install --yes libatomic1 -echo "libatomic installation complete." +if [[ "$INSTALL_NETWORK_MANAGER" == "yes" ]]; then + debug "NetworkManager installation specified. Installing components..." + install_if_missing network-manager + install_if_missing net-tools -if [[ "$INSTALL_NETWORK_MANAGER" == "true" ]]; then - echo "Installing network-manager..." - apt-get install --yes network-manager net-tools + debug "Configuring..." systemctl disable systemd-networkd-wait-online.service cat > /etc/netplan/00-default-nm-renderer.yaml <