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}"]}