#!/bin/sh # Copyright (c) 2017, 2021, 2023, 2024 Jonas 'Sortie' Termansen. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # tix-upgrade # Upgrade operating system and ports. set -e cachedir="" cancel=false clean=false collection=/ download_only=false fetch_options= sysroot="" upgrade=--upgrade upgrade_ports=false upgrade_system=false wait="" dashdash= previous_option= for argument do if test -n "$previous_option"; then eval $previous_option=\$argument previous_option= shift continue fi case $argument in *=?*) parameter=$(expr "X$argument" : '[^=]*=\(.*\)' || true) ;; *=) parameter= ;; *) parameter=yes ;; esac case $dashdash$argument in --) dashdash=yes ;; -w) wait=-w ;; --cachedir=*) cachedir=$parameter ;; --cachedir) previous_option=cachedir ;; --cancel) cancel=true ;; --clean) clean=true ;; --collection=*) collection=$parameter ;; --collection) previous_option=collection ;; --download-only) download_only=true ;; --fetch-options=*) fetch_options="$parameter" ;; --fetch-options) previous_option=fetch_options ;; --insecure-downgrade-to-http) fetch_options="$fetch_options $argument" ;; --insecure-no-check-certificate) fetch_options="$fetch_options $argument" ;; --no-upgrade) upgrade= ;; --ports) upgrade_ports=true ;; --system) upgrade_system=true ;; --sysroot) previous_option=sysroot ;; --sysroot=*) sysroot=$parameter ;; --wait) wait=--wait ;; -*) echo "$0: unrecognized option $argument" >&2 exit 1 ;; *) break ;; esac shift done if test -n "$previous_option"; then echo "$0: option '$argument' requires an argument" >&2 exit 1 fi if [ 0 -lt $# ]; then echo "$0: Unexpected extra operand: $1" >&2 exit 1 fi conf() { sed -E -e 's/([a-zA-Z0-9_]+) *? *= */\U\1=/' \ -e 's/=yes$/=true/' -e 's/no$/=false/' "$3" | \ tix-vars -d "$2" - "$4" } escape_extended_regex_sed() { printf "%s\n" "$1" | sed -E -e 's/[[$()*?\+.^{|}'"$2"']/\\\0/g' } collection=$(cd "$collection" && pwd) if ! $upgrade_ports && ! $upgrade_system; then upgrade_ports=true upgrade_system=true if [ -e "$collection/etc/upgrade.conf" ]; then upgrade_ports=$(conf -d true "$collection/etc/upgrade.conf" PORTS) upgrade_system=$(conf -d true "$collection/etc/upgrade.conf" SYSTEM) fi fi # If this isn't a system installation, only upgrade the ports. if [ ! -e "$collection/tix/manifest/system" ]; then upgrade_system=false fi case "$upgrade_system$upgrade_ports" in truefalse) what_to_upgrade=--system;; falsetrue) what_to_upgrade=--ports;; *) what_to_upgrade=;; esac if [ -z "$cachedir" ]; then cachedir="${collection%/}/var/cache/tix" fi if $cancel || $clean; then echo "Removing cache directory: $cachedir" rm -rf -- "$cachedir" fi if $cancel; then sysmerge -t "$collection" --cancel exit fi mkdir -p -- "$cachedir" mkdir -p -- "$cachedir/new" # Fetch the latest official signed release.sh and its matching sha256sum file. tix-fetch $fetch_options \ --collection="$collection" \ --output-release-file="$cachedir/new/release.sh" \ --output-sha256sum="$cachedir/new/sha256sum" \ --output-upgrade-file="$cachedir/new/upgrade.sh" \ $upgrade # If release.sh or sha256sum changed, clean the cache directory of downloads # that were currently in progress as they might not have the right checksums. if [ ! -e "$cachedir/release.sh" ] || [ ! -e "$cachedir/sha256sum" ] || [ ! -e "$cachedir/upgrade.sh" ] || ! (cd "$cachedir/new" && sha256sum release.sh sha256sum upgrade.sh) | (cd "$cachedir" && sha256sum -cs); then rm -rf -- "$cachedir/boot" rm -rf -- "$cachedir/repository" rm -rf -- "$cachedir/sysroot" fi # Store the new release.sh and sha256sum files so we can resume the download # if cancelled and these files still match. mv -- "$cachedir/new/release.sh" "$cachedir/release.sh" mv -- "$cachedir/new/sha256sum" "$cachedir/sha256sum" mv -- "$cachedir/new/upgrade.sh" "$cachedir/upgrade.sh" rm -rf -- "$cachedir/new" # Check if we're upgrading to a new release. UPGRADE_SIG_URL=$(tix-vars -d '' "$cachedir/upgrade.sh" UPGRADE_SIG_URL) if [ -n "$UPGRADE_SIG_URL" ]; then UPGRADE_CHANNEL=$(tix-vars "$cachedir/upgrade.sh" UPGRADE_CHANNEL) UPGRADE_KEY=$(tix-vars "$cachedir/upgrade.sh" UPGRADE_KEY) UPGRADE_NAME=$(tix-vars "$cachedir/upgrade.sh" UPGRADE_NAME) if [ -n "$upgrade" ]; then echo "Upgrading to $UPGRADE_NAME." else echo "Ignoring available upgrade to $UPGRADE_NAME." fi fi # Decide what binary packages to upgrade. installed_packages=$(LC_ALL=C ls -- "$collection/tix/tixinfo") if $upgrade_system && $upgrade_ports; then upgrade_packages="$installed_packages" else upgrade_packages= for package in $installed_packages; do is_system=$(tix-vars -d false "$collection/tix/tixinfo/$package" SYSTEM) if ($upgrade_system && [ "$is_system" = true ]) || ($upgrade_ports && [ "$is_system" = false ]); then upgrade_packages="$upgrade_packages $package" fi done fi # TODO: Tracking sets like minimal/basic/full with new mandatory or recommended ports. mkdir -p -- "$cachedir/repository" tix-fetch $fetch_options \ --collection="$collection" \ --input-release-file="$cachedir/release.sh" \ --input-sha256sum="$cachedir/sha256sum" \ --repository-metadata -O "$cachedir/repository" renames.list # Follow RENAMES recursively to handle renames, splits, and deletions. rename() { if grep -Eq "^$(escape_extended_regex_sed "$1"):" \ "$cachedir/repository/renames.list"; then for new in $(grep -E "^$(escape_extended_regex_sed "$1"):" \ "$cachedir/repository/renames.list" | grep -Eo '[^:]*$'); do rename "$new" done else echo "$1" | grep -Eo '^[^@]*' fi } # Determine the final package list after the renames. packages="" for package in $(LC_ALL=C ls -- "$collection/tix/tixinfo"); do edition=$(tix-vars -d 1 "$collection/tix/tixinfo/$package" EDITION) packages="$packages $(rename "$package@$edition")" done # Sort and deduplicate the package list and check for existence. packages=$(for package in $packages; do # The package exists upstream if it has a hash. if [ -n "$(tix-fetch $fetch_options \ --collection="$collection" \ --input-release-file="$cachedir/release.sh" \ --input-sha256sum="$cachedir/sha256sum" \ --sha256 --package -- $package)" ]; then echo $package fi done | LC_ALL=C sort -u) # Fetch each binary package from the mirror. for package in $packages; do tix-fetch $fetch_options \ --collection="$collection" \ --input-release-file="$cachedir/release.sh" \ --input-sha256sum="$cachedir/sha256sum" \ -c --package -O "$cachedir/repository" -- $package done # Stop if only downloading. if $download_only; then exit fi rm -rf -- "$cachedir/sysroot" mkdir -p -- "$cachedir/sysroot" # Forward the upgrade metadata. UPGRADE_SIG_URL=$(tix-vars -d '' "$cachedir/upgrade.sh" UPGRADE_SIG_URL) if [ -n $upgrade ] && [ -n "$UPGRADE_SIG_URL" ]; then mkdir -p -- "$cachedir/etc" # TODO: More flexible and simple model. cat > "$cachedir/etc/upgrade.conf" << EOF channel = $UPGRADE_CHANNEL release_key = $UPGRADE_KEY release_sig_url = $UPGRADE_SIG_URL EOF fi # Extract the binary packages into the sysroot. for package in $packages; do echo "Extracting $package.tix.tar.xz..." tar -C "$cachedir/sysroot" -xJf "$cachedir/repository/$package.tix.tar.xz" rm -f "$cachedir/repository/$package.tix.tar.xz" done # Merge the new sysroot onto the installation. sysmerge -t "$collection" --full $what_to_upgrade $wait "$cachedir/sysroot" rm -rf -- "$cachedir/repository" rm -rf -- "$cachedir/sysroot"