#! /usr/bin/env sh # nixos-config - My current NixOS configuration # # Copyright (C) 2025 Benedikt Peetz # SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of my nixos-config. # # You should have received a copy of the License along with this program. # If not, see . UPDATE_SCRIPT_NAME="update.sh" info() { echo "Info: $1" } dbg() { if [ "${DEBUG_ENABLE-unset}" != "unset" ]; then echo "Debug: $1" >&2 fi } die() { echo "Error: $1" exit 1 } # Search for a file “upwards”. # This will return the relative path from "$1" to the found file. # # # Type # upfind :: Path -> String -> Path # # # Arguments # $1 # : The directory to use as start of your search. # # $2 # : The file to search for. # # # Example # upfind "/home/user1" "/usr" # => /usr upfind() { starting_directory="$(readlink --canonicalize "$1")" search_string="$2" current_directory="$starting_directory" while search_result=$(fd "$search_string" "$current_directory/" --max-depth 1) dbg "upfind - search in $current_directory gives: $search_result" [ -z "$search_result" ] && [ "$current_directory" != "/" ] do current_directory=$(dirname "$current_directory"); done realpath --relative-to="$1" "$search_result" } # Construct the storage path for the update script allowed hashes. # # # Type # get_storage_path :: Path -> Path # # # Arguments # $1 # : The path to the update script # # # Returns # The constructed storage path. get_storage_path() { update_script="$(realpath "$1")" storage_path="$XDG_DATA_HOME/fupdate-flake/$update_script" echo "$storage_path" } # Checks if a given path to the update script is allowed. # # # Type # is_allowed :: Path -> bool # # # Arguments # $1 # : The path to the update script to check. # # # Return exit code # 0 # : If the update script is allowed # # 1 # : If it is not. is_allowed() { update_script="$(realpath "$1")" storage_path="$(get_storage_path "$update_script")" # Use this invocation, to also include the path to the `$update_script` update_script_hash="$(sha256sum "$update_script")" if [ -f "$storage_path" ]; then if [ "$(cat "$storage_path")" = "$update_script_hash" ]; then return 0 else return 1 fi else return 1 fi } # Asks the user if they want to allow a given script. # # # Type # ask_to_allow_update_script :: Path # # # Arguments # $1 # : The path to the update script to ask for. ask_to_allow_update_script() { update_script="$(realpath "$1")" printf "\033[2J" # clear the screen cat "$update_script" printf "Do you want to allow this script?[N/y]: " read -r allow case "$allow" in [yY]) info "Update script allowed." storage_path="$(get_storage_path "$update_script")" update_script_hash="$(sha256sum "$update_script")" mkdir --parents "$(dirname "$storage_path")" printf "%s" "$update_script_hash" >"$storage_path" ;; *) info "Update script not allowed." ;; esac } # Performs a full update. # This consists of running an update script. # Additionally, it also checks for duplicated inputs in a `flake.lock` file, if it exists. # # # Type # update :: Path -> Path -> [String] # # # Arguments # $1 # : The path to the update script to execute. # # $2 # : The base directory from which to start the update. # # $3 # : Arguments to pass to the update script. update() { update_script="$1" base_directory="$2" shift 2 cd "$base_directory" || die "The provided base directory '$base_directory' cannot be accessed" dbg "Changed directory to: $base_directory" dbg "Executing update script ('$update_script') following args: '$*'" "$update_script" "$@" if [ -f "flake.lock" ] && grep '[^0-9]_[0-9]' flake.lock --quiet; then batgrep '[^0-9]_[0-9]' flake.lock die "Your flake.nix contains duplicate inputs!" fi } main() { base_directory="$(git rev-parse --show-toplevel)" update_script="$(upfind "$PWD" "$UPDATE_SCRIPT_NAME")" dbg "update_script is: $update_script" if [ "$update_script" = "" ]; then die "Failed to find update script." elif is_allowed "$update_script"; then update "$update_script" "$base_directory" "$@" else ask_to_allow_update_script "$update_script" is_allowed "$update_script" && main "$@" fi } main # vim: ft=sh