Skip to content

Instantly share code, notes, and snippets.

@eldersixpack13
Last active May 2, 2024 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eldersixpack13/5a4de7e2b6043cf950856b330287eec8 to your computer and use it in GitHub Desktop.
Save eldersixpack13/5a4de7e2b6043cf950856b330287eec8 to your computer and use it in GitHub Desktop.
script to download LE nightlies for updates/downgrades
#!/bin/sh
# SCRIPT_VERSION="0.13";
# Build Date 02.05.2024
#
# author's mail: sixpack13@online.de
#
# script is under GPL
#
########################################################################
#
# Version HINT's
# ==============
# * Version 0.13 (02.05.2024)
# - script has switched to LE nightly 13 !
# - if you're comming from LE nighlty 12 you ONCE NEED to manually download a nighlty 13 into ~/.update and reboot.
# After that the script runs as usual.
# - adjust script version following nightly version: nightly 13 => script version 0.13
#
# * Version 0.11 (09.03.2023)
# - script has switched to LE nightly 12 !
# - if you're comming from LE nighlty 11 you ONCE NEED to manually download a nighlty 12 into ~/.update and reboot.
# After that the script runs as usual.
#
#########################################################################################################
#
# What the script does:
# ====================
# mainly: downloads LibreELEC nightlies for supported platforms [*] from the download server
# [*] (currently only) Generic, Generic-legacy, RPi2, RPi4
#
# - reads info from currently running nightly
# - checks the download server for available nightlies images for an update/downgrade
# - updates: to the newest (I hope this plays well when jumping over several nightlies in one step !)
# - downgrades: one nightly before currently running (stepwise one image down)
# - checks for de-compression needed disk space when the LE updater runs (549 MB) and if okay:
# - downloads a chosen nightly image to ~/.update/
# - checksum image (OFF for now) and the update installer does this anyway
# - offers to reboot
#
# TODO
# ====
# - further test: Generic-legacy, RPi4, RPi2, others ?
# - test with lower disk space
#
########################################################################
# VARs
########################################################################
SCRIPT_VERSION="0.13";
DEBUG=1; # writes debug output; NO image will be downloaded (simulate it only) !
DEBUG=0; # if un-commented: debug is OFF
LE_RELEASE="13.0"; # DON'T touch !!!
SERVER="https://test.libreelec.tv/json"; # DON'T touch !!!
SUPPORTED_HW="Generic|Generic-legacy|RPi"; # DON'T touch !!!
PARTITION="/storage";
UPDATE_DIR="/storage/.update";
ETC_OS_RELEASE="/etc/os-release";
WORK_DIR="/storage/";
MY_TMPDIR="$(mktemp -d -p ${WORK_DIR})";
AVAILABLE_IMAGES="${MY_TMPDIR}/available_images";
POTENTIAL_IMAGES="${MY_TMPDIR}/potenial_images";
########################################################################
#
# Functions
#
########################################################################
error_warning() {
printf %b "\n\n\t ${1} \n\n";
clean_up;
exit 13;
}
########################################################################
print_message() {
printf %b "\n ${1} \n";
return 0;
}
########################################################################
clean_up() {
[ ${DEBUG} -gt 0 ] && print_message "+++ in Function clean up +++";
[ -d ${UPDATE_DIR} ] && rm -f ${UPDATE_DIR}/*;
[ -d "${MY_TMPDIR}" ] && rm -rf "${MY_TMPDIR}";
printf %b "\n";
return 0;
}
########################################################################
get_HW_info(){
if [ -s ${ETC_OS_RELEASE} ]; then
PLATFORM="$(cat ${ETC_OS_RELEASE} | grep LIBREELEC_ARCH | sed 's/"/ /g' | cut -d " " -f2 | sed 's/ //g')";
RUNNING_VERSION="$(cat ${ETC_OS_RELEASE} | grep ^VERSION | grep nightly | awk -F '"' '{print $2}' | sed 's/nightly-//g' | sed 's/ //g')";
RUNNING_VERSION_DATE="$(echo "${RUNNING_VERSION}" | awk -F "-" '{print $1}' | sed 's/ //g')";
GIT_HASH_INSTALLED="$(echo "${RUNNING_VERSION}" | awk -F "-" '{print $2}' | sed 's/ //g')";
DEVICE="$(cat ${ETC_OS_RELEASE} | grep "LIBREELEC_DEVICE=" | awk -F '"' '{print $2}' | sed 's/ //g')";
if [ ${DEBUG} -gt 0 ]; then
print_message "+++ Debug Info +++";
print_message "\t your Platform is: ${PLATFORM}";
print_message "\t your Device: ${DEVICE}";
print_message "\t build date is: ${RUNNING_VERSION_DATE}";
print_message "\t Git Hash is: ${GIT_HASH_INSTALLED}";
print_message "\t Version is: ${RUNNING_VERSION}";
print_message "\n";
fi;
else
error_warning "can't find your ${ETC_OS_RELEASE} \n\n\t Aborting !!!";
fi;
}
########################################################################
check_HW_Support() {
# handle only SUPPORTED_HW (see section VARs), deny other variants
RC="$(echo "${PLATFORM}" | grep -iE ${SUPPORTED_HW})";
# -z STRING: the length of STRING is zero
[ -z "${RC}" ] && error_warning "this script only supports Generic* and RPi's. \n\n\t Aborting !!!";
}
########################################################################
create_ServerURL() {
# create the download URL for the running HW
PROJECT="$(cat ${ETC_OS_RELEASE} | grep LIBREELEC_PROJECT | sed 's/"/ /g' | cut -d " " -f2 | sed 's/ //g')";
DOWNLOAD_URL="${SERVER}/${LE_RELEASE}/${PROJECT}/${DEVICE}/"; # the last slash is important, otherwise NOTHING will be downloaded
if [ ${DEBUG} -gt 0 ]; then
print_message "+++ Debug Info +++";
print_message "\t using download URL:\t ${DOWNLOAD_URL}";
fi
}
########################################################################
check_available_Images() {
if [ ${DEBUG} -gt 0 ]; then
print_message "+++ Debug Info +++";
print_message "\t in function check_available_Images";
fi
# get what's on the download server available for the found HW
[ -e "${AVAILABLE_IMAGES}" ] && rm -f "${AVAILABLE_IMAGES}";
curl -s "${DOWNLOAD_URL}" -o "${AVAILABLE_IMAGES}";
RC=$?; # return code from curl
[ "${RC}" -gt "0" ] && error_warning "something went wrong during download from the Server: \n\n\t ${DOWNLOAD_URL} \n\n\t curl return code was: ${RC} \n\n\t Aborting !!!";
if [ ${DEBUG} -gt 0 ]; then
print_message "\t curl return code after downloading server list to ${AVAILABLE_IMAGES} was:\t ${RC} \n";
fi
# -s FILE: FILE exists and has a size greater than zero
if [ ! -s "${AVAILABLE_IMAGES}" ]; then
error_warning "Can't find anything on the download server: ${AVAILABLE_IMAGES} was empty ! \n\n\t Aborting !!!";
else
sed -i -e "/${PLATFORM}/!d" \
-e '/^$/d' \
-e '/.sha256/d' \
-e '/\[/d' \
-e '/\]/d' \
-e 's/{//g' \
-e 's/}//g' \
-e 's/,//g' \
-e 's/"name"://g' \
-e 's/"type":"file"//g' \
-e 's/"mtime"://g' \
-e 's/"size"://g' \
-e 's/^ //g' \
-e 's/ $//g' "${AVAILABLE_IMAGES}";
[ -e "${POTENTIAL_IMAGES}" ] && rm -rf "${POTENTIAL_IMAGES}";
while read -r LINE; do
# prepare for sortable dates the form YYMMDDHHMM
DAY="$(echo "${LINE}" | awk -F " " '{print $3}' | sed 's/ //g')";
MONTH_STRING="$(echo "${LINE}" | awk -F " " '{print $4}' | sed 's/ //g')";
YEAR="$(echo "${LINE}" | awk -F " " '{print $5}' | sed 's/ //g')";
TIME="$(echo "${LINE}" | awk -F " " '{print $6}' | cut -d: -f1,2 | sed 's/ //g' | sed 's/://g')";
case ${MONTH_STRING} in
Jan)
MONTH=01;
;;
Feb)
MONTH=02;
;;
Mar)
MONTH=03;
;;
Apr)
MONTH=04;
;;
May)
MONTH=05;
;;
Jun)
MONTH=06;
;;
Jul)
MONTH=07;
;;
Aug)
MONTH=08;
;;
Sep)
MONTH=09;
;;
Oct)
MONTH=10;
;;
Nov)
MONTH=11;
;;
Dec)
MONTH=12;
;;
esac
MY_DATE="${YEAR}${MONTH}${DAY}${TIME}";
echo "${MY_DATE} ${LINE}" >> "${POTENTIAL_IMAGES}";
done < "${AVAILABLE_IMAGES}";
###############################################################
#
# possible cases in ${POTENTIAL_IMAGES}
#
# case 1. nothing found cause ${POTENTIAL_IMAGES} is empty
# case 2. ${POTENTIAL_IMAGES} doesn't contain the currently running nightly => NO decision/comparsion possible
# Case 3. only one entry: => is it a update or a downgrade
# Case 4. more then 2 entries: => which one is the update or downgrade
#
#####################################################################################
###
### case 1.: ${POTENTIAL_IMAGES} is empty => nothing to download
###
# -s FILE: FILE exists and has a size greater than zero
[ ! -s "${POTENTIAL_IMAGES}" ] && error_warning "NO nightlies found today ! \n\n\t Please try later again";
# sort list downward to get the newest on top
sort -ur "${POTENTIAL_IMAGES}" -o "${POTENTIAL_IMAGES}";
#####################################################################################
###
### case 2.: if I can't find the currently running nightly in the list I got from the download server I can't make any decision
###
DATE_CURRENTLY_RUNNING="$(cat "${POTENTIAL_IMAGES}" | grep "${RUNNING_VERSION}" | head -1 | awk -F " " '{print $1}' | sed 's/ //g')";
# -z STRING: the length of STRING is zero
[ -z "${DATE_CURRENTLY_RUNNING}" ] && error_warning "I can't find your currently running nighty on the dowload server ! \n\n\t Aborting !!!";
#####################################################################################
###
### case 3.: only one entry in ${POTENTIAL_IMAGES}
###
COUNTER="$(cat "${POTENTIAL_IMAGES}" | wc -l)";
if [ "${COUNTER}" -eq 1 ]; then
DATE_SINGLE_ENTRY="$(cat "${POTENTIAL_IMAGES}" | head -1 | awk -F " " '{print $1}' | sed 's/ //g')";
if [ "${DATE_SINGLE_ENTRY}" -gt "${DATE_CURRENTLY_RUNNING}" ]; then
UPDATE_IMAGE="$(cat "${POTENTIAL_IMAGES}" | grep "${DATE_SINGLE_ENTRY}" | head -1 | awk -F " " '{print $2}' | sed -e 's/"//g' -e 's/ //g')";
UPDATE_VERSION="$(cat "${POTENTIAL_IMAGES}" | grep "${DATE_SINGLE_ENTRY}" | head -1 | awk -F " " '{print $2}' | sed -e 's/"//g' -e 's/.img.gz//g'| awk -F 'nightly-' '{print $2}')";
DOWNGRADE_IMAGE="";
else
UPDATE_IMAGE="";
DOWNGRADE_IMAGE="$(cat "${POTENTIAL_IMAGES}" | grep "${DATE_SINGLE_ENTRY}" | head -1 | awk -F " " '{print $2}' | sed -e 's/"//g' -e 's/ //g')";
DOWNGRADE_VERSION="$(cat "${POTENTIAL_IMAGES}" | grep "${DATE_SINGLE_ENTRY}" | head -1 | awk -F " " '{print $2}' | sed -e 's/"//g' -e 's/.img.gz//g'| awk -F 'nightly-' '{print $2}')";
fi
fi
#####################################################################################
###
### case 4.: more then 2 entries in ${POTENTIAL_IMAGES} => which one is the update or downgrade
###
# and cause of sorting with "-ur" the newest is at top
sort -ur "${POTENTIAL_IMAGES}" -o "${POTENTIAL_IMAGES}";
# remember top entry => possible update candidate
DATE_UPDATE_CANDIDATE="$(cat "${POTENTIAL_IMAGES}" | head -1 | awk -F " " '{print $1}' | sed 's/ //g')";
### corner case: NO update image in the list: the currently running is the same as the newest in the list
if [ "${DATE_UPDATE_CANDIDATE}" -gt "${DATE_CURRENTLY_RUNNING}" ]; then
UPDATE_IMAGE="$(cat "${POTENTIAL_IMAGES}" | grep "${DATE_UPDATE_CANDIDATE}" | head -1 | awk -F " " '{print $2}' | sed -e 's/"//g' -e 's/ //g')";
UPDATE_VERSION="$(cat "${POTENTIAL_IMAGES}" | grep "${DATE_UPDATE_CANDIDATE}" | head -1 | awk -F " " '{print $2}' | sed -e 's/"//g' -e 's/.img.gz//g'| awk -F 'nightly-' '{print $2}')";
else
UPDATE_IMAGE=""; # needed, otherwise UPDATE_* is undefined
UPDATE_VERSION=""; # needed, otherwise UPDATE_* is undefined
fi
# sort ascending to get the oldest at top and search the list until currently running => possible downgrade candidate
sort -ui "${POTENTIAL_IMAGES}" -o "${POTENTIAL_IMAGES}";
while read -r LINE; do
[ "${LINE}" = " " ] && break;
eval echo "${LINE}" | grep -q "${RUNNING_VERSION}" && break;
DATE_DOWNGRADE_CANDIDATE="$(echo "${LINE}" | awk -F " " '{print $1}' | sed 's/ //g')";
done < "${POTENTIAL_IMAGES}";
### corner case: NO downgrade image in the list: the currently running is the same as the oldest in the list
# -n STRING: the length of STRING is nonzero
if [ -n "${DATE_DOWNGRADE_CANDIDATE}" ]; then
if [ "${DATE_DOWNGRADE_CANDIDATE}" -lt "${DATE_CURRENTLY_RUNNING}" ]; then
DOWNGRADE_IMAGE="$(cat "${POTENTIAL_IMAGES}" | grep "${DATE_DOWNGRADE_CANDIDATE}" | head -1 | awk -F " " '{print $2}' | sed -e 's/"//g' -e 's/ //g')";
DOWNGRADE_VERSION="$(cat "${POTENTIAL_IMAGES}" | grep "${DATE_DOWNGRADE_CANDIDATE}" | head -1 | awk -F " " '{print $2}' | sed -e 's/"//g' -e 's/.img.gz//g'| awk -F 'nightly-' '{print $2}')";
fi
else
DOWNGRADE_IMAGE=""; # needed, otherwise DOWNGRADE_* is undefined
DOWNGRADE_VERSION=""; # needed, otherwise DOWNGRADE_* is undefined
fi
if [ ${DEBUG} -gt 0 ]; then
[ "${COUNTER}" ] && print_message "\t the list contains \t ${COUNTER} entries";
[ "${DATE_SINGLE_ENTRY}" ] && print_message "\t single Entry in list: \t ${DATE_SINGLE_ENTRY}";
[ "${DATE_UPDATE_CANDIDATE}" ] && print_message "\n\t Update candidate: \t ${DATE_UPDATE_CANDIDATE}";
[ "${UPDATE_VERSION}" ] && print_message "\t Update Version: \t ${UPDATE_VERSION}";
[ "${UPDATE_IMAGE}" ] && print_message "\t Update image: \t ${UPDATE_IMAGE}";
[ "${DATE_DOWNGRADE_CANDIDATE}" ] && print_message "\n\t downgrade candiate: \t ${DATE_DOWNGRADE_CANDIDATE}";
[ "${DOWNGRADE_VERSION}" ] && print_message "\t Downgrade_Version: \t ${DOWNGRADE_VERSION}";
[ "${DOWNGRADE_IMAGE}" ] && print_message "\t Downgrade image: \t ${DOWNGRADE_IMAGE}";
print_message "\n\n";
fi
fi
}
########################################################################
check_disk_space() {
if [ ${DEBUG} -gt 0 ]; then
print_message "+++ Debug Info +++";
print_message "\t in function check_disk_space";
fi
# all Images (Generic*, RPi*) decompresses to the same size: 549 MB
SPACE_NEEDED="562176"; # 549 MB
SPACE_AVAILABLE=$(df | grep ${PARTITION} | awk 'END {print $4}');
[ "${SPACE_AVAILABLE}" -lt "${SPACE_NEEDED}" ] && error_warning "Sorry, NOT enough disk space to proceed ! \n\n\t The update installer needs roughly $((SPACE_NEEDED/1024)) MB of free disk space !\n\n\t Aborting !!!";
if [ ${DEBUG} -gt 0 ]; then
print_message "+++ Debug Info +++";
print_message "\t needed disk space is: ${SPACE_NEEDED} MB ";
print_message "\t avaiable disk space is: ${SPACE_AVAILABLE} MB ";
fi
return 0;
}
########################################################################
download_and_install() {
if [ ${DEBUG} -gt 0 ]; then
print_message "+++ Debug Info +++";
print_message "\t in Function download_and_install";
fi
IMAGE_TO_INSTALL=$1;
[ ${DEBUG} -gt 0 ] && print_message "\t for download selected image is: \t ${IMAGE_TO_INSTALL}";
# images are downloaded to ~/.update
# -d FILE: FILE exists and is a directory
[ ! -d "${UPDATE_DIR}" ] && mkdir -p ${UPDATE_DIR};
# don't overread: cd ... is TRUE when cd is successfully executed and only THEN "rm -f ./* " takes action !
[ -d ${UPDATE_DIR} ] && cd ${UPDATE_DIR} && rm -f ./*;
if [ ${DEBUG} -eq 0 ]; then
print_message "\n\t downloading: \t ${IMAGE_TO_INSTALL} \n\n\t wait ...\n"; # and \n\t downloading: \t ${IMAGE_TO_INSTALL}.sha256 \n\n\t wait ...\n";
curl "${DOWNLOAD_URL}/${IMAGE_TO_INSTALL}" -o "${IMAGE_TO_INSTALL}"; # && curl -# "${DOWNLOAD_URL}/${IMAGE_TO_INSTALL}.sha256" -o ${IMAGE_TO_INSTALL}.sha256;
RC=$?; # see: man curl
[ "${RC}" -gt "0" ] && error_warning "something went wrong during image download ! \n curl return code is:\t ${RC} \n\n\t Aborting !!!";
########################################################################
#
# switch checksumming OFF for now, cause of damaged *.sha256 on the server
#
# print_message "\n\t checksumming the downloaded image, wait...\n";
# sha256sum -c ${IMAGE_TO_INSTALL}.sha256 > /dev/null;
# RC=$?;
# [ "${RC}" -gt "0" ] && error_warning "Error during checksumming. The return code is: \t ${RC} \n\n\t Aborting !!!";
########################################################################
# cause we downloaded to ~/.update remove the *.sha256 to not confuse the update installer during next boot
[ -e "${IMAGE_TO_INSTALL}.sha256" ] && rm -f "${IMAGE_TO_INSTALL}.sha256";
else
print_message "+++ Debug Info +++";
print_message "\t while in DEBUG mode nothing will be downloaded, but the command would be:";
print_message "\t curl ${DOWNLOAD_URL}/${IMAGE_TO_INSTALL} -o ${IMAGE_TO_INSTALL}"; # && curl -# "${DOWNLOAD_URL}/${IMAGE_TO_INSTALL}.sha256" -o "${IMAGE_TO_INSTALL}.sha256" ";
fi
# check for zero length/empty nightlies !
# -s FILE: FILE exists and has a size greater than zero
if [ -s "${IMAGE_TO_INSTALL}" ]; then
print_message "\t nightly image under: \t ${UPDATE_DIR} is ready ! \n\n\t reboot to install the nightly or maybe reboot via following command: \n\n\t sync && sync && systemctl reboot \n\n";
else
[ ${DEBUG} -eq 0 ] && error_warning "downloaded image seems broken or has zero length, please try later again And/Or check the website: \n\n\t ${DOWNLOAD_URL}";
fi
[ -d "${MY_TMPDIR}" ] && rm -rf "${MY_TMPDIR}";
}
########################################################################
#
# MAIN
#
########################################################################
trap "error_warning 'script was aborted !'" INT TERM # SIGINT SIGTERM
[ ${DEBUG} -eq 0 ] && clear;
get_HW_info;
check_HW_Support;
create_ServerURL;
check_available_Images;
print_message " *** script version: ${SCRIPT_VERSION}\n";
print_message "\t your Platform is: ${PLATFORM}";
print_message "\t running nightly from: ${RUNNING_VERSION_DATE}";
print_message "\t and has Git Hash: ${GIT_HASH_INSTALLED}";
print_message "\t Version: ${RUNNING_VERSION}";
print_message "\n *** the following images are currently available on the download server:";
if [ "${UPDATE_IMAGE}" ]; then
print_message "\t * Update (to the most recent Image): \t ${UPDATE_IMAGE} ";
else
print_message "\t *** No updates today *** ";
fi
if [ "${DOWNGRADE_IMAGE}" ]; then
print_message "\t * Downgrade (to the previous Image): \t ${DOWNGRADE_IMAGE} ";
else
print_message "\t *** No downgrade today *** ";
fi
while true; do
REPLY="";
print_message "\n *** What do you want me to do ? ";
print_message "\t u) \t update to the lastest nightly";
print_message "\t d) \t downgrade to the previous nightly";
print_message "\t q) \t quit (auto. selected in 20 seconds !!!)";
print_message;
read -r -n 1 -t 20 -p "Enter selection [u, d, q] > ";
case ${REPLY} in
u)
[ ${DEBUG} -gt 0 ] && print_message "\t update was selected";
# -n STRING: the length of STRING is nonzero
if [ -n "${UPDATE_IMAGE}" ]; then
check_disk_space && download_and_install "${UPDATE_IMAGE}";
else
error_warning "No Image for updates available \n\n\t Aborting !!!";
fi;
break;
;;
d)
[ ${DEBUG} -gt 0 ] && print_message "\t downgrade was selected";
# -n STRING: the length of STRING is nonzero
if [ -n "${DOWNGRADE_IMAGE}" ]; then
check_disk_space && download_and_install "${DOWNGRADE_IMAGE}";
else
error_warning "No Image for downgrades available \n\n\t Aborting !!!";
fi;
break;
;;
q | *)
[ ${DEBUG} -gt 0 ] && print_message "quit selected";
print_message "\n\n\t *** Sayonara *** \n\n";
clean_up && exit 0;
break;
;;
esac
done
exit 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment