#!/usr/bin/env bash set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" # shellcheck source=scripts/apk/package-lib.sh source "${repo_root}/scripts/apk/package-lib.sh" mode="${APK_UPDATE_CHECK_MODE:-warn}" if [[ "${mode}" != "warn" && "${mode}" != "fail" ]]; then printf 'APK_UPDATE_CHECK_MODE must be warn or fail\n' >&2 exit 2 fi github_latest_release() { local repo="$1" local response response="$(curl -fsSL \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2022-11-28' \ "https://api.github.com/repos/${repo}/releases/latest" 2>/dev/null)" || return 1 printf '%s\n' "${response}" | jq -er '.tag_name' } github_release_rows() { local repo="$1" local current_tag="$2" local page=1 local response local release_count while :; do response="$(curl -fsSL \ -H 'Accept: application/vnd.github+json' \ -H 'X-GitHub-Api-Version: 2022-11-28' \ "https://api.github.com/repos/${repo}/releases?per_page=100&page=${page}" 2>/dev/null)" || return 1 release_count="$(printf '%s\n' "${response}" | jq -er 'if type == "array" then length else error("expected release array") end')" || return 1 [[ "${release_count}" -gt 0 ]] || return 0 printf '%s\n' "${response}" | jq -r ' .[] | select(.draft | not) | select(.prerelease | not) | [ .tag_name, (.published_at // "-"), (.name // .tag_name), .html_url, ( (.body // "") | gsub("[\\r\\n\\t]+"; " ") | gsub("[`*_>#]"; "") | gsub(" +"; " ") | gsub("^ +"; "") | gsub(" +$"; "") | if length > 240 then .[0:237] + "..." else . end ) ] | @tsv ' if printf '%s\n' "${response}" | jq -e --arg tag "${current_tag}" ' any(.[]; .tag_name == $tag and (.draft | not) and (.prerelease | not)) ' >/dev/null; then return 0 fi page=$((page + 1)) done } apkbuild_value() { local package_dir="$1" local key="$2" awk -F= -v key="${key}" ' $1 == key { value = $2 gsub(/["'\'']/, "", value) gsub(/^[ \t]+|[ \t]+$/, "", value) print value exit } ' "${package_dir}/APKBUILD" } load_update_metadata() { local package_dir="$1" local metadata="${package_dir}/update-check.conf" provider="" repo="" version_prefix="v" if [[ ! -f "${metadata}" ]]; then return 1 fi # shellcheck source=/dev/null source "${metadata}" if [[ "${provider}" != "github" || -z "${repo}" ]]; then return 1 fi } normalize_version() { local version="$1" local prefix="$2" case "${prefix}" in none|"") printf '%s\n' "${version}" ;; *) printf '%s\n' "${version#"${prefix}"}" ;; esac } version_is_newer() { local latest="$1" local current="$2" local apk_result local sorted if [[ "${latest}" == "${current}" ]]; then return 1 fi if command -v apk >/dev/null 2>&1; then apk_result="$(apk version -t "${latest}-r0" "${current}-r0" 2>/dev/null || true)" [[ "${apk_result}" == ">" ]] return fi if sort -V /dev/null 2>&1; then sorted="$(printf '%s\n%s\n' "${current}" "${latest}" | sort -V | tail -n 1)" [[ "${sorted}" == "${latest}" && "${latest}" != "${current}" ]] return fi return 0 } printf '%-14s %-14s %-14s %-18s %s\n' "PACKAGE" "CURRENT" "LATEST" "STATUS" "UPSTREAM" has_update=0 has_unknown=0 release_details="" while IFS= read -r package_dir; do apk_validate_package_dir "${package_dir}" package_name="$(apk_package_name "${package_dir}")" current_version="$(apkbuild_value "${package_dir}" "pkgver")" status="unknown" latest_version="-" upstream_url="-" if load_update_metadata "${package_dir}"; then upstream_url="https://github.com/${repo}/releases" if latest_tag="$(github_latest_release "${repo}")" && [[ -n "${latest_tag}" ]]; then latest_version="$(normalize_version "${latest_tag}" "${version_prefix}")" if version_is_newer "${latest_version}" "${current_version}"; then status="update-available" has_update=1 package_details="${package_name}: ${current_version} -> ${latest_version}" case "${version_prefix}" in none|"") current_tag="${current_version}" ;; *) current_tag="${version_prefix}${current_version}" ;; esac if release_rows="$(github_release_rows "${repo}" "${current_tag}")" && [[ -n "${release_rows}" ]]; then current_tag_found=0 while IFS=$'\t' read -r release_tag published_at release_name release_url release_excerpt; do [[ -n "${release_tag}" ]] || continue release_version="$(normalize_version "${release_tag}" "${version_prefix}")" if [[ "${release_tag}" == "${current_tag}" ]]; then current_tag_found=1 fi if version_is_newer "${release_version}" "${current_version}"; then package_details+=$'\n' package_details+=" ${release_version} (${published_at}) — ${release_name}" package_details+=$'\n' package_details+=" ${release_url}" if [[ -n "${release_excerpt}" ]]; then package_details+=$'\n' package_details+=" ${release_excerpt}" fi fi done <<< "${release_rows}" if [[ "${current_tag_found}" == 0 ]]; then package_details+=$'\n' package_details+=" Note: current release tag ${current_tag} was not found; listed releases compare newer than ${current_version}." fi else package_details+=$'\n' package_details+=" Note: release details could not be fetched." fi release_details+=$'\n' release_details+="${package_details}" else status="current" fi else has_unknown=1 fi else has_unknown=1 fi printf '%-14s %-14s %-14s %-18s %s\n' \ "${package_name}" \ "${current_version:-"-"}" \ "${latest_version}" \ "${status}" \ "${upstream_url}" done < <(apk_package_dirs "${repo_root}") if [[ -n "${release_details}" ]]; then printf '\nRelease details:%s\n' "${release_details}" fi if [[ "${mode}" == "fail" && ( "${has_update}" -ne 0 || "${has_unknown}" -ne 0 ) ]]; then exit 1 fi