#!/bin/bash # meld-pkgbuilds - search, download, cache and compare PKGBUILDs # across Parabola abslibre and various upstreams # # Copyright (C) 2023 bill-auger # # SPDX-License-Identifier: AGPL-3.0-or-later # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # # # NOTE: you should create a /packages directory and install 'pacman-all.conf' to /etc # # NOTE: FSDG warning: this program is capable of locating and downloading build recipes # which are not be FSDG-fit. It is mainly useful for merging upstream changes into # PKGBUILDs which Parabola replaces; but should be used cautiously. The usual reason # why Parabola replaces those packages is because the upstream source or recipes are # not FSDG-fit; so you must apply your own judgement when using this tool. readonly DEBUG=0 readonly ARCH_DIR=/packages/arch readonly ARTIX_DIR=/packages/artix readonly AUR_DIR=/packages/aur readonly ABSLIBRE_DIR=/packages/abslibre readonly ARCH_URL=https://gitlab.archlinux.org/archlinux/packaging readonly ARTIX_URL=https://gitea.artixlinux.org readonly AUR_URL=https://aur.archlinux.org readonly CAQUA='\033[00;36m' readonly CYELLOW='\033[01;33m' readonly CEND='\033[00m' readonly CLONE_MSG="attempting to clone from" PKGNAME_RX= # deferred SORT= # deferred PKGNAME= # deferred MeldPkgbuilds() { DBG() { echo -e "${CAQUA}DEBUG: $@${CEND}" >&2 ; } WARN() { echo -e "${CYELLOW}WARNING: $@${CEND}" >&2 ; } get_repo() { LANG=C sudo pacman --config /etc/pacman-all.conf -Syi ${PKGNAME} 2>/dev/null | \ grep ^Repository | sort ${SORT} | head -n 1 | cut -d : -f 2 | tr -d ' ' } find_statefile() { local state_dir="${ARCH_DIR}"/state local statefile="$(find "${state_dir}"/{core,extra,multilib}-{any,$(uname -m)} \ -maxdepth 2 -type f -name ${PKGNAME} 2> /dev/null )" echo "${statefile}" [[ -n "${statefile}" ]] } verify_writable_dir() # ("target_dir") { local target_dir="$1" local param_err_msg="invalid targ# NOTE: you should install 'pacman-all.conf'et_dir '${target_dir}' - quitting" local perms_err_msg="lacking write permission for '${target_dir}' - quitting" [[ -n "${target_dir}" ]] || ! echo "${param_err_msg}" || return 1 mkdir -p "${target_dir}" || ! echo "${perms_err_msg}" || return 1 } clone_or_pull() # (git_url "target_dir") { # eg: | $ARCH_URL | | $PKGNAME | # eg: https://gitlab.archlinux.org/archlinux/packaging/packages/qutebrowser.git local git_url=$1 local target_dir="$2" if (( DEBUG )) ; then ([[ ! -d "${target_dir}"/.git ]] && DBG "git clone ${git_url} \"${target_dir}\"" || DBG "(cd \"${target_dir}\" ; git pull ;)" ) >&2 ; fi if [[ ! -d "${target_dir}"/.git ]] then git clone ${git_url} "${target_dir}" 2>&1 else (cd "${target_dir}" ; git fetch &> /dev/null ; git pull ;) # fi &> /dev/null fi | grep -E 'Cloning|Fast-forward|Already up to date.' } find_pkgbuild_dirs() # ("pkgbuilds_dir") { local pkgbuilds_dir="$1" dirname $(find "${pkgbuilds_dir}" -path "*/${PKGNAME}/PKGBUILD" | sort ${SORT}) 2> /dev/null } find_pkgbuild_dir() # ("pkgbuilds_dir") { find_pkgbuild_dirs "$1" | head -n 1 } local blacklist_entry=$(libreblacklist get ${PKGNAME} 2> /dev/null) local arch_dir="$( find_pkgbuild_dir ${ARCH_DIR} )" local artix_dir="$( find_pkgbuild_dir ${ARTIX_DIR} )" local aur_dir="$( find_pkgbuild_dir ${AUR_DIR} )" local abslibre_dir="$(find_pkgbuild_dir ${ABSLIBRE_DIR})" local is_nonsystemd local cached_dir local upstream local upstream_url local source_dir local statefile local arch local pkgver (( DEBUG )) && DBG "PKGNAME='$PKGNAME'" (( DEBUG )) && DBG "arch_dir='$arch_dir'" (( DEBUG )) && DBG "artix_dir='$artix_dir'" (( DEBUG )) && DBG "aur_dir='$aur_dir'" (( DEBUG )) && DBG "abslibre_dir='$abslibre_dir'" if [[ -n "${blacklist_entry}" ]] then echo "blacklist reason: $(libreblacklist get-reason <<<${blacklist_entry})" else echo "pkgname: '${PKGNAME}' is not blacklisted" fi if [[ -n "${arch_dir}${artix_dir}${aur_dir}${abslibre_dir}" ]] then echo "cached PKGBUILDs:" find_pkgbuild_dirs ${ARCH_DIR} | sed 's|^| |' find_pkgbuild_dirs ${ARTIX_DIR} | sed 's|^| |' find_pkgbuild_dirs ${AUR_DIR} | sed 's|^| |' find_pkgbuild_dirs ${ABSLIBRE_DIR} | sed 's|^| |' fi # consult the arch 'state' repo first echo "consulting arch state repo ...." clone_or_pull ${ARCH_URL}/state.git "${ARCH_DIR}"/state ((! $?)) && echo "synchronized state repo" || WARN "failed to synchronize state repo" if statefile="$(find_statefile)" then repo=$(basename $(dirname "${statefile}")) arch=${repo##*-} ; repo=${repo%-*} ; pkgver=$(cut -d ' ' -f 2 ${statefile}) # [pkgbase pkgver git-tag checksum] # eg: 'qutebrowser' (extra/any) echo "found statefile for '${PKGNAME}' (${arch}/${repo} ${pkgver})" else WARN "statefile not found for '${PKGNAME}'" fi (( DEBUG )) && DBG "abslibre_dir='$abslibre_dir'" (( DEBUG )) && DBG "pkgbuild='$pkgbuild'" (( DEBUG )) && DBG "upstream='$upstream' upstream_url='$upstream_url'" (( DEBUG )) && DBG "source_dir(init)='${source_dir}'" # (( DEBUG )) && exit 0 # (( DEBUG )) && source_dir='' # set abslibre target directory is_nonsystemd=$( [[ "${abslibre_dir}" =~ /nonsystemd(-testing)?/[^/]*$ || \ "$(get_repo ${PKGNAME})" =~ ^nonsystemd(-testing)?$ || \ -z "${statefile}" ]] ; \ echo $((! $?)) ; ) cached_dir="${abslibre_dir}" if [[ -z "${abslibre_dir}" ]] then if [[ -n "${statefile}" ]] then if (( is_nonsystemd )) then abslibre_dir="${ABSLIBRE_DIR}"/nonsystemd/${PKGNAME} else abslibre_dir="${ABSLIBRE_DIR}"/libre/${PKGNAME} fi else if (( is_nonsystemd )) then abslibre_dir="${ABSLIBRE_DIR}"/nonsystemd/${PKGNAME} else abslibre_dir="${ABSLIBRE_DIR}"/pcr/${PKGNAME} fi fi fi # set upstream source if (( is_nonsystemd )) then [[ -z "${artix_dir}" ]] && echo "PKGBUILD not found in artix cache" upstream=artix ; upstream_url=${ARTIX_URL}/packages/${PKGNAME}.git ; source_dir="${ARTIX_DIR}"/${PKGNAME} else [[ -z "${arch_dir}" ]] && echo "PKGBUILD not found in arch cache" upstream=arch ; upstream_url=${ARCH_URL}/packages/${PKGNAME}.git ; source_dir="${ARCH_DIR}"/${PKGNAME} fi # warnings [[ -z "${arch_dir}" || -z "${aur_dir}" ]] || WARN "multiple upstream PKGBUILDs found (arch and AUR)" [[ -z "${artix_dir}" || -z "${aur_dir}" ]] || WARN "multiple upstream PKGBUILDs found (artix and AUR)" (( $(find_pkgbuild_dirs ${ABSLIBRE_DIR} | wc -l) < 2 )) || WARN "multiple abslibre PKGBUILDs found (use --reverse if necessary)" [[ -z "${cached_dir}" || "${abslibre_dir}" == "${cached_dir}" ]] || WARN "orphaned abslibre_dir: '${pkgbuild}' (cleanup manually)" # sanity checks verify_writable_dir "${source_dir}" || exit 1 verify_writable_dir "${abslibre_dir}" || exit 1 # search arch or artix VCS for PKGBUILD if [[ "$(curl -s -w '%{http_code}' -o /dev/null ${upstream_url%.git})" == 200 ]] then [[ -f "${source_dir}"/PKGBUILD ]] && echo "syncronizing ${upstream} ...." || echo "${CLONE_MSG} ${upstream} ...." clone_or_pull ${upstream_url} "${source_dir}" else WARN "${upstream} repo not found for '${PKGNAME}'" fi # search AUR VCS for PKGBUILD if [[ -f "${source_dir}"/PKGBUILD ]] then echo "PKGBUILD found in ${upstream}" else echo "PKGBUILD not found in ${upstream} ... ${CLONE_MSG} AUR ...." source_dir="${AUR_DIR}"/${PKGNAME} # posibbly same as $aur_dir verify_writable_dir "${source_dir}" || exit 1 [[ -f "${source_dir}"/PKGBUILD ]] && echo "PKGBUILD found in AUR cache" clone_or_pull ${AUR_URL}/${PKGNAME}.git "${source_dir}" [[ -f "${source_dir}"/PKGBUILD ]] || clone_or_pull ${AUR_URL}/${PKGNAME}-git.git "${source_dir}" [[ -f "${source_dir}"/PKGBUILD ]] && echo "PKGBUILD found in AUR" || [[ ! -d "${source_dir}"/.git ]] || WARN "VCS is empty: ${source_dir}" fi [[ -f ${source_dir}/PKGBUILD ]] || ! echo "PKGBUILD not found - quitting" || return 1 (( DEBUG )) && DBG "source_dir(final)='${source_dir}'" if [[ ! -f "${abslibre_dir}"/PKGBUILD ]] then echo "no such parabola PKGBUILD - seeding from upstream" # copy upstream recipe files and recurse mkdir -p "${abslibre_dir}" cp -a "${source_dir}"/* "${abslibre_dir}"/ fi (( DEBUG )) && DBG "DEBUG done - set DEBUG=0 to launch meld" && return echo -e "\nlaunching meld on:\n" ls -l "${source_dir}"/ "${abslibre_dir}"/ ; echo ; (set -x ; meld "${source_dir}"/ "${abslibre_dir}"/) return 0 } readonly PKGNAME_RX='^[-@\._\+0-9a-z]+$' ; readonly SORT=$( [[ "$1" == '--reverse' ]] && echo "$1" ) ; [[ -z "${SORT}" ]] || shift ; readonly PKGNAME=$( [[ "$1" =~ ${PKGNAME_RX} ]] && echo "$1" ) (( $# == 1 )) || ! echo "invalid params" || exit 1 [[ -n "${PKGNAME}" ]] || ! echo "invalid pkgname: '$1'" || exit 1 MeldPkgbuilds