#!/bin/bash

## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## exception_handler_general | exception_handler_unchroot_unmount | exception_handler_unmount
## -> exception_handler_process_shared -> function_trace_function -> process_backtrace_function -> exception_handler_shell -> exception_handler_retry -> exception_handler_maybe_exit

true "$BASH_SOURCE INFO: begin"

if test -o xtrace ; then
   xtrace_was_set="true"
else
   xtrace_was_set="false"
fi

## Cannot be implemented in help-steps/parse-cmd since it runs later than this.
if printf '%s\n' "$@" | grep --fixed-strings -- "--debug" &>/dev/null; then
   dist_build_no_unset_xtrace=true
fi

if [ ! "$dist_build_no_unset_xtrace" = "true" ]; then
   dist_build_no_unset_xtrace=false
   ## This runs even before the command line parser. So this cannot easily be implemented as as '--debug' option.
   true "\
$BASH_SOURCE INFO: Disabling verbose/debug output (xtrace) (set +x) because dist_build_no_unset_xtrace=false. For more verbose output add the following build option: --debug"
   set +x
fi

#set -x
set -e
set -o pipefail
set -o errtrace
shopt -s extdebug

if [ "$CI" = "true" ]; then
   true "$BASH_SOURCE INFO: CI detected."
   export dist_build_non_interactive=true
   [ -n "$rsync_cmd" ] || rsync_cmd="echo simulate-only rsync"
fi

output_cmd_set() {
   if [ -o xtrace ]; then
      output_cmd=true
   else
      output_cmd=echo
   fi
}

build_run_function() {
   case $build_skip_run_functions in
   *"$@"*)
      true "${cyan}INFO: ${bold}Skipping${bold} function ${under}$1${eunder}, because variable build_skip_run_functions includes it.${reset}"
      return 0
      ;;
   esac

   true "INFO: ${under}Running${eunder} function ${under}$1${eunder}, because variable build_skip_run_functions does not include it..."
   "$@"
   true "INFO: ${under}End${eunder} of function ${under}$1${eunder} reached, ok."
}

error() {
   output_cmd_set
   $output_cmd "${blue}############################################################${reset}"
   $output_cmd "${bold}${under}ERROR:${eunder}${reset}"
   $output_cmd "          ${bold}\$0:${reset} ${under}$0${eunder}"
   $output_cmd "${bold}\$BASH_SOURCE:${reset} ${under}$BASH_SOURCE${eunder}${reset}"
   $output_cmd "      ${bold}${red}message:${reset} ${under}${@}${reset}"
   $output_cmd "${blue}############################################################${reset}"
   error_reason="${bold}message:${reset} ${@}
"
   error_ "See above! (There should be a bold, red message surrounded by blue hashtags (#).)"
   true
}

function_trace_function() {
   $output_cmd "INFO: BEGIN: $FUNCNAME"
   if [ -o xtrace ]; then
      set +x
      local xtrace_reenable="true"
   fi
   local frame temp one two
   frame="0"
   while caller >/dev/null 2>&1 $frame; do
      temp="$(caller $frame)" || true
      read -r one two _ <<< "$temp"
      if [ "$function_trace_result" = "" ] ; then
         function_trace_result="$two (line number: $one)"
      else
         function_trace_result="$function_trace_result
$two (line number: $one)"
      fi
      frame="$(( $frame + 1 ))" || true
   done
   function_trace_result="$(echo "$function_trace_result" | tac)" || true
   if [ "$xtrace_reenable" = "true" ]; then
      set -x
   fi
   $output_cmd "INFO: END  : $FUNCNAME"
}

## {{{ process_backtrace_function - GPL v2.0 (only)

process_backtrace_function() {
   ## function    : process_backtrace_function
   ## Copyright   : Gian Paolo Ghilardi
   ## LICENSE     : GPL v2.0 (only)
   ## Source      : http://stackoverflow.com/a/1438241/2605155
   ## Modification: Patrick Schleizer
   $output_cmd "INFO: BEGIN: $FUNCNAME"
   if [ -o xtrace ]; then
      set +x
      local xtrace_reenable="true"
   fi
   local TRACE CP CMDLINE PP loop_counter who_ami ps_p_output
   loop_counter=0
   who_ami="$(whoami)" || true
   TRACE=""
   CP="$$" ## PID of the script itself [1]
   while true ; do
      loop_counter="$(( loop_counter + 1 ))"
      if [ "$loop_counter" -ge 20 ]; then
         echo "$FUNCNAME ERROR: who_ami: $who_ami | user_name: $user_name | loop counter greater than 20. Aborting $FUNCNAME."
         break
      fi
      if [ "$CP" = "" ]; then
         echo "$FUNCNAME ERROR: who_ami: $who_ami | user_name: $user_name | Variable CP is empty."
         break
      fi
      if [ "$CP" = "1" ]; then
         ## we reach 'init' [PID 1] => backtrace end
         TRACE="$TRACE : init\n"
         break
      fi
      if test -f "/proc/$CP/cmdline" ; then
         CMDLINE="$(cat "/proc/$CP/cmdline" | tr '\000' ' ')" || true
      else
         ps_p_output="$(ps --no-headers -p "$CP")"
         echo "$FUNCNAME ERROR: who_ami: $who_ami | user_name: $user_name | /proc/$CP/cmdline does not exist | ps_p_output: $ps_p_output"
      fi
      if test -f "/proc/$CP/status" ; then
         PP="$(grep -- PPid "/proc/$CP/status" | awk '{ print $2; }')" || true
      else
         ps_p_output="$(ps --no-headers -p "$CP")"
         echo "$FUNCNAME ERROR: who_ami: $who_ami | user_name: $user_name | /proc/$CP/status does not exist | ps_p_output: $ps_p_output"
      fi
      TRACE="$TRACE : $CMDLINE\n"
      CP="$PP"
   done
   ## using tac to "print in reverse" [3]
   process_backtrace_result="$(echo -en "$TRACE" | tac | grep --line-number -- ":")" || true
   if [ "$xtrace_reenable" = "true" ]; then
      set -x
   fi
   $output_cmd "INFO: END  : $FUNCNAME"
}

## }}} process_backtrace_function - GPL v2.0 (only)

exception_handler_shell() {
   $output_cmd "${cyan}${bold}INFO: Opening interactive shell...${reset}"
   $output_cmd "${cyan}${bold}INFO: When you have finished, please enter \"exit 0\" to \
continue (Recommended against!) or \"exit 1\" to cleanup and exit. (Recommended.)${reset}"
   interactive_chroot_shell_bash_exit_code="0"
   chroot_run /bin/bash || { interactive_chroot_shell_bash_exit_code="${PIPESTATUS[0]}" ; true; };
   if [ "$interactive_chroot_shell_bash_exit_code" = "0" ]; then
      $output_cmd "${cyan}${bold}INFO: Interactive shell terminated with \
exit code $interactive_chroot_shell_bash_exit_code, will ignore this error and continue...${reset}"
      ignore_error="true"
   else
      $output_cmd "${cyan}${bold}INFO: Interactive shell termited with \
exit code $interactive_chroot_shell_bash_exit_code, cleanup and exit as requested...${reset}"
      ignore_error="false"
   fi
}

exception_handler_retry() {
   if [ ! "$dist_build_dispatch_before_retry" = "" ]; then
      $output_cmd "${cyan}${bold}INFO: dispatch before retry (--retry-before)...: $dist_build_dispatch_before_retry ${reset}"
      dist_build_dispatch_before_retry_exit_code="0"
      eval $dist_build_dispatch_before_retry || { dist_build_dispatch_before_retry_exit_code="$?" ; true; };
      if [ "$dist_build_dispatch_before_retry_exit_code" = "0" ]; then
         $output_cmd "${cyan}${bold}INFO: dispatch before retry (--retry-after) exit code was 0, ok. ${reset}"
      else
         $output_cmd "${red}${bold}INFO: dispatch before retry (--retry-before) non-zero exit code: $dist_build_dispatch_before_retry_exit_code ${reset}"
      fi
   else
      $output_cmd "INFO: Skipping dist_build_dispatch_before_retry (--retry-before), because empty, ok."
   fi

   $output_cmd "${cyan}${bold}INFO: Retrying last_failed_bash_command...: $last_failed_bash_command ${reset}"
   retry_last_failed_bash_command_exit_code="0"
   $last_failed_bash_command || { retry_last_failed_bash_command_exit_code="$?" ; true; };
   if [ "$retry_last_failed_bash_command_exit_code" = "0" ]; then
      $output_cmd "${cyan}${bold}INFO: Retry succeeded. exit code of last_failed_bash_command: $retry_last_failed_bash_command_exit_code ${reset}"
      unset dist_build_auto_retry_counter
   else
      $output_cmd "${red}${bold}INFO: Retry failed. exit code of last_failed_bash_command: $retry_last_failed_bash_command_exit_code ${reset}"
      last_failed_exit_code="$retry_last_failed_bash_command_exit_code"
      last_failed_bash_command="$last_failed_bash_command"
   fi

   if [ ! "$dist_build_dispatch_after_retry" = "" ]; then
      $output_cmd "${cyan}${bold}INFO: dispatch after retry (--retry-after)...: $dist_build_dispatch_after_retry ${reset}"
      dist_build_dispatch_after_retry_exit_code="0"
      eval $dist_build_dispatch_after_retry || { dist_build_dispatch_after_retry_exit_code="$?" ; true; };
      if [ "$dist_build_dispatch_after_retry_exit_code" = "0" ]; then
         $output_cmd "${cyan}${bold}INFO: dispatch after retry (--retry-after) exit code was 0, ok. ${reset}"
      else
         $output_cmd "${red}${bold}INFO: dispatch after retry (--retry-after) non-zero exit code: $dist_build_dispatch_after_retry_exit_code ${reset}"
      fi
   else
      $output_cmd "INFO: Skipping dist_build_dispatch_after_retry (--retry-after), because empty, ok."
   fi

   if [ "$retry_last_failed_bash_command_exit_code" = "0" ]; then
      return 0
   else
      exception_handler_process_shared "NONE_(called_by_exception_handler_retry)"
   fi
}

exception_handler_process_shared() {
   last_script="$0"
   ## $1 contains trap signal type.
   trap_signal_type_previous="$trap_signal_type_last"
   if [ "$trap_signal_type_previous" = "" ]; then
      trap_signal_type_previous="unset"
   fi
   trap_signal_type_last="$1"
   dist_build_error_counter="$(( $dist_build_error_counter + 1 ))"
   benchmark_took_time="$(benchmarktimeend "$benchmark_time_start")" || true
   local first
   read -r first _ <<< "$last_failed_bash_command" || true

   process_backtrace_function ## sets process_backtrace_result
   function_trace_function ## sets function_trace_result
   output_cmd_set
   $output_cmd "
${cyan}${bold}############################################################${reset}
${red}${bold}ERROR detected in script!: ${under}$last_script${eunder}${reset}

${blue}${bold}#####${reset}
${blue}${bold}User Help Message 2/2:${reset}

${cyan}Please READ this message carefully.${reset}

Copying/pasting/screenshotting this box alone will not be insightful, and no help can be provided with it alone as it may not contain sufficient information by itself.

In many instances, providing a longer segment above this box or the entire log may be necessary for an effective diagnosis.
${blue}${bold}#####${reset}

dist_build_version: ${under}$dist_build_version${eunder}
dist_build_error_counter: $dist_build_error_counter
benchmark: $benchmark_took_time
last_failed_exit_code: $last_failed_exit_code
trap_signal_type_previous: $trap_signal_type_previous
trap_signal_type_last    : $trap_signal_type_last

${bold}process_backtrace_result${reset}:
$process_backtrace_result

${bold}function_trace_result${reset}:
$function_trace_result

$error_reason${red}${bold}last_failed_bash_command${reset}: ${bold}$last_failed_bash_command${reset}
${cyan}${bold}############################################################${reset}
"

   unset error_reason

   if [ "$trap_signal_type_last" = "INT" ] || [ "$trap_signal_type_last" = "TERM" ]; then
      $output_cmd "${red}${bold}INFO: signal $trap_signal_type_last received.${reset}"
      exception_handler_process_shared "NONE_(called_because_int_or_term)"
      ## Can return here, because next that will happen is receiving signal ERR.
      return 0
   fi

   if [ "$trap_signal_type_last" = "ERR" ] || [ "$trap_signal_type_last" = "NONE_(called_by_exception_handler_retry)" ] ; then
      #$output_cmd "INFO: trap_signal_type_last: $trap_signal_type_last, considering auto retry..."
      if [ ! "$dist_build_auto_retry" = "0" ]; then
         if [ "$dist_build_auto_retry_counter" = "" ]; then
            dist_build_auto_retry_counter="1"
         fi
         [ -n "$dist_build_auto_retry" ] || dist_build_auto_retry="1"
         [ -n "$dist_build_wait_auto_retry" ] || dist_build_wait_auto_retry="5"
         if [ "$first" = "error_" ]; then
            $output_cmd 'INFO: No auto retry because first item of last_failed_bash_command is "error_".'
         elif [ "$dist_build_auto_retry_counter" -gt "$dist_build_auto_retry" ]; then
            $output_cmd "${cyan}${bold}INFO: Auto retried (--retry-max) already $dist_build_auto_retry times. No more auto retry. ${reset}"
         else
            $output_cmd "${cyan}${bold}INFO: Auto retry attempt number: $dist_build_auto_retry_counter. Max retry attempts: $dist_build_auto_retry (--retry-max). Auto retry... ${reset}"
            dist_build_auto_retry_counter="$(( $dist_build_auto_retry_counter + 1 ))"
            if [ ! "$dist_build_wait_auto_retry" = "0" ]; then
               $output_cmd "${cyan}${bold}INFO: Waiting (--retry-wait) $dist_build_wait_auto_retry seconds before auto retry... ${reset}"
               sleep "$dist_build_wait_auto_retry" &
            fi
            wait "$!"
            ignore_error="$output_cmd"
            error_handler_do_retry="$output_cmd"
            exception_handler_retry
            return 0
         fi
      else
         $output_cmd "INFO: dist_build_auto_retry set to $dist_build_auto_retry (--retry-max). No auto retry."
      fi
   else
      $output_cmd "INFO: No auto retry for non ERR signal type: $trap_signal_type_last"
   fi
   unset dist_build_auto_retry_counter

   while true; do
      ## Default.
      ignore_error="false"

      answer=""
      if [ "$trap_signal_type_last" = "ERR" ] || [ "$trap_signal_type_last" = "NONE_(called_by_exception_handler_retry)" ] || [ "$trap_signal_type_last" = "NONE_(called_because_int_or_term)" ] ; then

         if [ "$dist_build_non_interactive" = "true" ]; then
            $output_cmd "INFO: using non-interactive error handler."
            break
         else
            if [ -t "0" ]; then
               $output_cmd "INFO: stdin connected to terminal, using interactive error handler."
               $output_cmd "\
${red}${bold}ERROR: An issue in ${under}$0${eunder} has been detected!${reset}

${blue}${bold}#####${reset}
${blue}${bold}User Help Message 1/2:${reset}

${cyan}Please READ this message carefully.${reset}

Copying/pasting/screenshotting this error message alone will not be insightful, and no help can be provided with it alone as it does not contain comprehensive information by itself. Instead, please scroll up and review the block encapsulated within ${cyan}${bold}###${reset} for more detailed information.

For support queries, it is essential to, at minimum, provide the portion of the log located above this message containing the actual error details. In many instances, providing a longer segment or the entire log may be necessary for an effective diagnosis.

Regrettably, assistance cannot be provided without the aforementioned details.

Options:

Choose ${under}either${eunder} option A), B), C) ${under}or${eunder} D).

 - A) Press ${blue}${bold}c${reset} and press enter to bypass this error and continue with the build. (For Developers Only!) (Strongly discouraged for regular users as it may lead to unstable builds! Please refrain from reporting any issues encountered subsequently!)
 - B) Press ${blue}${bold}r${reset} and enter to retry.
 - C) Press ${blue}${bold}s${reset} and enter to initiate a chroot interactive shell. (For Developers Only!)
 - D) Press ${blue}${bold}a${reset} and enter to abort.${reset}
${blue}${bold}#####${reset}"

               read -p "Answer? " answer
            else
               $output_cmd "INFO: stdin not connected to terminal, using non-interactive error handler."
               break
            fi
         fi
      else
         $output_cmd "INFO: Aborting because of non ERR signal type: $trap_signal_type_last"
         return 0
      fi
      error_handler_do_retry=""
      interactive_chroot_shell=""
      if [ "$answer" = "continue" ] || [ "$answer" = "c" ]; then
         ignore_error="true"
         interactive_chroot_shell="false"
         break
      elif [ "$answer" = "s" ] || [ "$answer" = "shell" ]; then
         interactive_chroot_shell="true"
         exception_handler_shell
         break
      elif [ "$answer" = "r" ] || [ "$answer" = "retry" ]; then
         ignore_error="true"
         interactive_chroot_shell="false"
         error_handler_do_retry="true"
         exception_handler_retry
         break
      elif [ "$answer" = "a" ]; then
         ignore_error="false"
         interactive_chroot_shell="false"
         break
      else
         $output_cmd "${red}${bold}${under}ERROR: Invalid answer!${reset}"
         continue
      fi
   done
}

if [ "$dist_build_error_counter" = "" ]; then
   dist_build_error_counter="0"
fi

exception_handler_maybe_exit() {
   if [ "$error_handler_do_retry" = "true" ]; then
      if [ "$last_failed_bash_command" = "0" ]; then
         true "INFO ($FUNCNAME): auto retry succeeded. Will not exit. Continue."
         return 0
      fi
   fi
   if [ "$ignore_error" = "true" ]; then
      abort_or_continue="Continue"
   else
      abort_or_continue="Aborted"
      ## Remove lockfile for systemcheck
      rm --force "/run/package_manager_lock"
   fi
   if [ "$ignore_error" = "true" ]; then
      $output_cmd "INFO: dist_build_non_interactive: $dist_build_non_interactive"
      if [ "$dist_build_non_interactive" = "true" ]; then
         $output_cmd "INFO: using non-interactive error handler."
      else
         $output_cmd "${red}${bold}You have chosen to ignore this error. Your build may be unstable!
This is recommended against unless you know what you are doing. Do not report
bugs, that are a result of this! Please press enter to continue. ${reset}"
      fi
   else
      if [ "$trap_signal_type_last" = "INT" ] || [ "$trap_signal_type_last" = "TERM" ]; then
         $output_cmd "INFO $FUNCNAME: trap_signal_type_last: $trap_signal_type_last"
         return 0
      fi
      trap - EXIT
      $output_cmd "${red}${bold}INFO: Now exiting from $last_script (because error was detected, see above) with exit code ${under}1${eunder}.${reset}"
      exit 1
   fi
}

exception_handler_general() {
   last_failed_exit_code="$?"
   last_failed_bash_command="$BASH_COMMAND"
   output_cmd_set
   $output_cmd "${cyan}INFO: Middle of function $FUNCNAME of $0.${reset}"
   ## $1 contains trap signal type.
   exception_handler_process_shared "$1"
   exception_handler_maybe_exit "$1"
   $output_cmd "${cyan}INFO: End of function $FUNCNAME of $0.${reset}"
}

exception_handler_unchroot_unmount() {
   last_failed_exit_code="$?"
   last_failed_bash_command="$BASH_COMMAND"
   output_cmd_set
   ## $1 contains trap signal type.
   exception_handler_process_shared "$1"
   if [ "$ignore_error" = "false" ]; then
      "$dist_source_help_steps_folder"/remove-local-temp-apt-repo "${args[@]}"
      "$dist_source_help_steps_folder"/unchroot-raw "${args[@]}"
      "$dist_source_help_steps_folder"/unprevent-daemons-from-starting "${args[@]}"
      "$dist_source_help_steps_folder"/unmount-raw "${args[@]}"
   fi
   exception_handler_maybe_exit "$1"
}

exception_handler_unmount() {
   last_failed_exit_code="$?"
   last_failed_bash_command="$BASH_COMMAND"
   output_cmd_set
   ## $1 contains trap signal type.
   exception_handler_process_shared "$1"
   if [ "$ignore_error" = "false" ]; then
      "$dist_source_help_steps_folder"/unmount-raw "${args[@]}"
   fi
   exception_handler_maybe_exit "$1"
}

## Thanks to:
## camh
## http://stackoverflow.com/a/2183063/2605155
exception_handler_setup() {
   local handler signal
   ## $1 contains the name of the error handler function.
   handler="$1" ; shift
   for signal ; do
       trap "$handler $signal" "$signal"
   done
}

output_cmd_set

exception_handler_setup "exception_handler_general" ERR INT TERM
set +e

export -f output_cmd_set
export -f exception_handler_shell
export -f exception_handler_retry
export -f exception_handler_process_shared
export -f exception_handler_maybe_exit
export -f exception_handler_general
export -f exception_handler_unchroot_unmount
export -f exception_handler_unmount
export -f exception_handler_setup
export -f function_trace_function
export -f process_backtrace_function

exception_handler_setup "exception_handler_general" ERR INT TERM

bash -n "$BASH_SOURCE"

maybe_set_plus_x() {
   if [ "$MINUS_X_SET" = "1" ]; then
      set +x
   fi
}

## Thanks to  perreal for the convertsecs function.
## http://stackoverflow.com/a/12199798
convertsecs() {
   local h m s
   ((h="${1}/3600")) || true
   ((m="(${1}%3600)/60")) || true
   ((s="${1}%60")) || true
   printf "%02d:%02d:%02d\n" "$h" "$m" "$s" || true
}

export -f convertsecs

benchmark_time_start() {
   export benchmark_time_start="$(date +%s)" || true
}

benchmarktimeend() {
   benchmarktimeend="$(date +%s)" || true
   benchmark_took_seconds="$(( $benchmarktimeend - $1 ))" || true
   echo "$(convertsecs "$benchmark_took_seconds")" || true
}

export -f benchmarktimeend

benchmark_time_start

exithandler() {
   local exit_code="$?"
   if [ ! "$exit_code" = "0" ]; then
      exit "$exit_code"
   fi
   benchmark_took_time="$(benchmarktimeend "$benchmark_time_start")" || true
   output_cmd_set
   ## XXX
   if [ "$exit_code" = "0" ]; then
      $output_cmd "${bold}INFO: Script ${under}$0${eunder} completed.${reset} Exit Code: ${under}$exit_code${eunder}. Errors Detected: ${under}$dist_build_error_counter${eunder}. Execution Time: $benchmark_took_time${reset}"
   else
      $output_cmd "${bold}${red}ERROR: Exiting ${under}$0${eunder} with non-zero exit code ${under}$exit_code${eunder}. Errors Detected: ${under}$dist_build_error_counter${eunder}. Execution Time: $benchmark_took_time${reset}."
   fi
   exit "$exit_code"
}

export -f exithandler

trap "exithandler" EXIT

root_check() {
   if [ "$(id -u)" = "0" ]; then
       $output_cmd "${red}${bold}ERROR: This must NOT be run as root (sudo)!${reset}"
       exit 1
   fi
   true "INFO: Script running as as non-root, ok."
   ## Not using 'sudo --non-interactive --validate' because that caused an
   ## error on an ansible server "sudo: a password is required". Potential bug in sudo?
   ## https://forums.whonix.org/t/derivative-maker-automated-ci-builder/14468/14
   true "INFO: Running 'sudo --non-interactive -- test -d /usr' to test if sudo password entry prompt is needed..."
   if sudo --non-interactive -- test -d /usr ; then
      true "INFO: sudo password already previously cached (entered) or this system has passwordless sudo, ok."
   else
      $output_cmd "INFO: Going to run 'sudo --validate' to prompt for password..."
      $output_cmd "${bold}INFO: Please enter sudo password.${reset}"
      ## sudo password prompt
      ## overwrite with '|| true' so sudo prompts only once (with the sudo default allowed retires).
      ## Avoiding the built-in auto retry in this script. Validation happens below.
      sudo --validate || true
      $output_cmd "INFO: Running 'sudo --non-interactive test -d /usr' to test if sudo password prompt succeeded..."
      if sudo --non-interactive -- test -d /usr ; then
         $output_cmd "INFO: sudo password prompt success."
      else
         $output_cmd "${red}${bold}ERROR: sudo password prompt entry failure!${reset}"
         error "sudo password prompt entry failure"
      fi
   fi
   true "INFO: Running 'sudo --non-interactive -- test -d /usr 2>&1' to test if output (stdout, stderr) is empty as expected (no warnings or error messages shown)..."
   ## SUDO_TO_ROOT unavailable here because set in file: help-steps/variables
   local sudo_test_output
   sudo_test_output="$(sudo --non-interactive -- test -d /usr 2>&1)" || true
   if [ ! "$sudo_test_output" = "" ]; then
      $output_cmd "${red}${bold}ERROR: Unexpected non-empty sudo command output!${reset}"
      $output_cmd "${red}${bold}ERROR: General system sudo issue. Most likely unrelated to this derivative-maker. Please manually run 'sudo --non-interactive -- test -d /usr' and fix your system issue.${reset}"
      $output_cmd "${red}${bold}sudo_test_output:${reset}"
      $output_cmd "${red}${bold}$sudo_test_output${reset}"
      error "sudo command output is non-empty!"
      return 0
   fi
   true "INFO: root_check ok."
}

nothing_to_commit_test() {
   local nothing_to_commit_msg git_status_last_line
   nothing_to_commit_msg="nothing to commit, working tree clean"
   git_status_last_line="$(git status | tail -n1)"
   if [ "${git_status_last_line}" = "${nothing_to_commit_msg}" ]; then
      return 0
   fi
   return 1
}

root_check

true "$BASH_SOURCE INFO: End of script, ok."
