diff --git a/source.list b/source.list new file mode 100644 index 0000000..e5ae3a7 --- /dev/null +++ b/source.list @@ -0,0 +1,3 @@ +[host] https://packages.microsoft.com +[dists=amd64] https://packages.microsoft.com/repos/edge/dists stable main +[pool] https://packages.microsoft.com/repos/edge/ diff --git a/updater b/updater new file mode 100755 index 0000000..50533f7 --- /dev/null +++ b/updater @@ -0,0 +1,345 @@ +#!/bin/bash +########################################################## +# updater - Automatically update Microsoft Edge on Solus # +# # +# Dependencies: # +# wget # +# gunzip # +# tar # +# sort (coreutils >= 8.32) # +# # +# # +# Disclaimer: This version only works for MS Edge Beta. # +# A later release may allow for update of Dev builds # +# and potentially both. # +# # +# Author: Ethan Smith-Coss # +# Version: 0.1.2 # +# Created: 2021-05-20T16:47+0100 # +# Last Modified: 2021-05-23T21:18+0100 # +########################################################## +## program name +prog_name="Microsoft Edge Updater" +## version +version="0.1.2" +## usage statement +usage="Usage: $(basename $0) [OPTION] +$prog_name ($version) - An updater program which can install the latest available version of Microsoft Edge to non-Debian-based Linux distros. + +DISCLAIMER: Currently this program can only update the Beta release of Microsoft Edge for amd64 architectures. + +Options: + -y, --yes-all Assume yes to all yes/no decisions. + -v, --version Print out the version of the script and exit. + --help Print this help message and exit +" + +runtime_dir=$(realpath "${BASH_SOURCE[0]}" | xargs -r dirname) +log_file="$runtime_dir/.log.1" +logs_dir="$runtime_dir/logs" +tmp_path="/tmp/microsoft-edge-autoupdater" +garbage="$tmp_path/garbage.tmp" +SECONDS=0 + +[[ ! -d "$logs_dir" ]] && mkdir "$logs_dir" +[[ ! -d "$tmp_path" ]] && mkdir "$tmp_path" + + +function clean_up { + log "DEBUG" "EXIT signal was raised, cleaning up system after session before exiting..." "$log_file" + printf "Cleaning up system after session..." + cp "$log_file" "$logs_dir/updater.log" && rm "$log_file" + [[ -d "$tmp_path/opt" ]] && rm -r "$tmp_path/opt" + [[ -d "$tmp_path/usr" ]] && rm -r "$tmp_path/usr" + + log "CLEANER" "Finished cleaning up system after session. Nice and clean :D Goodbye." "$runtime_dir/logs/updater.log" + echo "done. Goodbye." + + elapsed_time=$SECONDS + script_time="$(($elapsed_time / 60))m $(($elapsed_time % 60))s" + log "END" "Total time for script execution was: $script_time" "$runtime_dir/logs/updater.log" + + echo >> "$runtime_dir/logs/updater.log" + cat "$logs_dir/updater.log" >> "$logs_dir/updater.history" +} +## iterate over all user inputs +while test $# -gt 0 ; do + case $1 in + -y | --yes-all) + yes_flag=0 ; shift + ;; + --help) + echo "$usage" + exit 0 + ;; + -v | --version) + echo "$prog_name ($version)" + exit 0 + ;; + *) + shift + ;; + esac +done + +source "$runtime_dir/utils/common" +log "PREINIT" "----[New instance of script has been started: $(date -Iseconds)]----" "$log_file" + +## setup the yes flag to automatically accept all yes/no inputs +yes_flag="${yes_flag:-1}" +log "DEBUG" "The yes flag has been set to: $yes_flag." "$log_file" + +# set trap to trigger clean up function on any exit +trap clean_up EXIT + +## check if Microsoft Edge (Beta) is already installed +exit_code=$(command -v microsoft-edge-beta &>/dev/null) +[[ ! $exit_code -eq 0 ]] && { echo "Microsoft Edge (Beta) is not installed. Exiting updater..." ; +log "DEBUG" "Microsoft Edge (Beta) not recognised as an installed program. Exiting...(1)" ; exit 1 ; } +log "DEBUG" "Microsoft Edge (Beta) is installed to system. Proceeding with update..." "$log_file" + +## ensure source.list exists +source_list="$runtime_dir/source.list" +log "DEBUG" "Checking for a source.list in current runtime directory..." "$log_file" +printf "Checking for source.list..." +[[ ! -f "$source_list" ]] && { echo -e "\nUpdater encountered an issue: source.list not found. Exiting..." ; +log "ERROR" "Cannot find source.list in runtime directory ($source_list). Exiting...(2)" "$log_file" ; exit 2 ; } + +log "DEBUG" "Found a souce.list in $runtime_dir." "$log_file" +echo "found." + +## ensure host is reachable +host=$(grep "\[host\]" "$source_list" | cut -d' ' -f 2) +log "DEBUG" "Checking if host is available according to defined value of [host]: $host..." "$log_file" +echo "Checking to see if the host is known and reachable..." +if ! wget --spider "$host" >/dev/null 2>&1 ; then + echo "Cannot reach end-point for distribution information. Check your Internet connection and try again. Exiting..." + log "DEBUG" "Upstream end-point is cannot be contacted. Potential Internet connection issue or end-point address ($dist_upstream). Exiting...(3)" "$log_file" + exit 3 +fi +log "DEBUG" "Host is known and reachable. Continuing update to fetch data..." "$log_file" +echo "Host known and reachable: (host) $host" + +## identify the end-point URL for distribution information +dist_upstream=$(grep "\[dists.*\]" "$source_list" | cut -d' ' -f 2-4 --output-delimiter '/') +archi=$(grep -o "\[dists.*\]" "$source_list" | sed -E 's/\[dists=(.*)\]/\1/') +log "DEBUG" "Fetching Packages file from $dist_upstream for $archi architectures..." "$log_file" +## fetch the file - :@Ethan: there's no reason to inform the user of this operation unless it fails +exit_code=$(wget -a "$log_file" -O "$tmp_path/Packages.gz" "$dist_upstream/binary-$archi/Packages.gz") +[[ ! $exit_code -eq 0 ]] && { echo "There was an issue retrieving the package information for the update. Please check $logs_dir/updater.log, for more information. Exiting..." ; + log "DEBUG" "Encountered an issue with wget. Exiting...(4)" "$log_file" ; exit 4 ; } +log "DEBUG" "Successfully downloaded release package information." "$log_file" + +log "DEBUG" "Fetching Contents-$archi.gz from ${dist_upstream::-4}" "$log_file" +exit_code=$(wget -a "$log_file" -O "$tmp_path/Contents-$archi.gz" "${dist_upstream::-4}/Contents-$archi.gz") +[[ ! $exit_code -eq 0 ]] && { echo "There was an issue retrieving the package contents for the update. Please check $log_file/updater.log, so more information. Exiting..." ; + log "ERROR" "Encountered an issue with wget. Exiting...(4)" "$log_file" ; exit 4 ; } +log "DEBUG" "Successfully downloaded release contents information." "$log_file" + +### :@TODO: continue with TODO list. Finish off above code section to ensure wget is successful and logging appropriately. +log "DEBUG" "Uncompressing downloaded gz file using gunzip, $tmp_path/Packages.gz..." "$log_file" +## force gunzip to overwrite decompressed file if it already exists +gunzip -f "$tmp_path/Packages.gz" "$tmp_path/Contents-$archi.gz" >/dev/null 2>&1 +log "DEBUG" "Successfully uncompressed content." "$log_file" + +## get the most recent package entry from Packages list for Beta version of browser +log "DEBUG" "Identifying the most recent package information from Packages catelogue..." "$log_file" +## find the earliest entry for Edge Beta release +start_point=$(grep -n 'Package:.*-beta' "$tmp_path/Packages" | tr '\n' ' ' | cut -d: -f1) +## make a list of all lines which are blank - used to identify end of package information entry +end_points=( $(grep -n '^\s*$' "$tmp_path/Packages" | sed 's/://g') ) +## loop over all potential end points +for (( i=0 ; i<"${#end_points[@]}"; i++)) ; do + ## if the end point is greater than start point, set $end_point to the index of i + ### :@NOTE: this will more often then not be the first value in the array. However, + ### if a dev release comes out before the next beta version, the package entry may be + ### much later in the file. We break once the correct end point is found. + [[ "${end_points[$i]}" -gt $start_point ]] && end_point="${end_points[$i]}" && break +done +## adjust the start and end point values +((start_point--)) ; ((end_point++)) +## this ensures end_point is set. :@Ethan: I could just test if the variable exists but I like the clarity of this for now +end_point="${end_point:--1}" +## if for some reason $end_point is set to -1, we have an issue with the Packages file and should exit +[[ $end_point -eq -1 ]] && { log "ERROR" "EOF reached and indexing out of bounds value has been set. Exiting...(5)" "$log_file" ; exit 255 ; } +## extrapolate the package information and save to Release +package_entry=$(awk -v start=$start_point -v end=$end_point 'NR>1*start&&NR<1*end' "$tmp_path/Packages" > "$tmp_path/Release") +log "DEBUG" "Extrapolated most recent package information entry from $tmp_path/Release. Package information selected is as below:" "$log_file" +## pretty print each line when appending to make it easier to read in log file +sed 's/^/ (Release Info) /' "$tmp_path/Release" | sed -E 's/(^\s*\(Release Info\)\s*$)/\1(END)/' >> "$log_file" + +### :@Ethan: at this point we can start ensuring that a newer release is available, inform the user of the new size, +### check dependences, and just ensure the user wants to go ahead with the update. + +## get the current version number and the version in Release +current_version=$(microsoft-edge-beta --version | cut -d' ' -f3) +release_version=$(grep 'Version' "$tmp_path/Release" | sed -E 's/Version:\s*(.*)-.*/\1/') +log "DEBUG" "Checking if there is an update available (release: $release_version) or if most recent version is already installed to the system..." "$log_file" +## compare the versions - using sort -V. +if [[ "$release_version" == "$current_version" ]]; then + upgradeable=1 +else + [[ "$current_version" != "$(echo -e "$current_version\n$release_version" | sort -V | head -n2)" ]] && upgradeable=0 +fi +## this should never be substituted to 1 - but it's a cover if the release version happens to be behind the current version +##"${upgradeable:=1}" +upgradeable=${upgradeable:-1} +## check if we can upgrade, can exit 1 for passive exit - exit not notifying a failure for another reason +if [[ $upgradeable -eq 1 ]] ; then + elapsed_time=$SECONDS + script_time="$(($elapsed_time / 60))m $(($elapsed_time % 60))s" + echo "Most recent version of Microsoft Edge (Beta) [v$current_version] is already installed on this system, no need to update. Finished in $script_time" + log "DEBUG" "Most recent version of Microsoft Edge (Beta) is already installed to the system (v$current_version). Completed in $script_time. Exiting...(1)" "$log_file" + + exit 1 +fi +log "DEBUG" "There is a more recent release of Microsoft Edge (Beta) available for download (v$release_version). Continuing with update." +echo "Identified a new release of Microsoft Edge (Beta) [Current: v$current_version. New: v$release_version]. Starting the download and installation process..." + +## check if msedge process is already running +is_running=$(ps -aux | grep -oc 'msedge') +if [[ $is_running -gt 1 ]] ; then + log "DEBUG" "Identified running processes for msedge. Determining how script should handle process..." "$log_file" + if [[ ! $yes_flag -eq 0 ]] ; then + echo -n "Microsoft Edge (Beta) is already open. To continue update, the browser must be closed. Are you sure you wish to proceed? [(Y)es/No]: " && read -n 1 choice ; echo + [[ $(echo $choice | awk '{print tolower($0)}') == "n" ]] && { log "DEBUG" "User opted against allowing the script to close Microsoft Edge (Beta) by default. Exiting...(1)" "$log_file" ; exit 1 ; } + fi + log "DEBUG" "Confirmation has been given to proceed with killing Microsoft Edge (Beta) processes. Killing msedge..." "$log_file" + exit_code=$(killall msedge) + [[ ! $exit_code -eq 0 ]] && { log "ERROR" "Process is refusing to exit. Sending SIGKILL signal." "$log_file" ; + pkill -9 msedge ; } +fi +log "DEBUG" "Microsoft Edge (Beta) has been closed, either by yes flag enabled or by user decision. Process may not have been running thus never killed." "$log_file" + + +# Download the newest version of Microsoft Edge (Beta) +## get the end-point for .deb file location and filename +pool=$(grep '\[pool\]' "$source_list" | cut -d' ' -f2) +filename=$(grep 'Filename:.*' "$tmp_path/Release" | cut -d' ' -f2) +url="$pool$filename" ## set URL to be concatenation of pool + filename +## check if the file end-point is actually reachable before trying to download +log "DEBUG" "Checking the following release version file end-point is accessible ($url)..." "$log_file" + +### :@Ethan: This is trying to download the file for a response so effectively we will be doing the same work twice +### so is inefficient for testing if the end-point is reachable. ping and telnet are not suitable for this operation. +### An assumption will have to be made for now. + +### :@Ethan: The above message has been addressed and wget commands for checking end-points has been changed from +### `wget -qO- [URL] >/dev/null 2>&1` to: `wget --spider [URL] >/dev/null 2>&1`. Check manpage for more about the +### spider argument. + +if ! wget --spider "$url" >/dev/null 2>&1 ; then + echo "Cannot reach end-point for latest release. Check your Internet connection and try again." + log "DEBUG" "Pool end-point is cannot be contacted. Potential Internet connection issue or end-point address ($url). Exiting...(5)" "$log_file" + exit 5 +fi +log "DEBUG" "Pool is known and reachable. Beginning download..." "$log_file" +echo "Host known and reachable: (download) $url" + +## set filename to not be pool address but the name of file +filename=$(echo "$url" | rev | cut -d'/' -f1 | rev) +filesize=$(grep '^Size:.*' "$tmp_path/Release" | cut -d' ' -f2) +log "DEBUG" "Checking if user wishes to proceed with the update installation..." "$log_file" +## inform the user of the new download size before downloading +if [[ $yes_flag -eq 1 ]] ; then + echo -n "The following package will be installed, $filename [$filesize]. Are you sure you wish to continue? [(Y)es/No]: " && read -n 1 choice + [[ "$(echo $choice | awk '{print tolower($0)}')" == "n" ]] && { echo -e "\nThe following update to Microsoft Edge (Beta) v$release_version will not be installed. Exiting updater..." ; + log "DEBUG" "User opted out for updating from $current_version to $release_version. Exiting...(1)" "$log_file" ; exit 1 ; } + echo +fi +log "DEBUG" "Confirmation has been given to proceed with the following update of Microsoft Edge (Beta) [v$release_version]." "$log_file" + +# download the debian file +log "DEBUG" "Checking if the file is already downloaded to the system..." "$log_file" +if [[ ! -f "$tmp_path/$filename" ]] ; then + log "DEBUG" "Downloading the latest release version [v$release_version], file: $filename (URL: $url)" "$log_file" + echo "Downloading the following release file: $filename. This may take a moment..." + wget_timed=$(\time --format "%x:%e" wget -a "$tmp_path/wget_dump.log" -O "$tmp_path/$filename" "$url" 2>&1) + exit_code=$(echo $wget_timed | cut -d: -f1) + [[ ! $exit_code -eq 0 ]] && { echo "There was an issue downloading the Debian version of Microsoft Edge (Beta)." \ + "Please check $logs_dir/updater.log, for more information. Exiting update..." ; + log "DEBUG" "Encountered an issue with wget. Exiting...(6)" "$log_file" ; exit 6 ; } + awk -v end=$(grep -n '^\s*$' "$tmp_path/wget_dump.log" | tr '\n' ' ' | cut -d: -f1) 'NR>1*0&&NR<1*end+1' "$tmp_path/wget_dump.log" >> "$log_file" + log "DEBUG" "Download completed successfully in $(echo $wget_timed | cut -d' ' -f2)s, wget log below." "$log_file" + printf "Download complete in $(echo $wget_timed | cut -d' ' -f2)s. Validating checksums..." +else + log "DEBUG" "File already downloaded to system. Skipped download and verifying checksum..." "$log_file" + printf "Latest Debian release file is downloaded to the system, skipping download. Validating checksums..." +fi + +## verify the downloaded file +if [[ "$(sha256sum "$tmp_path/$filename" | cut -d' ' -f1)" != "$(grep 'SHA256:.*' "$tmp_path/Release" | cut -d' ' -f2)" ]] ; then + ## automatically remove the file - :@Ethan: it's either corrupt or hazardous to the health of the system + rm "$tmp_path/$filename" + log "DEBUG" "Checksum (SHA256) failed and integrity of file lost. File has been removed as either corrupt or hazardous/dangerous. Exiting...(7)" "$log_file" + echo -e "failed.\nThe checksum (SHA256) failed for some reason and removed either because it was corrupt or dangerous. Exiting updater...\n" + exit 7 +fi +echo "complete." + + +# unarchive the .deb file and extract data.tar.gz +log "DEBUG" "Unarchiving the downloaded DEB file ($tmp_path/$filename)..." "$log_file" +printf "Unzipping download..." +## use ar to unarchive .deb file +exit_code=$(ar vx "$tmp_path/$filename" --output "$tmp_path" >/dev/null 2>&1) +[[ ! $exit_code -eq 0 ]] && { echo -e "incomplete.\nThere was an issue unarchiving $filename. Exiting updater...\n" ; + log "ERROR" "There was an issue when unarchiving $filename. Exiting...(8)" "$log_file" ; exit 8 ; } +log "DEBUG" "Successfully unarchived $filename, decompressing the data.tar.gz file..." "$log_file" +## extract data from data.tar.gz +exit_code=$(tar --overwrite -xvf "$tmp_path/data.tar.xz" --directory "$tmp_path" >>"$log_file" 2>&1) +[[ ! $exit_code -eq 0 ]] && { echo -e "incomplete.\nThere was an extracting data.tar.gz. Exiting updater...\n" ; + log "ERROR" "There was an issue when decompressing data.tar.gz using tar -xvf. Exiting...(8)" "$tmp_path" ; exit 8 ; } +log "DEBUG" "Successfully extracted files from data.tar.gz." "$log_file" +echo "completed." +## etc/ directory only contains a cron daily directory, remove it +rm -r "$tmp_path/etc/" + +# compress archive the currently installed version of Edge (Beta) for restoration on failure +## :@TODO: compress everything on the system for a backup. Remove it if installation of new version was successful +log "DEBUG" "Creating an archive gzip tarball of system /opt/ and /usr/ directories..." "$log_file" +printf "Installing new version to system. This may take a few minutes..." +exit_code=$(archive_system "microsoft/msedge-beta") +if [[ ! $exit_code -eq 0 ]] ; then + ### :@Ethan: there was an issue with creating an archive, should probably inform the user to make a decision (unless -y is set) + echo -e "failed.\nThere was an issue creating a backup of the system. Exiting..." + log "ERROR" "There was an issue creating a gzip tarball of the system. Exiting...($exit_code)" "$log_file" + exit $exit_code +fi +log "DEBUG" "Successfully created an archive gzip tarball of the system. Safe to continue with installation." "$log_file" + +### :@NOTE: used to terminate the program at a certain point for incremental testing of functionality recently added up to the point of this exit. +#exit 0 + +# install the new version to system +log "DEBUG" "Copying files to there appropriate location..." "$log_file" +exit_code=$(cp -r "$tmp_path/opt" "/" >>"$log_file" 2>&1) +[[ ! $exit_code -eq 0 ]] && { echo -e "failed.\nThere was an issue installing files to the system. Reverting to previous version..." ; + log "DEBUG" "There was an issue copying $tmp_path/opt/ file to system /opt/. Reverting system files and exiting...(9)" "$log_file" ; restore_files && exit 9 ; } +exit_code=$(cp -r "$tmp_path/usr" "/" >>"$log_file" 2>&1) +[[ ! $exit_code -eq 0 ]] && { echo -e "failed.\nThere was an issue installing files to the system. Reverting to previous version..." ; + log "DEBUG" "There was an issue copying $tmp_path/usr/ file to system /usr/. Reverting system files and exiting...(9)" "$log_file" ; restore_files && exit 9 ; } +log "DEBUG" "Successfully installed files to their appropriate location." "$log_file" +echo "installation complete." + +## confirm the update was successful - try to restore system otherwise +## :@TODO: validate new version has successfully installed properly - exit 12 +if [[ "$(microsoft-edge-beta --version | cut -d' ' -f3)" != "$release_version" ]] ; then + log "ERROR" "There was an issue with the update and installation. Restoring to previous version..." "$log_file" + printf "Update was unsuccessful. Restoring to previous version..." + + ## attempt restoration of system + return_code=$(restore_files "microsoft/msedge-beta") + [[ $return_code -eq 12 ]] && { echo -e "failed.\nThere was an error trying to restore system files." \ + "Either there no back was made or another issue occurred. Consult $log_file for more information." ; exit $return_code ; } + log "DEBUG" "System restoration was successful, safe to use; however, the update was still failed. Exiting...(11)" "$log_file" + echo -e "completed. However, there was still an issue trying to update Microsoft Edge (Beta) to the latest version." \ + "Consult $log_file for more information. Exiting..." + + exit 11 +fi + +elapsed_time=$SECONDS +script_time="$(($elapsed_time / 60))m $(($elapsed_time % 60))s" +log "DEBUG" "Installation and update was successful (completed in: $script_time) and system can be cleaned up to remove any waste." "$log_file" +echo "Microsoft Edge (Beta) has been successfully updated to the latest version: v$release_version, in $script_time." diff --git a/utils/common b/utils/common new file mode 100755 index 0000000..1d13df5 --- /dev/null +++ b/utils/common @@ -0,0 +1,53 @@ +#!/bin/bash +########################################################### +# common - Utility file with common functions for updater # +# # +# Dependencies: # +# tar # +# # +# # +# Author: Ethan Smith-Coss # +# Version: 0.1.1 # +# Created: 2021-05-20T16:47+0100 # +# Last Modified: 2021-05-22T23:23+0100 # +########################################################### + +function log { + local log_out=$(realpath "${0%/*}/../.log.1") + [[ $# -eq 0 ]] && log "ERROR" "Log function was called without any arguments. Returning 1." "$log_out" && return 1 + [[ ! $# -eq 3 ]] && log "ERROR" "Log function expected at least 3 arguments, recieved $#. Returning 1." "$log_out" && return 1 + $(echo "$(date -Iseconds) ($1): $2" >> "$3") >/dev/null 2>&1 + [[ ! $? -eq 0 ]] && log "ERROR" "Log function encountered an issue writing to \"$3\". Returning 1." "$log_out" && return 1 + + return 0 +} + +function archive_system { + local log_out=$(realpath "${0%/*}/../.log.1") + [[ -e $1 ]] && log "ERROR" "Archive function was called without any arguments." "$log_out" + + local archive_output="$/tmp/microsoft-edge-autoupdater/$(echo $1 | sed 's/\//_/').tar.gz" + [[ -f "$archive_output" ]] && return 0 + + exit_code=$(grep 'usr/.*' "/tmp/microsoft-edge-autoupdater/Contents-amd64" | tar --overwrite -cvzf "$archive_output" "/opt/$1/" >>"$log_out" 2>&1 ) + [[ ! $exit_code -eq 0 ]] && { log "ERROR" "(Archive) There was an issue creating the archive file of system. Returning...(10)" "$log_out" ; return 10 ; } + + return 0 +} + +function restore_files { + local log_out=$(realpath "${0%/*}/../.log.1") + [[ -e $1 ]] && log "ERROR" "Restore function was called without any arguments." "$log_out" + local backup_file="/tmp/microsoft-edge-autoupdater/$(echo $1 | 's/\/_/').tar.gz" + [[ -f "$backup_file" ]] && { log "ERROR" "There is no backup of Microsoft Edge (Beta) identified. Restoration not possible." "$log_out" ; return 12 ; } + exit_code=$( tar -xvzf "$backup_file" "/" >>"$log_out" 2>&1) + [[ ! $exit_code -eq 0 ]] && { log "ERROR" "There was an issue restoring pervious files to system." \ + "The backup created will not be removed to allow for manual restoration. Returning...(12)" ; return 12 ; } + + return 0 +} + +function notify { + ## :@TODO: implement notification system. + return 0 +}