#!/bin/bash { #prevents errors if script was modified while in use #$1 is an action, like install #$2 is app name, like Arduino DIRECTORY="$(readlink -f "$(dirname "$0")")" function error { echo -e "\e[91m$1\e[39m" exit 1 } if [[ $(id -u) == 0 ]]; then error "Artian-Apps is not designed to be run as root! Please try again as a regular user." fi if [ -z "$1" ];then error "You need to specify an operation, and in most cases, which app to operate on." fi set -a #make all functions in the api available to apps source "${DIRECTORY}/api" || error "failed to source ${DIRECTORY}/api" validate_apps_gui() { #Given a list of actions and apps, graphically notify the user if there is a problem or ask for confirmation. Result is sent to stdout. #action and app are separated by semicolon. (;) #example line of input on $1: "install;Zoom" local IFS=$'\n' local queue="$1" local action local app local line #Ensure that the first word of each line is 'install' or 'uninstall' or 'update' or 'refresh' or 'update-file' local i=1 #track the line number in queue for line in $queue ;do action="$(echo "$line" | awk -F ';' '{print $1}')" if [ "$action" != install ] && [ "$action" != uninstall ] && [ "$action" != update ] && [ "$action" != refresh ] && [ "$action" != update-file ];then warning "illegal mode: '$action' Removing this line from the queue" #remove the app from list queue="$(echo "$queue" | sed ${i}d)" fi i=$((i+1)) done [ -z "$queue" ] && exit 0 #Ensure that each app-name is valid local i=1 #track the line number in queue for line in $queue ;do action="$(echo "$line" | awk -F ';' '{print $1}')" app="$(echo "$line" | awk -F ';' '{print $2}')" if [ "$action" == update-file ];then # the current line is a pseudo-action used to update a file # skip app validation true elif ([ "$action" == update ] || [ "$action" == refresh ]) && [ ! -d "${DIRECTORY}/update/Artian-apps/apps/${app}" ];then yad --class Artian-Apps --name "Artian-Apps" --text="Invalid app "\""$app"\"". Cannot $action it." \ --text-align=center --center --title='Error' --window-icon="${DIRECTORY}/icons/logo.png" \ --button=OK!"${DIRECTORY}/icons/check.png":0 #remove the app from list queue="$(echo "$queue" | sed ${i}d)" elif [ "$action" != update ] && [ "$action" != refresh ] && [ ! -d "${DIRECTORY}/apps/${app}" ];then yad --class Artian-Apps --name "Artian-Apps" --text="Invalid app "\""$app"\"". Cannot $action it." \ --text-align=center --center --title='Error' --window-icon="${DIRECTORY}/icons/logo.png" \ --button=OK!"${DIRECTORY}/icons/check.png":0 #remove the app from list queue="$(echo "$queue" | sed ${i}d)" fi i=$((i+1)) done [ -z "$queue" ] && exit 0 #if trying to install an already-installed app, or trying to uninstall and already-uninstalled app, ask for confirmation i=1 #track the line number in queue for line in $queue ;do action="$(echo "$line" | awk -F ';' '{print $1}')" app="$(echo "$line" | awk -F ';' '{print $2}')" if [ "$action" == update-file ];then # the current line is a pseudo-action used to update a file # skip app validation true elif [ "$(app_status "${app}")" == "${action}ed" ];then yad --class Artian-Apps --name "Artian-Apps" --text="$app is already ${action}ed. Are you sure you want to $action it again?" \ --text-align=center --center --title='Quick question' --window-icon="${DIRECTORY}/icons/logo.png" \ --button=No!"${DIRECTORY}/icons/exit.png":1 --button=Yes!"${DIRECTORY}/icons/check.png":0 if [ $? != 0 ];then #user clicked No, so remove the app from list queue="$(echo "$queue" | sed ${i}d)" fi fi i=$((i+1)) done [ -z "$queue" ] && exit 0 #Check if any apps are updatable and ask user if they really want to INSTALL an outdated version. (This is skipped if uninstalling apps) local update_queue='' for line in $queue ;do action="$(echo "$line" | awk -F ';' '{print $1}')" app="$(echo "$line" | awk -F ';' '{print $2}')" if [ $action == install ];then #determine the filename for the app's script to be run script_name_cpu="$(script_name_cpu "$app")" #if the app-script doesn't match version in update folder if [ -f "${DIRECTORY}/update/Artian-apps/apps/${app}/${script_name_cpu}" ] && ! files_match "${DIRECTORY}/update/Artian-apps/apps/${app}/${script_name_cpu}" "${DIRECTORY}/apps/${app}/${script_name_cpu}" ;then "${DIRECTORY}/updater" set-status &>/dev/null & #check for updates in background, so if user chooses "Yes", the updater will be guaranteed to have the app listed yad --class Artian-Apps --name "Artian-Apps" --text="Hold up..."$'\n'"$app's $script_name_cpu script does not match the online version. Either you are about to install an outdated version, or you've made changes to the script yourself."$'\n\n'"Would you like to install the newest official version of $app?" \ --text-align=center --center --title='Quick question' --window-icon="${DIRECTORY}/icons/logo.png" --width=400 \ --image="${DIRECTORY}/apps/$app/icon-64.png" \ --button="I know what I am doing, Install current version"!"${DIRECTORY}/icons/forward.png":1 --button="Yes, Install newest official version"!"${DIRECTORY}/icons/download.png"!"In most cases, this is the button you should click.":0 if [ $? == 0 ];then if [ -z "$update_queue" ] && ! files_match "${DIRECTORY}/api" "${DIRECTORY}/update/Artian-apps/api" ;then #if user clicked Yes, update api (for potential new functions) and the app update_queue="update-file;api"$'\n'"refresh;$app" elif [ -z "$update_queue" ];then update_queue="refresh;$app" else update_queue+=$'\n'"refresh;$app" fi fi fi fi done if [ ! -z "$update_queue" ]; then # place all updates before queue input to validate_apps_gui queue="$update_queue"$'\n'"$queue" fi echo "$queue" } #remove old mcpi repositories - this runonce is here so that terminal-only users will still receive the fix. (runonce <<"EOF" if [ -f /etc/apt/sources.list.d/Alvarito050506_mcpi-devs.list ];then sudo rm -f /etc/apt/sources.list.d/Alvarito050506_mcpi-devs.list fi if [ -f /etc/apt/sources.list.d/mcpi-revival.list ];then sudo rm -f /etc/apt/sources.list.d/mcpi-revival.list sudo rm -f /etc/apt/trusted.gpg.d/mcpi-revival.gpg fi if dpkg -l box86-no-binfmt-restart &>/dev/null ;then sudo apt purge -y box86-no-binfmt-restart sudo apt update sudo apt install -y box86 fi EOF ) &>/dev/null #An apt repository's Packages file can be corrupted so that an apt update will silently fail. See: https://bugs.launchpad.net/ubuntu/+source/apt/+bug/1809174 #This line will fix the problem by removing any zero-size Packages files. removal_list="$(find /var/lib/apt/lists -type f -name '*Packages' -size 0 2>/dev/null)" if [ ! -z "$removal_list" ]; then if [ x$DISPLAY != x ] ; then while ! sudo -n true; do yad --class Artian-Apps --name "Artian-Apps" --title="Broken Local Packages Repo Detected" --text="Please enter your user password \nso Artian-apps can attempt a repair:" --image="dialog-password" --entry --hide-text 2>/dev/null | sudo -S echo "" 2>&1 >/dev/null done fi echo "$removal_list" | xargs sudo rm -f fi #Silently re-download repo if github repository is over 3 months out of date { #first compare local git repo's last-commit-time with the current system time (Unix epoch time format) current_git_date="$(cd "$DIRECTORY"; git show -s --format=%ct)" current_local_date="$(date +%s)" if [ -z "$current_git_date" ] || [ "$current_local_date" -gt $(($current_git_date + 7776000)) ];then #if local git repo's last-commit-time is 3 months older than current system time, now compare the local git repo's last-commit-time with the online repo's modification time to avoid false positives. #This two-tiered approach prevents unnecessary GitHub API calls and speeds up manage script's execution for normal usage. command -v curl >/dev/null || sudo apt install -y curl upstream_git_date="$(curl https://git.fdc.abrish.ir/a.kamyar/PublicFiles/Artian-Apps 2>&1 | grep '"date":' | tail -n 1 | sed 's/"date"://g' | xargs date +%s -d 2>/dev/null)" if [[ "$upstream_git_date" =~ ^[0-9]+$ ]] && ([ -z "$current_git_date" ] || [ "$upstream_git_date" -gt $(($current_git_date + 7776000)) ]);then yad --class Artian-Apps --name "Artian-Apps" --window-icon="${DIRECTORY}/icons/logo.png" --width=500 --no-buttons --center --title="Auto-updating Artian-Apps" \ --text="Your Artian-Apps installation is somehow 3 months out-of-date."$'\n'"Reinstalling Artian-Apps and saving the old version to ${DIRECTORY}-3-months-old..." & yadpid=$! clear echo -e "\nYour Artian-Apps installation is somehow 3 months out-of-date.\nReinstalling Artian-Apps and saving the old version to ${DIRECTORY}-3-months-old...\n\n" 1>&2 cd $HOME rm -rf ~/Artian-apps-forced-update command -v git >/dev/null || sudo apt install -y git git clone "$(cat "${DIRECTORY}/etc/git_url")" Artian-apps-forced-update 1>&2 && \ cp -af "${DIRECTORY}/data" ~/Artian-apps-forced-update && \ cp -af "${DIRECTORY}/apps" ~/Artian-apps-forced-update && \ mv -f "$DIRECTORY" "${DIRECTORY}-3-months-old" && \ mv -f ~/Artian-apps-forced-update "$DIRECTORY" sleep 10 kill $yadpid 2>/dev/null #run new manage script in background "${DIRECTORY}/manage" "$@" #run updater to update apps "${DIRECTORY}/updater" gui exit $? fi fi } mkdir -p "${DIRECTORY}/data/status" "${DIRECTORY}/data/update-status" "${DIRECTORY}/logs" #remove week-old logfiles find "${DIRECTORY}/logs" -type f -mtime +6 -exec rm -f {} \; &>/dev/null & #check if hardware and OS is supported if [ "$supported" == "yes" ] || [ "$supported" == "no" ]; then true else if is_supported_system >/dev/null;then export supported=yes export unsupported_message='' else export supported=no export unsupported_message="$(is_supported_system)" fi fi if [ "$1" == 'daemon' ];then #Daemon to run in the background and install/uninstall/update apps as the user makes selections #This allows the user to queue up a list of actions that will run sequentially. #this folder will contain the runtime files necessary to make the daemon work. mkdir -p "${DIRECTORY}/data/manage-daemon" #a list of pending steps is displayed with yad. This function adds an item to the list add_to_list() { # $1 is action, $2 is app, $3 is exit code status ('0' = success), $4 is output location (usually blank, but can be 'stdout') local action="$1" local app="$2" local status="$3" # $4 specifies where the output should go. Default is to send the output to the yadlist pipe, but if value is 'stdout', output to stdout. local output="$4" #choose an icon for this app or file being updated/installed if [ -f "${DIRECTORY}/apps/$app/icon-24.png" ];then local icon="${DIRECTORY}/apps/$app/icon-24.png" elif [ "$action" == update-file ];then #determine mimetype of file to display an informative icon in the list if [ "$(file -b --mime-type "${DIRECTORY}/${app}")" == 'text/x-shellscript' ];then #if updatable file in question is a main Artian-apps shell script, then display shellscript icon. local icon="${DIRECTORY}/icons/shellscript.png" app+=' (script)' elif [[ "${DIRECTORY}/${app}" == *.png ]] || [[ "${DIRECTORY}/${app}" == *.svg ]];then local icon="${DIRECTORY}/icons/image.png" app+=' (image)' else #otherwise display txt icon. local icon="${DIRECTORY}/icons/txt.png" app+=' (file)' fi action=update else local icon="${DIRECTORY}/icons/none-24.png" fi if [ -z "$status" ];then #if there is no status number, then this action has not occured yet. local content="${DIRECTORY}/icons/wait.png ${DIRECTORY}/icons/$action.png Will $action $icon $app" elif [ "$status" == 0 ];then #if status is 0, then action completed successfully. local content="${DIRECTORY}/icons/success.png ${DIRECTORY}/icons/$action.png $(echo "${action^}ed" | sed 's/Updateed/Updated/g') $icon $app" elif [ "$status" == 'in-progress' ];then #if status is "in-progress", then action is currently being executed. local content="${DIRECTORY}/icons/prompt.png ${DIRECTORY}/icons/$action.png $(echo "${action^}ing..." | sed 's/Updateing/Updating/g') $icon $app" else #if status is 1, then action completed unsuccessfully. local content="${DIRECTORY}/icons/failure.png ${DIRECTORY}/icons/$action.png ${action^} failed $icon $app" fi #write the output to yadlist or stdout if [ "$output" == stdout ];then echo "$content" else echo "$content" > "${DIRECTORY}/data/manage-daemon/yadlist" fi } clear_list() { #clear the queue-viewer window echo -e '\f' > "${DIRECTORY}/data/manage-daemon/yadlist" } write_list() { #given $queue in $1, rebuild the queue-viewer window. This avoids clearing the list until the new one has been generated, so makes for a smoother experience local queue="$1" local output='' #variable to send to yadlist file local IFS=$'\n' local line for line in $queue ;do local action="$(echo "$line" | awk -F';' '{print $1}')" local app="$(echo "$line" | awk -F';' '{print $2}')" local code="$(echo "$line" | awk -F';' '{print $3}')" output+="$(add_to_list "$action" "$app" "$code" stdout)"$'\n' done output="${output::-1}" #remove final newline character clear_list echo "$output" > "${DIRECTORY}/data/manage-daemon/yadlist" } reorder_list() { # given $queue in $1, output a new queue with app refreshes and file updates prioritized before install/uninstalls local queue="$1" # only reorder queue for actions that have not completed yet (no exit code present) local queue_pending local queue_pending_refresh local queue_pending_files local queue_pending_other local queue_completed local queue_reordered local IFS=$'\n' local line for line in $queue ;do if ! [[ "$(echo "$line" | awk -F';' '{print $3}')" =~ ^[0-9]+$ ]] ; then # echo "$line is still pending" 1>&2 queue_pending+="$line"$'\n' else # echo "$line has been completed already" 1>&2 queue_completed+="$line"$'\n' fi done [ ! -z "$queue_pending" ] && queue_pending="${queue_pending::-1}" #remove final newline character [ ! -z "$queue_completed" ] && queue_reordered+="$queue_completed" #already includes trailing newline for line in $queue_pending ;do if [[ "$line" =~ ^"refresh;".* ]] ; then # echo "$line has the refresh action" 1>&2 queue_pending_refresh+="$line"$'\n' elif [[ "$line" =~ ^"update-file;".* ]];then # echo "$line has the update-file action" 1>&2 queue_pending_files+="$line"$'\n' else # echo "$line has some other action" 1>&2 queue_pending_other+="$line"$'\n' fi done [ ! -z "$queue_pending_files" ] && queue_reordered+="$queue_pending_files" #already includes trailing newline [ ! -z "$queue_pending_refresh" ] && queue_reordered+="$queue_pending_refresh" #already includes trailing newline [ ! -z "$queue_pending_other" ] && queue_reordered+="$queue_pending_other" #already includes trailing newline queue_reordered="${queue_reordered::-1}" #remove final newline character echo "$queue_reordered" } #make a named pipe so that other daemon processes can notify this master daemon process to complete new tasks if [ ! -e "${DIRECTORY}/data/manage-daemon/queue" ];then mkfifo "${DIRECTORY}/data/manage-daemon/queue" fi #each line in $2 is something like "install Zoom" or "uninstall Arduino" queue="$2" #To simplify parsing, place a ';' character between $1 (the action) and subsequent args. (the app) queue="$(echo "$queue" | sed 's/^\( *[^ ]\+\) /\1;/')" #validate the selections first queue="$(validate_apps_gui "$queue")" [ -z "$queue" ] && exit 0 #send each requested action to the queue file if [ ! -z "$queue" ];then echo "$queue" > "${DIRECTORY}/data/manage-daemon/queue" & fi #only one instance of this script should ever be running at a time. #Use a PID file to check if another daemon process is running. if [ -f "${DIRECTORY}/data/manage-daemon/pid" ];then #check if PID is running if process_exists $(cat "${DIRECTORY}/data/manage-daemon/pid") ;then echo "Sending instructions to daemon. (PID $(cat "${DIRECTORY}/data/manage-daemon/pid"))" #Immediately add these new actions to the gui list IFS=$'\n' for line in $queue ;do #get first word of this line - the action. Subsequent words are the name of the app. action="$(echo "$line" | awk -F ';' '{print $1}')" app="$(echo "$line" | awk -F ';' '{print $2}')" add_to_list "$action" "$app" done #exit script - data has been sent to already-running daemon exit 0 fi fi #past this point, this instance is acting as the daemon. #write my own PID to the pid file echo $$ > "${DIRECTORY}/data/manage-daemon/pid" #Display a list of actions and their current status. #This list is updated with new information as time progresses. #Another named pipe is created to refresh the yad list later. rm -f "${DIRECTORY}/data/manage-daemon/yadlist" mkfifo "${DIRECTORY}/data/manage-daemon/yadlist" #make a named pipe [ -z "$geometry2" ] && geometry2='--center' tail -f --retry "${DIRECTORY}/data/manage-daemon/yadlist" | yad --class Artian-Apps --name "Artian-Apps" --width=330 --height=400 "$geometry2" --title='Monitor Progress' \ --list --tail --no-headers --column=:IMG --column=:IMG --column=Text --column=:IMG --column=Text \ --separator='\n' --window-icon="${DIRECTORY}/icons/logo.png" \ --dclick-action=true --select-action=true \ --no-buttons & yadpid=$! trap "kill $yadpid 2>/dev/null" EXIT #Used to track which line of $queue is currently being dealt with. current_line_num=1 sourced_updater=0 #updater script needs to be sourced if files are updated. This allows it to only be sourced once. queue='' IFS=$'\n' while true;do #repeat until nothing is left in the queue #check for new actions to be executed echo -n > "${DIRECTORY}/data/manage-daemon/queue" & #ensure that the pipe is in write mode to prevent cat from hanging if the queue file is empty new_lines="$(tac "${DIRECTORY}/data/manage-daemon/queue")" # tac reverses the order of the list. a plain cat of the file will give the newest item in the queue first. #keep track of all actions for this session with the $queue variable if [ -z "$queue" ];then queue="$new_lines" elif [ ! -z "$new_lines" ];then # add new_lines to queue if new_lines is not empty queue+=$'\n'"$new_lines" fi # reorder queue list to prioritize app refresh and file update actions queue="$(reorder_list "$queue")" if [ ! -z "$new_lines" ] && [ "$sourced_updater" == 0 ] && grep -q "update\|refresh\|update-file" <<<"$new_lines" ;then #source updater if necessary source "${DIRECTORY}/updater" source sourced_updater=1 fi #echo "length of queue is $(echo "$queue" | wc -l)" #exit loop if queue is complete and no new actions were added to the queue if [ "${current_line_num}" -gt "$(echo "$queue" | wc -l)" ];then break fi #echo "current position in queue is ${current_line_num}" #echo "queue is '$queue'" line="$(echo "$queue" | sed -n "${current_line_num}"p)" #echo "Now handling request: '$line'" #indicate current action in current line of $queue queue="$(echo "$queue" | sed "${current_line_num}s/$/;in-progress/")" #get first word of this line - the action. Subsequent words are the name of the app. action="$(echo "$line" | awk -F';' '{print $1}')" app="$(echo "$line" | awk -F';' '{print $2}')" #Set terminal title echo -ne "\e]0;${action^}ing ${app}\a" | sed 's/Updateing/Updating/g' #refresh the list in queue-viewer window as a background process - skip it if the list is still refreshing from last loop iteration; in game dev this is 'dropped input' if [ -z "$write_list_pid" ] || ! process_exists "$write_list_pid" ;then write_list "$queue" & write_list_pid=$! #secondary list-writing background process - kill it if it exists because write_list just sent a newer version of the list to yad [ ! -z "$secondary_write_list_pid" ] && process_exists "$secondary_write_list_pid" && kill "$secondary_write_list_pid" else #if app1 is refreshed and app2 is then reinstalled, the list would only say app1 is bring refreshed for the entirety of app2's reinstallation, due to the process-skipping. #launch a secondary background process that waits for $write_list_pid to finish #only allow one secondary background process to run; kill previous jobs and start a new one [ ! -z "$secondary_write_list_pid" ] && process_exists "$secondary_write_list_pid" && kill "$secondary_write_list_pid" (while process_exists $write_list_pid ;do sleep 1 ;done ; write_list "$queue") & secondary_write_list_pid=$! fi #run manage script for app installs, uninstalls, or updates. Avoid using it for file-updates and refreshes because that is out of the scope for manage script. if [ "$action" == update-file ];then update_file "$app" exitcode=$? elif [ "$action" == refresh ];then refresh_app "$app" exitcode=$? else "${DIRECTORY}/manage" "$action" "$app" exitcode=$? fi #record exit code in current line of $queue queue="$(echo "$queue" | sed "${current_line_num}s/;in-progress$/;$exitcode/")" #one more line of $queue has been completed. current_line_num=$((current_line_num+1)) done #refresh the list in queue-viewer window for the final time with all actions complete [ ! -z "$secondary_write_list_pid" ] && process_exists "$secondary_write_list_pid" && kill "$secondary_write_list_pid" #kill secondary list writer wait $write_list_pid write_list "$queue" #before exiting the loop, add a line to the queue-viewer window indicating that all items have completed. echo "${DIRECTORY}/icons/none-1.png ${DIRECTORY}/icons/none-1.png Done. ${DIRECTORY}/icons/none-1.png " > "${DIRECTORY}/data/manage-daemon/yadlist" #all actions have been completed. Daemon has effectively stopped listening, so remove its pid file rm -f "${DIRECTORY}/data/manage-daemon/pid" #close the queue-viewer window in a few seconds (sleep 5; kill $yadpid 2>/dev/null) & #diagnose every failed app's logfile - list item format is $action;$app;$exitcode failed_apps="$(echo "$queue" | grep ';1$' | awk -F';' '{print $2}')" diagnose_apps "$failed_apps" # if update refresh or update-file actions were run then update the .git folder if [ "$sourced_updater" == 1 ]; then update_git fi #updates could have been run as part of the manage-daemon, so update the updatable-files and updatable-apps status files "${DIRECTORY}/updater" set-status elif [ "$1" == 'multi-uninstall' ] || [ "$1" == 'multi-install' ];then if [ "$1" == 'multi-uninstall' ];then action=uninstall elif [ "$1" == 'multi-install' ];then action=install fi app_list="$2" #newline-separated list of apps to install/uninstall #check if any app names are invalid - use the validate_apps_gui function which requires the action to prefix each line. queue="$(validate_apps_gui "$(echo "$app_list" | sed "s/^/${action};/g")")" [ -z "$queue" ] && exit 0 if grep -q "update\|refresh\|update-file" <<<"$queue" ;then #source updater if necessary source "${DIRECTORY}/updater" source fi #install/uninstall one app at a time. If it fails then add the app to the list of failed apps IFS=$'\n' failed_apps='' for line in $queue ;do action="$(echo "$line" | awk -F ';' '{print $1}')" app="$(echo "$line" | awk -F ';' '{print $2}')" #Set terminal title echo -ne "\e]0;${action^}ing ${app}\a" #run manage script for app installs, uninstalls, or updates. Avoid using it for file-updates and refreshes because that is out of the scope for manage script. if [ "$action" == update-file ];then update_file "$app" exitcode=$? elif [ "$action" == refresh ];then refresh_app "$app" exitcode=$? else "${DIRECTORY}/manage" "$action" "$app" exitcode=$? fi if [ $exitcode != 0 ];then #this app failed to install - add it to the list of failed apps failed_apps+="$app"$'\n' fi done if [ ! -z "$failed_apps" ];then exit 1 fi elif [ "$1" == 'install-if-not-installed' ];then #if not installed if [ "$(app_status "$2")" != installed ];then #install it "${DIRECTORY}/manage" install "$2" || exit 1 fi elif [ "$1" == 'install' ] || [ "$1" == 'uninstall' ];then #for this operation, a program name must be specified. app="$2" if [ -z "$app" ];then error "For this operation, you must specify which app to operate on." elif [ ! -d "${DIRECTORY}/apps/$app" ];then error "${DIRECTORY}/apps/$app does not exist!" fi if [ "$1" == install ];then action=install else action=uninstall fi if [ "$action" == install ];then #check for internet connection errors="$(command wget --spider https://github.com 2>&1)" if [ $? != 0 ];then error "No internet connection! (github.com failed to respond)\nErrors:\n$errors" fi fi #ensure not a disabled app if [ "$action" == install ] && [ "$(app_status "${app}")" == 'disabled' ];then warning "Not installing the $app app. IT IS DISABLED." exit 0 fi #determine path for log file to be created logfile="${DIRECTORY}/logs/${action}-incomplete-${app}.log" if [ -f "$logfile" ] || [ -f "$(echo "$logfile" | sed 's+-incomplete-+-success-+g')" ] || [ -f "$(echo "$logfile" | sed 's+-incomplete-+-fail-+g')" ];then #append a number to logfile's file-extension if the original filename already exists i=1 while true;do #if variable $i is 2, then example newlogfile value: /path/to/install-Discord.log2 newlogfile="$logfile$i" if [ ! -f "$newlogfile" ] && [ ! -f "$(echo "$newlogfile" | sed 's+/-incomplete-+-success-+g')" ] && [ ! -f "$(echo "$newlogfile" | sed 's+-incomplete-+-fail-+g')" ];then logfile="${newlogfile}" break fi i=$((i+1)) done fi #display warning if hardware and os is unsupported if [ "$supported" == no ];then warning "YOUR SYSTEM IS UNSUPPORTED:\n$unsupported_message" 2>&1 | tee -a "$logfile" sleep 1 echo -e "\e[103m\e[30mThe ability to send error reports has been disabled.\e[39m\e[49m" | tee -a "$logfile" sleep 1 echo -e "\e[103m\e[30mWaiting 10 seconds... (To cancel, press Ctrl+C or close this terminal)\e[39m\e[49m" | tee -a "$logfile" sleep 10 fi #if this app has scripts, determine which script to run if [ "$(app_type "$app")" == standard ];then if [ "$action" == install ];then scriptname="$(script_name_cpu "$app")" #will be install, install-32, or install-64 if [ -z "$scriptname" ];then error "It appears $app does not have an install-${arch} script suitable for your ${arch}-bit OS." | tee -a "$logfile" exit 1 fi else #uninstall mode scriptname=uninstall fi appscript=("${DIRECTORY}/apps/${app}/${scriptname}") chmod u+x "$appscript" &>/dev/null #if this app just lists a package-name, set the appscript to install that package else appscript=(bash -c -o pipefail "apt_lock_wait ; sudo -E apt $(echo "$action" | sed 's/uninstall/purge --autoremove/g') -yf $(cat "${DIRECTORY}/apps/$app/packages") 2>&1 | less_apt") fi #print to terminal status "${action^}ing \e[1m${app}\e[0m\e[96m..." | tee -a "$logfile" echo cd $HOME if [ "$3" == "update" ]; then script_input="update" else script_input="" fi nice "${appscript[@]}" "$script_input" &> >(tee -a "$logfile") exitcode="${PIPESTATUS[0]}" #if app succeeded if [ $exitcode == 0 ];then #Contribute to app install/uninstall count as long as parent processes is NOT updater (during an app-reinstall) #See: https://askubuntu.com/a/1012236 if [ "$(cat /proc/$PPID/comm)" != "updater" ] && [ "$3" != "update" ];then shlink_link "$app" "$action" & fi status_green "\n${action^}ed ${app} successfully." | tee -a "$logfile" echo "${action}ed" > "${DIRECTORY}/data/status/${app}" format_logfile "$logfile" #remove escape sequences from logfile mv "$logfile" "$(echo "$logfile" | sed 's+-incomplete-+-success-+g')" #rename logfile to indicate it was successful else #if app failed to install/uninstall #remove dummy deb if app failed to install (to avoid dummy debs being left on a users install for broken apps) package_name="$(app_to_pkgname "$app")" if [[ ${action} == "install" ]] && [[ "$(app_type "$app")" == standard ]] && package_installed "$package_name"; then #Run purge_packages echo $'\n'"Running purge_packages..." >> "$logfile" purge_packages 2>&1 | tee -a "$logfile" >/dev/null fi unset package_name echo -e "\n\e[91mFailed to ${action} ${app}!\e[39m \e[40m\e[93m\e[5m◢◣\e[25m\e[39m\e[49m\e[93mNeed help? Copy the \e[1mENTIRE\e[0m\e[49m\e[93m terminal output or take a screenshot. Please ask on Github: \e[94m\e[4mhttps://git.fdc.abrish.ir/a.kamyar/PublicFiles/Artian-Apps\e[24m\e[93m Or on Discord: \e[94m\e[4mhttps://discord.gg/RXSTvaUvuu\e[0m" | tee -a "$logfile" #set the app's status to 'corrupted' if the diagnostics determine the error is NOT internet or system or package, AND if the app is a script-type app if ! [[ "$(log_diagnose "$logfile" | head -n1)" =~ ^(system|internet|package)$ ]] && [ "$(app_type "$app")" == standard ];then echo "corrupted" > "${DIRECTORY}/data/status/${app}" fi format_logfile "$logfile" #remove escape sequences from logfile mv "$logfile" "$(echo "$logfile" | sed 's+-incomplete-+-fail-+g')" #rename logfile to indicate it was unsuccessful fi #if the app is a package, set its status to whatever dpkg thinks it is if [ "$(app_type "$app")" == package ];then refresh_pkgapp_status "$app" fi #exit the manage script with the same exit-code of the app's script exit $exitcode elif [ "$1" == 'update' ];then #user-facing argument to update an app. Artian-Apps scripts should directly use updater's update_app function. #for this operation, a program name must be specified. app="$2" if [ -z "$app" ];then error "For this operation, you must specify which app to operate on." fi # make sure update_app function is available typeset -f update_app &>/dev/null || source "${DIRECTORY}/updater" source update_app "$app" || exit $? elif [ "$1" == 'check-all' ];then #The manage script no longer handles updates. This mode is only for backwards-compatibility and uses the updater script warning "The manage script ONLY updates apps, and this mode has been replaced by the updater script. If you want to update Artian-Apps from the command-line, please use: ~/Artian-apps/updater cli-yes" #get functions from updater script source "${DIRECTORY}/updater" source check_repo get_updatable_apps elif [ "$1" == 'update-all' ];then #The manage script no longer handles updates. This mode is only for backwards-compatibility and uses the updater script warning "The manage script ONLY updates apps, and this mode has been replaced by the updater script. If you want to update Artian-Apps from the command-line, please use: ~/Artian-apps/updater cli-yes" #get functions from updater script source "${DIRECTORY}/updater" source check_repo updatable_apps="$(get_updatable_apps)" updatable_files='' update_now_cli else error "Invalid mode. ($1) Allowed values: 'install', 'multi-install', 'install-if-not-installed', 'uninstall', 'multi-uninstall', 'update', 'update-all', 'check-all', or 'daemon'." fi # exit script when finished. Prevents errors if script was modified while in use and new script is longer than previous script. exit "$?" }