diff --git a/.gitignore b/.gitignore index c6619faa..f7807ddb 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,6 @@ tools/*.out tools/git_scraper tools/custom_monitor tools/compiler -tools/tools # Ignore the output from the git scraper src/git_info.h diff --git a/tools/install_arduino.sh b/tools/install_arduino.sh deleted file mode 100755 index e66ecad0..00000000 --- a/tools/install_arduino.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -# install_arduino.sh -# -# Installs arduino-cli + the Teensy core. - -detect_distro() { - if [[ "$(uname -s)" == "Darwin" ]]; then echo "mac"; return; fi - if [[ -r /etc/os-release ]]; then - . /etc/os-release - case "${ID:-}" in - arch|manjaro|endeavouros|arcolinux|artix|garuda) echo "arch"; return ;; - ubuntu|debian|linuxmint|pop|raspbian|elementary|neon) echo "debian"; return ;; - esac - case "${ID_LIKE:-}" in - *arch*) echo "arch"; return ;; - *debian*|*ubuntu*) echo "debian"; return ;; - esac - fi - # pkg manager only - if command -v pacman >/dev/null 2>&1; then echo "arch"; return; fi - if command -v apt >/dev/null 2>&1; then echo "debian"; return; fi - echo "unknown" -} - -DISTRO="$(detect_distro)" -echo "Detected distro family: $DISTRO" - -# verify that curl is installed (IF YOU HAVE A PROBLEM WITH SYU INSTALL MANUALLY!!) -case "$DISTRO" in - arch) - sudo pacman -Syu --needed --noconfirm curl - ;; - debian) - sudo apt update - sudo apt install -y curl - ;; - mac) - # macOS ships with curl I think, homebrew install as fall back yep. - command -v curl >/dev/null 2>&1 || brew install curl - ;; - *) - echo "[WARN] Unsupported distro. Please install 'curl' manually, then re-run." >&2 - exit 1 - ;; -esac - -# download arduino-cli's install script -curl -o arduino-install-thing.sh https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh -# make this executable -chmod +x arduino-install-thing.sh -# run the install script and place it in /usr/local/bin (env var) -sudo BINDIR=/usr/local/bin/ ./arduino-install-thing.sh -# clean up install script -rm arduino-install-thing.sh - -# install the needed board/tools for Teensy -arduino-cli core install teensy:avr@1.59.0 --additional-urls "https://www.pjrc.com/teensy/package_teensy_index.json" - -# install the compiler -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -"$SCRIPT_DIR/install_compiler.sh" diff --git a/tools/install_compiler.sh b/tools/install_compiler.sh index f78c08c7..c6d16122 100755 --- a/tools/install_compiler.sh +++ b/tools/install_compiler.sh @@ -1,52 +1,142 @@ -# download links for gcc-arm-none-eabi-14.2.rel1 -x86_64_Linux="https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi.tar.xz" -AArch64_Linux="https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-aarch64-arm-none-eabi.tar.xz" -x86_64_MacOS="https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-darwin-x86_64-arm-none-eabi.tar.xz" -Arm64_MacOS="https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-darwin-arm64-arm-none-eabi.tar.xz" - -# where to store the compiler -OUTPUT=./tools/compiler - -TAR_NAME=./arm-gnu-toolchain-14.2.rel1.tar.xz - -# remove the old compiler -rm -rf $OUTPUT - -# create a directory to store the compiler -mkdir -p $OUTPUT - -# install the correct compiler based on the OS and architecture -if [[ "$(uname -s)" == "Linux" ]]; then - if [[ $(uname -m) == "x86_64" ]]; then - wget $x86_64_Linux -O $TAR_NAME - elif [[ $(uname -m) == "aarch64" ]]; then - wget $AArch64_Linux -O $TAR_NAME - fi -elif [[ "$(uname -s)" == "Darwin" ]]; then - # If wget is not installed, install it using Homebrew - # Suppress output with >/dev/null and 2>&1 sends errors to /dev/null - if ! command -v wget >/dev/null 2>&1; then - echo "wget not found. Installing via Homebrew..." - # If homebrew is installed, use it - if command -v brew >/dev/null 2>&1; then - brew install wget - else - echo "Homebrew is not installed. Please install Homebrew first: https://brew.sh/" - exit 1 - fi - fi - if [[ $(uname -m) == "x86_64" ]]; then - wget "$x86_64_MacOS" -O "$TAR_NAME" - elif [[ $(uname -m) == "arm64" ]]; then - wget "$Arm64_MacOS" -O "$TAR_NAME" - fi +#!/usr/bin/env bash +# install_compiler.sh +# +# Installs the gcc-arm-none-eabi toolchain into tools/compiler/arm-gnu-toolchain. +# +# The download is verified against a pinned SHA256 and only swapped into place +# once it has been extracted successfully, so a failed or interrupted run always +# leaves the previously installed toolchain untouched. + +set -euo pipefail + +# --- what to install ------------------------------------------------------- +# To bump the toolchain, change VERSION and refresh the table below. Each row is +# " ". Arm publishes the checksums +# next to each tarball, e.g. /.tar.xz.sha256asc +VERSION="14.2.rel1" +BASE_URL="https://developer.arm.com/-/media/Files/downloads/gnu/${VERSION}/binrel" + +# --- where to put it ------------------------------------------------------- +# Derived from the script's own location so it works from any directory. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OUTPUT="$SCRIPT_DIR/compiler" +DEST="$OUTPUT/arm-gnu-toolchain" + +# --- pick the right tarball for this machine ------------------------------- +OS="$(uname -s)" +ARCH="$(uname -m)" + +# A shell running under Rosetta reports x86_64 even on Apple Silicon, which +# would silently install the (much slower) translated Intel toolchain. +# TODO: remove this when apple ends support for Intel-based apps in macOS 28 +if [[ "$OS" == "Darwin" && "$ARCH" == "x86_64" ]] && + [[ "$(sysctl -n sysctl.proc_translated 2>/dev/null)" == "1" ]]; then + echo "[WARN] Running under Rosetta; installing the native arm64 toolchain instead." + ARCH="arm64" +fi + +case "$OS $ARCH" in + "Linux x86_64") + SLUG="x86_64-arm-none-eabi" + SHA256="62a63b981fe391a9cbad7ef51b17e49aeaa3e7b0d029b36ca1e9c3b2a9b78823" ;; + "Linux aarch64") + SLUG="aarch64-arm-none-eabi" + SHA256="87330bab085dd8749d4ed0ad633674b9dc48b237b61069e3b481abd364d0a684" ;; + "Darwin x86_64") + SLUG="darwin-x86_64-arm-none-eabi" + SHA256="2d9e717dd4f7751d18936ae1365d25916534105ebcb7583039eff1092b824505" ;; + "Darwin arm64") + SLUG="darwin-arm64-arm-none-eabi" + SHA256="c7c78ffab9bebfce91d99d3c24da6bf4b81c01e16cf551eb2ff9f25b9e0a3818" ;; + *) + echo "[ERROR] Unsupported platform: $OS $ARCH" >&2 + echo "Arm ships this toolchain for Linux (x86_64, aarch64) and macOS (x86_64, arm64) only." >&2 + echo "Install it manually into $DEST" >&2 + exit 1 + ;; +esac + +ASSET="arm-gnu-toolchain-${VERSION}-${SLUG}.tar.xz" +URL="$BASE_URL/$ASSET" + +# macOS ships shasum, most Linux distros ship sha256sum. Resolve it up front so +# a machine that has neither fails before downloading 128MB it cannot verify. +if command -v shasum >/dev/null 2>&1; then + SHA_CMD=(shasum -a 256) +elif command -v sha256sum >/dev/null 2>&1; then + SHA_CMD=(sha256sum) +else + echo "[ERROR] Neither shasum nor sha256sum is available; cannot verify the download." >&2 + exit 1 +fi + +# --- staging area ---------------------------------------------------------- +# Kept inside $OUTPUT (already gitignored, and on the same filesystem as $DEST +# so the final swap is a rename rather than a copy of ~1GB). +mkdir -p "$OUTPUT" +WORK="$(mktemp -d "$OUTPUT/.install.XXXXXX")" + +# If anything below fails after the old toolchain has been moved aside, put it +# back before clearing the staging area. +cleanup() { + if [[ -d "$WORK/previous" && ! -e "$DEST" ]]; then + echo "Restoring the previous toolchain..." >&2 + mv "$WORK/previous" "$DEST" + fi + rm -rf "$WORK" +} +trap cleanup EXIT + +# --- download -------------------------------------------------------------- +# -f so an HTTP error page is never written out as a "tarball", -C - so a retry +# resumes instead of restarting the transfer from zero. +echo "Downloading $ASSET ..." +if [[ -t 2 ]]; then + PROGRESS="--progress-bar" # interactive: one bar +else + PROGRESS="--silent" # piped/CI: no per-update spam, -S keeps errors +fi +curl -fL "$PROGRESS" --show-error --retry 3 --retry-delay 2 -C - -o "$WORK/$ASSET" "$URL" + +# --- verify ---------------------------------------------------------------- +echo "Verifying checksum..." +ACTUAL="$("${SHA_CMD[@]}" "$WORK/$ASSET" | awk '{print $1}')" +if [[ "$ACTUAL" != "$SHA256" ]]; then + echo "[ERROR] Checksum mismatch for $ASSET" >&2 + echo " expected: $SHA256" >&2 + echo " actual: $ACTUAL" >&2 + echo "The download was corrupted or tampered with. Nothing was installed." >&2 + exit 1 fi -# extract the compiler +# --- extract --------------------------------------------------------------- echo "Extracting the compiler..." -tar -xf "$TAR_NAME" -C "$OUTPUT" -mv "$OUTPUT"/arm-gnu-toolchain* "$OUTPUT"/arm-gnu-toolchain +mkdir -p "$WORK/extract" +tar -xf "$WORK/$ASSET" -C "$WORK/extract" +rm -f "$WORK/$ASSET" -# remove the downloaded tar file +# The tarball should contain exactly one top-level directory. +shopt -s nullglob +EXTRACTED=("$WORK"/extract/*/) +shopt -u nullglob +if [[ ${#EXTRACTED[@]} -ne 1 ]]; then + echo "[ERROR] Expected one directory in $ASSET, found ${#EXTRACTED[@]}." >&2 + exit 1 +fi + +# --- swap into place ------------------------------------------------------- +# Only now is the old toolchain touched, and it is moved aside rather than +# deleted so the cleanup trap can put it back if this fails. +echo "Installing to $DEST ..." +if [[ -e "$DEST" ]]; then + mv "$DEST" "$WORK/previous" +fi +mv "${EXTRACTED[0]%/}" "$DEST" + +# Discard the old toolchain now rather than leaving a multi-second delete +# running under the trap after the success message. echo "Cleaning up..." -rm -f "$TAR_NAME" +trap - EXIT +rm -rf "$WORK" + +echo "[OK] arm-none-eabi toolchain $VERSION installed at $DEST" diff --git a/tools/install_tytools.sh b/tools/install_tytools.sh index 5f2cff56..d5d6c241 100755 --- a/tools/install_tytools.sh +++ b/tools/install_tytools.sh @@ -41,51 +41,71 @@ if [[ "$OS" == "Linux" ]]; then # 2. Prepare a temporary build directory for AUR package echo "Creating temp build directory..." BUILD_DIR="$(mktemp -d)" + # Clean up even if the clone or build fails partway through + trap 'rm -rf "$BUILD_DIR"' EXIT # 3. Clone the tytools AUR package echo "Downloading tytools AUR pkg..." git clone https://aur.archlinux.org/tytools.git "$BUILD_DIR/tytools" - # 4. Enter the cloned package directory - echo "Entering tytools build dir..." - cd "$BUILD_DIR/tytools" - - # 5. Build and install tytools with makepkg + # 4. Build and install tytools with makepkg, in a subshell so we do not + # have to cd back afterwards echo "Building..." - makepkg -si --noconfirm - cd - >/dev/null && rm -rf "$BUILD_DIR" + ( cd "$BUILD_DIR/tytools" && makepkg -si --noconfirm ) echo "[DONE] tytools installed! yay! :3" elif [[ "$DISTRO" == "debian" ]]; then echo "--> Starting Debian based tytools installer..." + # tytools is installed from a pinned .deb rather than by adding + # download.koromix.dev to APT, so nothing persists in /etc/apt and everyone + # on the team runs an identical build. To bump it, pick a version from + # https://download.koromix.dev/debian/dists/stable/main/binary-/Packages + # and copy the Version and SHA256 fields for each architecture. + TYTOOLS_VERSION="0.9.9-1165" + DEB_ARCH="$(dpkg --print-architecture)" + case "$DEB_ARCH" in + amd64) TYTOOLS_SHA256="4e5ec03db55dc1119465d0303661c71f3432070bcbb6d17e1d026ee5321c099f" ;; + arm64) TYTOOLS_SHA256="28d3ce85c7fb0bb7e79d4eb034c64613bb35938acae0f81fa9b6e8afbdc00d79" ;; + *) + echo "[ERROR] No tytools package for architecture: $DEB_ARCH" >&2 + echo "Upstream builds amd64 and arm64 only. Build it from source instead:" >&2 + echo " https://github.com/Koromix/tytools" >&2 + exit 1 + ;; + esac + + DEB_NAME="tytools_${TYTOOLS_VERSION}_${DEB_ARCH}.deb" + DEB_URL="https://download.koromix.dev/debian/pool/${DEB_NAME}" + # 1. Ensure curl is present echo "Installing curl..." sudo apt update sudo apt install -y curl - # 2. Prepare keyrings directory - echo "Creating /etc/apt/keyrings with proper perm..." - sudo mkdir -p -m0755 /etc/apt/keyrings - - # 3. Download Koromix key - echo "Downloading Koromix GPG key..." - sudo curl -fsSL \ - https://download.koromix.dev/debian/koromix-archive-keyring.gpg \ - -o /etc/apt/keyrings/koromix-archive-keyring.gpg - - # 4. Add repo to sources.list.d - echo "Adding Koromix repo..." - echo \ - "deb [signed-by=/etc/apt/keyrings/koromix-archive-keyring.gpg] \ -https://download.koromix.dev/debian stable main" \ - | sudo tee /etc/apt/sources.list.d/koromix.dev-stable.list > /dev/null - - # 5. Update & install - echo "Updating APT and installing tytools..." - sudo apt update - sudo apt install -y tytools + # 2. Download the pinned package + TMP_DIR="$(mktemp -d)" + trap 'rm -rf "$TMP_DIR"' EXIT + + echo "Downloading ${DEB_NAME}..." + curl -fL --retry 3 --retry-delay 2 -o "${TMP_DIR}/${DEB_NAME}" "$DEB_URL" + + # 3. Verify it before handing it to dpkg + echo "Verifying checksum..." + ACTUAL="$(sha256sum "${TMP_DIR}/${DEB_NAME}" | awk '{print $1}')" + if [[ "$ACTUAL" != "$TYTOOLS_SHA256" ]]; then + echo "[ERROR] Checksum mismatch for ${DEB_NAME}" >&2 + echo " expected: $TYTOOLS_SHA256" >&2 + echo " actual: $ACTUAL" >&2 + echo "The download was corrupted or the pinned build changed. Nothing was installed." >&2 + exit 1 + fi + + # 4. Install it. apt pulls the Qt6 and udev dependencies from the distro's + # own repositories, so only the tytools package itself comes from here. + echo "Installing tytools..." + sudo apt install -y "${TMP_DIR}/${DEB_NAME}" echo "[OK] tytools installed!" @@ -97,28 +117,37 @@ https://download.koromix.dev/debian stable main" \ elif [[ "$OS" == "Darwin" ]]; then echo "--> Running macOS installer (tycmd)..." - REPO_URL="https://github.com/CU-Robotics/tycmd.git" + # tycmd is a prebuilt binary installed with sudo, so it is pinned to one + # commit and verified rather than tracking whatever is on main. To bump it, + # pick a commit and record its hash: + # curl -fsSL https://raw.githubusercontent.com/CU-Robotics/tycmd//tycmd | shasum -a 256 + TYCMD_COMMIT="5a416e8adfa0c454948f53bd801b3ce3e7f30f9c" + TYCMD_SHA256="00593e4d53672bf3ae86d30bb8eead3f4c8f671480f706a33129d2368952444a" + TYCMD_URL="https://raw.githubusercontent.com/CU-Robotics/tycmd/${TYCMD_COMMIT}/tycmd" + BIN_NAME="tycmd" DEST_DIR="/usr/local/bin" TMP_DIR="$(mktemp -d)" - - echo "Cloning ${REPO_URL} into ${TMP_DIR}..." - git clone "${REPO_URL}" "${TMP_DIR}" - - # Verify file exists (may not be executable yet) - if [[ ! -e "${TMP_DIR}/${BIN_NAME}" ]]; then - echo "Error: '${BIN_NAME}' not found in repo root." + trap 'rm -rf "$TMP_DIR"' EXIT + + echo "Downloading ${BIN_NAME} (${TYCMD_COMMIT:0:7})..." + curl -fL --retry 3 --retry-delay 2 -o "${TMP_DIR}/${BIN_NAME}" "$TYCMD_URL" + + echo "Verifying checksum..." + ACTUAL="$(shasum -a 256 "${TMP_DIR}/${BIN_NAME}" | awk '{print $1}')" + if [[ "$ACTUAL" != "$TYCMD_SHA256" ]]; then + echo "[ERROR] Checksum mismatch for ${BIN_NAME}" >&2 + echo " expected: $TYCMD_SHA256" >&2 + echo " actual: $ACTUAL" >&2 + echo "The download was corrupted or the pinned commit changed. Nothing was installed." >&2 exit 1 fi - echo "Moving '${BIN_NAME}' to ${DEST_DIR} (requires sudo)..." - sudo mv "${TMP_DIR}/${BIN_NAME}" "${DEST_DIR}/" - - echo "Setting executable permissions..." - sudo chmod +x "${DEST_DIR}/${BIN_NAME}" - - echo "Cleaning up..." - rm -rf "${TMP_DIR}" + # macOS does not create /usr/local/bin, and Homebrew on Apple Silicon uses + # /opt/homebrew, so a clean Mac may not have it yet. + echo "Installing '${BIN_NAME}' to ${DEST_DIR} (requires sudo)..." + sudo mkdir -p "${DEST_DIR}" + sudo install -m 0755 "${TMP_DIR}/${BIN_NAME}" "${DEST_DIR}/${BIN_NAME}" echo "[OK] '${BIN_NAME}' installed to ${DEST_DIR}/${BIN_NAME}."