From 50411f24a94691f2318f5010438bbc6aaf1e181d Mon Sep 17 00:00:00 2001 From: Aditya Alok Date: Mon, 25 Apr 2022 02:23:11 +0530 Subject: [PATCH] feat(auto update): update packages in there build order Currently, we assume that a package is not coupled with any specific version of it's dependencies. Therefore, we update them individually without any specific order. But this assumtion fails for package families like lxqt which requires all it's family members to be of specific version. Although we would have to manually update dependencies in such situation (if they can not be auto-updated), but we can atleast decide order of updation for packages that can be auto-updated. Signed-off-by: Aditya Alok --- scripts/bin/update-packages | 140 ++++++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 46 deletions(-) diff --git a/scripts/bin/update-packages b/scripts/bin/update-packages index 6f0e203d6..21d74fda9 100755 --- a/scripts/bin/update-packages +++ b/scripts/bin/update-packages @@ -19,7 +19,10 @@ export TERMUX_REPOLOGY_DATA_FILE TERMUX_REPOLOGY_DATA_FILE="$(mktemp -t termux-repology.XXXXXX)" # File to store repology data. export TERMUX_SCRIPTDIR -TERMUX_SCRIPTDIR="$(realpath "$(dirname "$0")/../..")" # Script directory. +TERMUX_SCRIPTDIR=$(realpath "$(dirname "$0")/../..") # Root of repository. + +export TERMUX_PACKAGES_DIRECTORIES +TERMUX_PACKAGES_DIRECTORIES=$(jq --raw-output 'keys | .[]' "${TERMUX_SCRIPTDIR}"/repo.json) # Define few more variables used by scripts. # shellcheck source=scripts/properties.sh @@ -76,15 +79,6 @@ _update() { TERMUX_PKG_NAME="$(basename "$1")" export TERMUX_PKG_BUILDER_DIR TERMUX_PKG_BUILDER_DIR="$(realpath "$1")" # Directory containing build.sh. - # Avoid: - # - ending on errors such as $(which prog), where prog is not installed. - # - error on unbound variable. - # - # Variables used by auto update script should be covered by above variables and properties.sh. - set +e +u - # shellcheck source=/dev/null - . "${pkg_dir}"/build.sh 2>/dev/null - set -e -u IFS="," read -r -a BLACKLISTED_ARCH <<<"${TERMUX_PKG_BLACKLISTED_ARCHES:-}" export TERMUX_ARCH="" # Arch to test updates. @@ -95,46 +89,24 @@ _update() { break fi done + # Set +e +u to avoid: + # - ending on errors such as $(which prog), where prog is not installed. + # - error on unbound variable. (All global variables used should be covered by properties.sh and above.) + set +e +u + # shellcheck source=/dev/null + . "${TERMUX_PKG_BUILDER_DIR}"/build.sh 2>/dev/null + set -e -u - # Only update if auto update is enabled. - if [[ "${TERMUX_PKG_AUTO_UPDATE}" == "true" ]]; then - echo # Newline. - echo "INFO: Updating ${TERMUX_PKG_NAME} [Current version: ${TERMUX_PKG_VERSION}]..." - termux_pkg_auto_update - fi -} - -# Check if an issue with same title already exists and is open. -_gh_check_issue_exists() { - local pkg_name="$1" - while read -r title; do - # Check for exact title match. - if [[ "${title}" == "Auto update failing for ${pkg_name}" ]]; then - return 0 - fi - done <<<"$( - gh issue list \ - --label "auto update failing" --label "bot" \ - --state open \ - --search "Auto update failing for ${pkg_name} in:title type:issue" \ - --json title | jq -r '.[] | .title' - )" - return 1 + echo # Newline. + echo "INFO: Updating ${TERMUX_PKG_NAME} [Current version: ${TERMUX_PKG_VERSION}]" + termux_pkg_auto_update } declare -A _FAILED_UPDATES=() +declare -a _ALREADY_SEEN=() # Array of packages successfully updated or skipped. _run_update() { local pkg_dir="$1" - # Check if this `pkg_dir` has a `build.sh` file. - if [[ ! -f "${pkg_dir}/build.sh" ]]; then - # Fail if detected a non-package directory. - termux_error_exit "ERROR: directory '${pkg_dir}' is not a package." - elif [[ "${GITHUB_ACTIONS:-}" == "true" ]] && _gh_check_issue_exists "$(basename "${pkg_dir}")"; then - # Skip if issue with same title already exists. - echo "INFO: Skipping '$(basename "${pkg_dir}")', an update issue for it hasn't been resolved yet." - return - fi # Run each package update in separate process since we include their environment variables. local output="" { @@ -147,14 +119,87 @@ _run_update() { # shellcheck disable=SC2181 if [[ $? -ne 0 ]]; then _FAILED_UPDATES["$(basename "${pkg_dir}")"]="${output}" + else + _ALREADY_SEEN+=("$(basename "${pkg_dir}")") fi } +declare -a _CACHED_ISSUE_TITLES=() +# Check if an issue with same title already exists and is open. +_gh_check_issue_exists() { + local pkg_name="$1" + if [[ -z "${_CACHED_ISSUE_TITLES[*]}" ]]; then + while read -r title; do + _CACHED_ISSUE_TITLES+=("'${title}'") # An extra quote ('') is added to avoid false positive matches. + done <<<"$( + gh issue list \ + --label "auto update failing" --label "bot" \ + --state open \ + --search "Auto update failing for in:title type:issue" \ + --json title | jq -r '.[] | .title' + )" + fi + # shellcheck disable=SC2076 # We want literal match here, not regex based. + if [[ "${_CACHED_ISSUE_TITLES[*]}" =~ "'Auto update failing for ${pkg_name}'" ]]; then + return 0 + fi + return 1 +} + +_should_update() { + local pkg_dir="$1" + + if [[ ! -f "${pkg_dir}/build.sh" ]]; then + # Fail if detected a non-package directory. + termux_error_exit "ERROR: directory '${pkg_dir}' is not a package." + fi + + # shellcheck disable=SC2076 + if ! grep -q '^TERMUX_PKG_AUTO_UPDATE=true$' "${pkg_dir}/build.sh" || + [[ " ${_ALREADY_SEEN[*]} ${!_FAILED_UPDATES[*]} " =~ " $(basename "${pkg_dir}") " ]]; then + return 1 # Skip. + fi + if [[ "${GITHUB_ACTIONS:-}" == "true" ]] && _gh_check_issue_exists "$(basename "${pkg_dir}")"; then + echo "INFO: Skipping '$(basename "${pkg_dir}")', an update issue for it hasn't been resolved yet." + return 1 + fi + + return 0 +} + +shopt -s extglob +_update_dependencies() { + local pkg_dir="$1" + + if ! grep -qE "^(TERMUX_PKG_DEPENDS|TERMUX_PKG_BUILD_DEPENDS|TERMUX_SUBPKG_DEPENDS)=" \ + "${pkg_dir}"/+(build|*.subpackage).sh; then + return 0 + fi + # shellcheck disable=SC2086 # Allow splitting of TERMUX_PACKAGES_DIRECTORIES. + while read -r dep dep_dir; do + if [[ -z $dep ]]; then + continue + elif [[ "${dep}" == "ERROR" ]]; then + termux_error_exit "ERROR: Obtaining update order failed for $(basename "${pkg_dir}")" + fi + _should_update "${dep_dir}" && _run_update "${dep_dir}" + done <<<"$("${TERMUX_SCRIPTDIR}"/scripts/buildorder.py "${pkg_dir}" $TERMUX_PACKAGES_DIRECTORIES || echo "ERROR")" +} + echo "INFO: Running update for: $*" if [[ "$1" == "@all" ]]; then for repo_dir in $(jq --raw-output 'keys | .[]' "${TERMUX_SCRIPTDIR}/repo.json"); do for pkg_dir in "${repo_dir}"/*; do + ! _should_update "${pkg_dir}" && continue # Skip if not needed. + # Update all its dependencies first. + _update_dependencies "${pkg_dir}" + # NOTE: I am not cheacking whether dependencies were updated successfully or not. + # There is no way I could know whether this package will build with current + # available verions of its dependencies or needs new ones. + # So, whatever the case may be. We just need packages to be updated in order + # and not care about anything else in between. If something fails to update, + # it will be reported by failure handling code, so no worries. _run_update "${pkg_dir}" done done @@ -168,11 +213,14 @@ else fi done fi - _run_update "${pkg}" # Here, `pkg` is a directory. + # Here `pkg` is a directory. + ! _should_update "${pkg}" && continue + _update_dependencies "${pkg}" + _run_update "${pkg}" done fi -################################################Failure handling################################################# +################################################FAILURE HANDLING################################################# _gh_create_new_issue() { local pkg_name="$1" @@ -197,7 +245,7 @@ _gh_create_new_issue() { Here's the output of the update script:
Show log -
${_FAILED_UPDATES["${pkg_name}"]}
+
${_FAILED_UPDATES["${pkg_name}"]}