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 <dev.aditya.alok@gmail.com>
This commit is contained in:
Aditya Alok 2022-04-25 02:23:11 +05:30
parent 232e5b1321
commit 50411f24a9
No known key found for this signature in database
GPG Key ID: 345AE134142077D8
1 changed files with 94 additions and 46 deletions

View File

@ -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:
<details>
<summary>Show log</summary>
<pre>${_FAILED_UPDATES["${pkg_name}"]}</pre>
<pre lang="bash">${_FAILED_UPDATES["${pkg_name}"]}</pre>
</details>
<hr>