#!/usr/bin/env bash set -euo pipefail : "${INSTRUMENTS_ENABLED:=true}" : "${INSTRUMENTS_RELEASE_REPOSITORY:=AeroCore-IO/booster-installer}" : "${INSTRUMENTS_VERSION:=latest}" : "${INSTRUMENTS_RELEASE_API_BASE:=https://api.github.com}" : "${INSTRUMENTS_SEED_ROOT:=/usr/share/aerocore-booster-release}" if [[ "${INSTRUMENTS_ENABLED}" != "true" ]]; then echo "AeroCore Booster image integration is disabled" exit 0 fi case "$(uname -m)" in x86_64 | amd64) asset_suffix="_linux-x86_64.tar.gz" ;; *) echo "Unsupported AeroCore Booster image architecture: $(uname -m)" >&2 exit 1 ;; esac for command in curl jq sha256sum tar; do command -v "$command" >/dev/null 2>&1 || { echo "Required command is unavailable: $command" >&2 exit 1 } done workdir="$(mktemp -d)" trap 'rm -rf "$workdir"' EXIT github_curl() { local args=( --fail --location --retry 3 --retry-delay 2 --retry-max-time 60 --silent --show-error ) if [[ -s /run/secrets/GITHUB_TOKEN ]]; then args+=(--header "Authorization: Bearer $(&2 exit 1 fi IFS=$'\t' read -r asset_name asset_url <<<"${payload_assets[0]}" checksum_name="${asset_name}.sha256" checksum_url="$( jq -er --arg name "$checksum_name" \ '.assets[] | select(.name == $name) | .browser_download_url' \ "$release_json" )" archive="$workdir/$asset_name" checksum_file="$workdir/$checksum_name" github_curl "$asset_url" --output "$archive" github_curl "$checksum_url" --output "$checksum_file" expected_checksum="$(awk 'NR == 1 { print $1 }' "$checksum_file")" actual_checksum="$(sha256sum "$archive" | awk '{ print $1 }')" if [[ ! "$expected_checksum" =~ ^[[:xdigit:]]{64}$ ]] || [[ "${actual_checksum,,}" != "${expected_checksum,,}" ]]; then echo "Checksum verification failed for $asset_name" >&2 exit 1 fi while IFS= read -r entry; do case "$entry" in /* | ../* | */../* | */..) echo "Unsafe path in release archive: $entry" >&2 exit 1 ;; esac done < <(tar -tzf "$archive") extract_dir="$workdir/extracted" install -d "$extract_dir" tar --extract --gzip --file "$archive" --directory "$extract_dir" \ --no-same-owner if [[ -d "$extract_dir/aerocore-booster-release" ]]; then payload="$extract_dir/aerocore-booster-release" elif [[ -d "$extract_dir/instruments-ng-release" ]]; then payload="$extract_dir/instruments-ng-release" else echo "Release archive does not contain a supported payload root" >&2 exit 1 fi required_paths=( install.sh appimage/AppRun appimage/AppRun.wrapped sidecar/sidecar-main.cjs sidecar/aerocore-bridge.cjs runtime/avionics runtime/cabin runtime/wing runtime/node/bin/node pkg/systemd/ac-avionics.service pkg/systemd/ac-cabin.service pkg/systemd/ac-wing.service pkg/systemd/ac-instruments.service scripts/check-runtime-health.cjs manifest.yaml ) for path in "${required_paths[@]}"; do if [[ ! -e "$payload/$path" ]]; then echo "Required path is missing from Booster release: $path" >&2 exit 1 fi done if [[ ! -f "$payload/pkg/aerocore-booster.sh" ]]; then echo "Booster launcher is missing from the release payload" >&2 exit 1 fi # Remove the first implementation's image-owned runtime when upgrading an # existing AeroCore OS image. The release seed itself remains immutable. rm -rf /usr/lib/aerocore-booster rm -f /usr/bin/aerocore-booster for unit in ac-avionics.service ac-cabin.service ac-wing.service \ ac-instruments.service; do rm -f "/usr/lib/systemd/system/$unit" done rm -rf "$INSTRUMENTS_SEED_ROOT" install -d -m 0755 "$INSTRUMENTS_SEED_ROOT" cp -a "$payload/." "$INSTRUMENTS_SEED_ROOT/" cat >"$INSTRUMENTS_SEED_ROOT/image-release" <