#!/bin/sh # usage: why_dont_you $action $package1 $package2 ... # investigate, why a certain operation is not done with certain packages # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" # TODO: read information from database action="$1" shift tmp_dir=$(mktemp -d 'tmp.why-dont-you.XXXXXXXXXX' --tmpdir) trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT case "${action}" in 'build') # shellcheck disable=SC2016 { printf 'CREATE TEMPORARY TABLE `pkgbases` (`pkgbase` VARCHAR(64));\n' printf 'INSERT INTO `pkgbases` VALUES ' # shellcheck disable=SC2046 printf '(from_base64("%s")),' \ $( printf '%s\n' "$@" | \ base64_encode_each ) | \ sed 's/,$/;\n/' # we select everything which is possibly of any interest: # - id (to see if it actually is on the build-list # - to_build.is_broken # - failed_builds_count # - to_build.is_blocked # - deps.pkgbase (any dependency pending?) # - build_slaves.name (is anyone building this?) # - pkgbase printf 'SELECT DISTINCT `to_build`.`ba_id`,' printf 'If(`to_build`.`is_broken`,1,0),' printf '(' printf 'SELECT count(*) FROM `failed_builds`' printf 'WHERE `failed_builds`.`build_assignment`=`to_build`.`ba_id`' printf ')' printf ',replace(to_base64(`%s`.`%s`),"\\n","")' \ 'to_build' 'is_blocked' \ 'deps' 'pkgbase' \ 'build_slaves' 'name' \ 'pkgbases' 'pkgbase' # at least one row for each given `pkgbase` printf ' FROM `pkgbases`' printf ' LEFT JOIN ' printf '(' # join the tables for the to-be-built packages: # package_source, build_assignment, binary_package, repostory printf 'SELECT DISTINCT `tb_ps`.`pkgbase`,`tb_bin`.`id` AS `bin_id`,`tb_ba`.`id` AS `ba_id`,`tb_ba`.`is_blocked`,`tb_ba`.`is_broken`' printf ' FROM `package_sources` AS `tb_ps`' mysql_join_package_sources_build_assignments 'tb_ps' 'tb_ba' mysql_join_build_assignments_binary_packages 'tb_ba' 'tb_bin' mysql_join_binary_packages_repositories 'tb_bin' 'tb_rep' printf ' WHERE `tb_rep`.`name`="build-list"' printf ') AS `to_build`' printf ' ON `to_build`.`pkgbase`=`pkgbases`.`pkgbase`' printf ' LEFT JOIN ' printf '(' # same join as above, but with different names - for the # potential dependencies printf 'SELECT DISTINCT `dep_ps`.`pkgbase`,`dependencies`.`dependent`' printf ' FROM `package_sources` AS `dep_ps`' mysql_join_package_sources_build_assignments 'dep_ps' 'dep_ba' mysql_join_build_assignments_binary_packages 'dep_ba' 'dep_bin' mysql_join_binary_packages_repositories 'dep_bin' 'dep_rep' # now we have some (=3) additional joins, # because we are interested in dependency relations to `to_build` mysql_join_binary_packages_install_target_providers 'dep_bin' mysql_join_install_target_providers_dependencies mysql_join_dependencies_dependency_types printf ' WHERE `dep_rep`.`name`="build-list"' printf ' AND `dependency_types`.`relevant_for_building`' printf ') AS `deps`' printf ' ON `deps`.`dependent`=`to_build`.`bin_id`' # now we join with build slaves to see if someone builds this printf ' LEFT JOIN `build_slaves` ON `build_slaves`.`currently_building`=`to_build`.`ba_id`' printf ';\n' } | \ mysql_run_query | \ tr '\t' ' ' | \ sort -k7,7 -k6,6 -k5,5 | \ sed ' / NULL \S\+$/ b multi-dep :multi-slave $!N s/^\(\(\S\+ \)\{5\}\)\(\S\+\)\( \S\+\)\n\(\S\+ \)\{5\}\(\S\+\)\4/\1\3,\6\4/ t multi-slave P D :multi-dep / NULL\( \S\+\)\{3\}$/! b $!N s/^\(\(\S\+ \)\{4\}\)\(\S\+\)\(\( \S\+\)\{2\}\)\n\(\S\+ \)\{4\}\(\S\+\)\4/\1\3,\7\4/ t multi-dep P D ' | \ sed ' s/NULL,//g ' | \ while read -r id is_broken trials is_blocked dependency slave pkgbase; do pkgbase=$( printf '%s' "${pkgbase}" | \ base64 -d ) if [ "${id}" = 'NULL' ]; then >&2 printf '"%s" is not on the build list.\n' \ "${pkgbase}" continue fi if [ "${slave}" != 'NULL' ]; then # beware: A slave named "5BË" will look exactly like this! printf '"%s" is locked by %s.\n' \ "${pkgbase}" \ "$( printf '%s\n' "${slave}" | \ tr ',' '\n' | \ while read -r line; do printf '%s\n' "${line}" | \ base64 -d printf ',' done | \ sed 's/,$//' )" continue fi if [ "${is_blocked}" != 'NULL' ]; then # beware: A block-reason "5BË" will look exactly like this! printf '"%s" is blocked: "%s".\n' \ "${pkgbase}" \ "$( printf '%s' "${is_blocked}" | \ base64 -d )" continue fi if [ "${dependency}" != 'NULL' ]; then printf '"%s" has unmet dependencies:\n' \ "${pkgbase}" printf '%s\n' "${dependency}" | \ tr ',' '\n' | \ while read -r line; do printf ' ' printf '%s\n' "${line}" | \ base64 -d printf '\n' done continue fi if [ "${is_broken}" = '1' ]; then printf '"%s" is broken (%sx built), but would be built.\n' \ "${pkgbase}" \ "${trials}" continue fi printf '"%s" would be built.\n' \ "${pkgbase}" done ;; 'stabilize'|'unstage') if [ "${action}" = 'stabilize' ]; then suffix='tested' else suffix='done' fi { awk '{print $1 "." $2 "." $3 "." $4}' < \ "${work_dir}/build-list" if [ "${action}" = 'stabilize' ]; then find "${work_dir}/package-states" -maxdepth 1 \( \ -name '*.done' -o \ -name '*.testing' \ \) -printf '%f\n' | \ sed 's|\.[^.]\+$||' fi } | \ sort -u > \ "${tmp_dir}/unmoveable-list" find "${work_dir}/package-states" -maxdepth 1 -name "*.${suffix}" -printf '%f\n' | \ sed 's|\.[^.]\+$||' | \ sort -u > \ "${tmp_dir}/moveable-list" cat "${tmp_dir}/moveable-list" "${tmp_dir}/unmoveable-list" | \ sed ' s|^|'"${work_dir}/package-infos/"'| s|$|.run-depends| ' | \ # base is boring, so we ignore it (might give result "... can be unstaged" although it _will_not_ be unstaged! xargs -r grep -vHxF 'base' | \ sed ' s|^[^:]*/|| s|\.run-depends:| | ' | \ sort -k2,2 > \ "${tmp_dir}/all-run-depends" cat "${tmp_dir}/moveable-list" "${tmp_dir}/unmoveable-list" | \ sed ' s|^|'"${work_dir}/package-infos/"'| s|$|.builds| ' | \ # base is boring, so we ignore it (might give result "... can be unstaged" although it _will_not_ be unstaged! xargs -r grep -vHxF 'base' | \ sed ' s|^[^:]*/|| s|\.builds:| | ' | \ sort -k2,2 > \ "${tmp_dir}/all-builds" for pkg in "$@"; do if ! state_file=$( grep '^'"$(str_to_regex "${pkg}")"'\(\.[^.]\+\)\{3\}$' "${tmp_dir}/moveable-list" ) || \ [ ! -f "${work_dir}/package-states/${state_file}.${suffix}" ]; then printf '"%s" is not %s yet!\n' "${pkg}" "${suffix}" continue fi echo "${state_file}" > "${tmp_dir}/dependent.new" touch "${tmp_dir}/dependent" while [ -s "${tmp_dir}/dependent.new" ]; do cat "${tmp_dir}/dependent.new" "${tmp_dir}/dependent" | \ sort -u | \ sponge "${tmp_dir}/dependent" { sed ' s|^|'"${work_dir}"'/package-infos/| s|$|.builds| ' "${tmp_dir}/dependent.new" | \ xargs -r cat | \ sort -u | \ join -1 1 -2 2 -o 2.1 - "${tmp_dir}/all-run-depends" sed ' s|^|'"${work_dir}"'/package-infos/| s|$|.run-depends| ' "${tmp_dir}/dependent.new" | \ xargs -r cat | \ sort -u | \ join -1 1 -2 2 -o 2.1 - "${tmp_dir}/all-builds" } | \ sort -u | \ sponge "${tmp_dir}/dependent.new" cat "${tmp_dir}/dependent.new" "${tmp_dir}/dependent" "${tmp_dir}/dependent" | \ sort | \ uniq -u | \ sponge "${tmp_dir}/dependent.new" done dependent_unmoveable=$( join -1 1 -2 1 -o 2.1 "${tmp_dir}/unmoveable-list" "${tmp_dir}/dependent" ) if [ -n "${dependent_unmoveable}" ]; then printf 'The following packages are dependent on "%s", but cannot be %sd:\n' "${pkg}" "${action}" echo "${dependent_unmoveable}" | \ while read -r sf; do printf '%s' "${sf}" if [ -f "${work_dir}/package-states/${sf}.testing" ]; then printf ' (not tested yet for %s days)' "$(( ($(date '+%s') - $(stat -c '%Y' "${work_dir}/package-states/${sf}.testing")) / 3600 / 24 ))" elif [ -f "${work_dir}/package-states/${sf}.done" ]; then printf ' (not unstaged yet for %s days)' "$(( ($(date '+%s') - $(stat -c '%Y' "${work_dir}/package-states/${sf}.done")) / 3600 / 24 ))" elif tr ' ' '.' < \ "${work_dir}/build-list" | \ grep -qxF "${sf}"; then printf ' (not built yet)' fi printf '\n' done printf '\n' continue fi printf 'Package "%s" can be %sd.\n' "${pkg}" "${action}" done ;; 'keep'|'stubbornly_keep') find '/var/lib/pacman/sync' -name '*.db' -execdir bsdtar -tf '{}' \; | \ sed -n ' s|-[^-]\+-[^-]\+/$|| T p ' | \ sort -u > \ "${tmp_dir}/upstream-packages" while read -r pkg; do if builds_file=$( find "${work_dir}/package-infos" -maxdepth 1 -printf '%f\n' | \ grep -m1 '^'"$(str_to_regex "${pkg}")"'\(\.[^.]\+\)\{3\}\.builds$' ); then builds_file="${builds_file%.*}" prepo="${builds_file##*.}" builds_file="${builds_file%.*}" mod_rev="${builds_file##*.}" builds_file="${builds_file%.*}" rev="${builds_file##*.}" else found_PKGBUILD=false mod_rev=$(cat "${work_dir}/archlinux32.revision") for repo in ${repo_names}; do eval 'repo_path="${repo_paths__'"${repo}"'}"' rev=$(cat "${work_dir}/${repo}.revision") if [ "${repo}" = 'archlinux32' ]; then if git -C "${repo_path}" archive "${mod_rev}" | \ grep -q '^[^/]\+/'"$(str_to_regex "${pkg}")"'/PKGBUILD$'; then prepo=$( git -C "${repo_path}" archive "${mod_rev}" | \ grep '^[^/]\+/'"$(str_to_regex "${pkg}")"'/PKGBUILD$' | \ cut -d/ -f1 ) found_PKGBUILD=true break fi else prepo=$( git -C "${repo_path}" archive "${rev}" -- "${pkg}/repos" 2>/dev/null | \ tar -t 2> /dev/null | \ grep '^[^/]\+/repos/[^/]\+/PKGBUILD$' | \ grep -v -- '-i686/PKGBUILD$' | \ grep -v -- '[-/]\(staging\|testing\|unstable\)-[^/]\+/PKGBUILD$' | \ sed ' s|^[^/]\+/repos/\([^/]\+\)-[^/-]\+/PKGBUILD$|\1| ' | \ head -n1 ) if [ -n "${prepo}" ]; then found_PKGBUILD=true break fi fi done if ! ${found_PKGBUILD}; then continue fi generate_package_metadata "${pkg}" "${rev}" "${mod_rev}" "${prepo}" fi sed "s|^|${pkg} builds |" "${work_dir}/package-infos/${pkg}.${rev}.${mod_rev}.${prepo}.builds" done < \ "${work_dir}/deletion-list" > \ "${tmp_dir}/deleted.builds" sort -k3,3 "${tmp_dir}/deleted.builds" | \ sponge "${tmp_dir}/deleted.builds" for pkg in "$@"; do if ! grep -qxF "${pkg}" "${work_dir}/deletion-list"; then printf 'Package "%s" is not on the deletion list.\n' "${pkg}" continue fi if ! grep -qxF "${pkg}" "${tmp_dir}/upstream-packages"; then printf 'Package "%s" is not available upstream.\n' "${pkg}" if [ "${action}" = 'keep' ]; then continue fi fi if git -C "${repo_paths__archlinux32}" archive "$(cat "${work_dir}/archlinux32.revision")" -- blacklist | \ tar -Ox 'blacklist' | \ sed ' s/\s*#.*$// /^\s*$/d ' | \ grep -qxF "${pkg}"; then printf 'Package "%s" is explicitely blacklisted.\n' "${pkg}" continue fi if [ "lib32-${pkg#lib32-}" = "${pkg}" ]; then printf 'Package "%s" is a library from multilib.\n' "${pkg}" continue fi build_depends=$( find "${work_dir}/package-infos" -maxdepth 1 -name "${pkg}.*.build-depends" -exec cat {} \; ) if [ -z "${build_depends}" ]; then printf 'Package "%s" was deleted in the git repositories.\n' "${pkg}" continue fi build_depends=$( echo "${build_depends}" | \ sort -u ) errors=$( { # shellcheck disable=SC2086 printf '%s\n' ${build_depends} awk '{print $3}' "${tmp_dir}/deleted.builds" | \ sort -u } | \ sort | \ uniq -d | \ join -1 1 -2 3 -o 2.1,2.2,2.3 - "${tmp_dir}/deleted.builds" ) if [ -n "${errors}" ]; then printf 'Package "%s" has dependencies on the deletion list:\n' "${pkg}" # shellcheck disable=SC2086,SC2183 printf '%s %s %s\n' ${errors} printf '\n' continue fi printf 'It seems, package "%s" should not be deleted.\n' "${pkg}" done ;; *) >&2 printf 'unknown action "%s"\n' "${action}" exit 1 esac