#!/bin/sh # move binary packages from staging to testing (if possible [1]) and # additionally tested packages from testing to the respective stable # repository (if possible [1]) # The condition [1] is explained in the stored function # calculate_maximal_moveable_set which is created in bin/bootsrap-mysql # TODO: separate locks for staging, testing (and stable) # TODO: we should delete more packages than just the ones in repositories # where we move to (think of [extra] -> [community]) # shellcheck disable=SC2039 # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" # shellcheck disable=SC2016 usage() { >&2 echo '' >&2 echo 'db-update [options] [packages]:' >&2 echo ' move tested packages from testing to stable.' >&2 echo ' move possible packages from staging to testing.' >&2 echo '' >&2 echo 'possible options:' >&2 echo ' -b|--block:' >&2 echo ' If necessary, wait for lock blocking.' >&2 echo ' -h|--help:' >&2 echo ' Show this help and exit.' >&2 echo ' -n|--no-action:' >&2 echo ' Only print what would be moved.' >&2 echo ' -p|--progressive:' >&2 echo ' Move forward any package which replaces no package whose' >&2 echo ' dependencies are all available somewhere.' >&2 echo ' Note, that this _may_ move _less_ packages.' [ -z "$1" ] && exit 1 || exit "$1" } eval set -- "$( getopt -o bhnp \ --long block \ --long help \ --long no-action \ --long progressive \ -n "$(basename "$0")" -- "$@" || \ echo usage )" block_flag='-n' no_action=false progressive=false while true do case "$1" in -b|--block) block_flag='' ;; -h|--help) usage 0 ;; -n|--no-action) no_action=true ;; -p|--progressive) progressive=true ;; --) shift break ;; *) >&2 echo 'Whoops, forgot to implement option "'"$1"'" internally.' exit 42 ;; esac shift done if [ $# -ne 0 ]; then >&2 echo 'db-update: too many arguments' usage fi if [ -s "${work_dir}/build-master-sanity" ]; then >&2 echo 'Build master is not sane.' exit fi # Create tmp_dir, lock and trap. tmp_dir=$(mktemp -d "${work_dir}/tmp.db-update.XXXXXXXXXX") trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT exec 9> "${package_database_lock_file}" if ! flock ${block_flag} 9; then >&2 echo 'come back (shortly) later - I cannot lock package database.' exit 0 fi exec 8> "${sanity_check_lock_file}" if ! flock -s ${block_flag} 8; then >&2 echo 'come back (shortly) later - sanity-check currently running.' exit 0 fi for source_stability in 'testing' 'staging'; do find "${tmp_dir}" -mindepth 1 -delete # shellcheck disable=SC2016 { if ${progressive}; then printf 'DROP TEMPORARY TABLE IF EXISTS `%s_binary_packages`;\n' \ 'moveable' 'replaced' printf 'CREATE TEMPORARY TABLE `replaced_binary_packages` (`id` BIGINT, `replaced_by` BIGINT, UNIQUE KEY (`id`));\n' printf 'CREATE TEMPORARY TABLE `moveable_binary_packages` (`id` BIGINT, `to_repository` MEDIUMINT, UNIQUE KEY (`id`));\n' printf 'INSERT IGNORE INTO `replaced_binary_packages` (`id`,`replaced_by`)' printf ' SELECT `binary_packages`.`id`,`subst_bp`.`id`' printf ' FROM `binary_packages`' mysql_join_binary_packages_repositories printf ' AND `repositories`.`is_on_master_mirror`' mysql_join_binary_packages_build_assignments mysql_join_build_assignments_package_sources mysql_join_package_sources_upstream_repositories mysql_join_upstream_repositories_repository_moves printf ' AND `repository_moves`.`to_repository`=`binary_packages`.`repository`' printf ' JOIN `binary_packages` AS `subst_bp`' printf ' ON `binary_packages`.`pkgname`=`subst_bp`.`pkgname`' printf ' AND `repository_moves`.`from_repository`=`subst_bp`.`repository`' mysql_join_binary_packages_repositories 'subst_bp' 'subst_r' mysql_join_repositories_repository_stabilities 'subst_r' 'subst_rs' printf ' AND `subst_rs`.`name`="%s"' \ "${source_stability}" # TODO: we may relax this condition, if we a) got rid of state files # or b) improved keeping them in sync (*.testing -> /dev/null is not # implemented, only .tested -> /dev/null) if [ ! "${source_stability}" = 'staging' ]; then printf ' AND `subst_bp`.`is_tested`' fi mysql_join_binary_packages_dependencies mysql_join_dependencies_dependency_types printf ' AND `dependency_types`.`relevant_for_binary_packages`' printf ' WHERE NOT EXISTS (' printf 'SELECT * FROM `install_target_providers`' printf ' WHERE `install_target_providers`.`install_target`=`dependencies`.`depending_on`' printf ');\n' printf 'INSERT IGNORE INTO `moveable_binary_packages` (`id`,`to_repository`)' printf ' SELECT `replaced_binary_packages`.`replaced_by`,`binary_packages`.`repository`' printf ' FROM `replaced_binary_packages`' printf ' JOIN `binary_packages` ON `binary_packages`.`id`=`replaced_binary_packages`.`id`' printf ';\n' printf 'INSERT IGNORE INTO `moveable_binary_packages` (`id`,`to_repository`)' printf ' SELECT `binary_packages`.`id`,`repository_moves`.`to_repository`' printf ' FROM `binary_packages`' mysql_join_binary_packages_repositories printf ' AND `repositories`.`is_on_master_mirror`' mysql_join_repositories_repository_stabilities printf ' AND `repository_stabilities`.`name`="%s"' \ "${source_stability}" # TODO: we may relax this condition, if we a) got rid of state files # or b) improved keeping them in sync (*.testing -> /dev/null is not # implemented, only .tested -> /dev/null) if [ ! "${source_stability}" = 'staging' ]; then printf ' AND `binary_packages`.`is_tested`' fi mysql_join_binary_packages_build_assignments mysql_join_build_assignments_package_sources mysql_join_package_sources_upstream_repositories mysql_join_upstream_repositories_repository_moves printf ' AND `repository_moves`.`from_repository`=`binary_packages`.`repository`' printf ' WHERE NOT EXISTS (' printf 'SELECT * FROM `binary_packages` AS `repl_bp`' printf ' WHERE `repl_bp`.`pkgname`=`binary_packages`.`pkgname`' printf ' AND `repl_bp`.`repository`=`repository_moves`.`to_repository`' printf ');\n' else printf 'CALL calculate_maximal_moveable_set("%s");\n' \ "${source_stability}" fi printf 'CREATE TEMPORARY TABLE `rps` (`id` MEDIUMINT, UNIQUE INDEX (`id`));\n' printf 'INSERT IGNORE INTO `rps` (`id`)' printf ' SELECT `moveable_binary_packages`.`to_repository`' printf ' FROM `moveable_binary_packages`;\n' printf 'INSERT IGNORE INTO `rps` (`id`)' printf ' SELECT `binary_packages`.`repository`' printf ' FROM `moveable_binary_packages`' printf ' JOIN `binary_packages` ON `moveable_binary_packages`.`id`=`binary_packages`.`id`;\n' printf 'INSERT IGNORE INTO `rps` (`id`)' printf ' SELECT `binary_packages`.`repository`' printf ' FROM `replaced_binary_packages`' printf ' JOIN `binary_packages` ON `replaced_binary_packages`.`id`=`binary_packages`.`id`;\n' printf 'SELECT "repositories",`repositories`.`name`' printf ' FROM `repositories`' printf ' JOIN `rps` ON `rps`.`id`=`repositories`.`id`;\n' printf 'SELECT "mv.id",`moveable_binary_packages`.`id`,`moveable_binary_packages`.`to_repository`' printf ' FROM `moveable_binary_packages`;\n' printf 'SELECT "mv",' mysql_package_name_query printf ',`repositories`.`name`,`new_repo`.`name`' printf ' FROM `moveable_binary_packages`' printf ' JOIN `binary_packages` ON `moveable_binary_packages`.`id`=`binary_packages`.`id`' mysql_join_binary_packages_repositories mysql_join_binary_packages_architectures printf ' JOIN `repositories` AS `new_repo` ON `new_repo`.`id`=`moveable_binary_packages`.`to_repository`' printf ';\n' printf 'SELECT "rm.id",`replaced_binary_packages`.`id`' printf ' FROM `replaced_binary_packages`;\n' printf 'SELECT "rm",' mysql_package_name_query printf ',`repositories`.`name`' printf ' FROM `replaced_binary_packages`' printf ' JOIN `binary_packages` ON `replaced_binary_packages`.`id`=`binary_packages`.`id`' mysql_join_binary_packages_repositories mysql_join_binary_packages_architectures printf ';\n' } | \ mysql buildmaster -N --raw --batch | \ tr '\t' ' ' | \ grep '^\(repositories\|\(rm\|mv\)\(\.id\)\?\) ' | \ while read -r what content; do printf '%s\n' "${content}" >> \ "${tmp_dir}/${what}" done if [ ! -s "${tmp_dir}/repositories" ]; then >&2 printf 'Nothing to move from %s.\n' "${source_stability}" continue fi touch \ "${tmp_dir}/mv" \ "${tmp_dir}/mv.id" \ "${tmp_dir}/rm" \ "${tmp_dir}/rm.id" # shellcheck disable=SC2086 for s in "${tmp_dir}/"*; do sort -u "${s}" | \ sponge "${s}" done # receive the repository databases from the master mirror mkdir "${tmp_dir}/dbs" while read -r repo; do mkdir "${tmp_dir}/dbs/${repo}" # shellcheck disable=SC2086 ${master_mirror_rsync_command} \ "${master_mirror_rsync_directory}/i686/${repo}/${repo}.db."* \ "${master_mirror_rsync_directory}/i686/${repo}/${repo}.files."* \ "${tmp_dir}/dbs/${repo}/" tar -Oxzf "${tmp_dir}/dbs/${repo}/${repo}.db.tar.gz" --wildcards '*/desc' | \ sed -n ' /^%FILENAME%$/{ N s/^\S\+\n\(\S\+-[^-.]\+\)\(-[^-]\+\)/\1.0\2 \1\2/ T p } ' done < \ "${tmp_dir}/repositories" | \ while read -r old new; do for file in 'rm' 'mv'; do sed -i ' s/\(\s\|^\)'"$(str_to_regex "${old}")"'\(\s\|$\)/\1'"${new}"'\2/ ' "${tmp_dir}/${file}" done done # remove to-be-deleted packages # shellcheck disable=SC2094 cut -d' ' -f2 < \ "${tmp_dir}/rm" | \ sort -u | \ while read -r repo; do grep " $(str_to_regex "${repo}")\$" "${tmp_dir}/rm" | \ sed ' s/\(-[^-]\+\)\{3\} \S\+$// ' | \ xargs -r repo-remove -q "${tmp_dir}/dbs/${repo}/${repo}.db.tar.gz" done # copy and delete moved packages # shellcheck disable=SC2094 cut -d' ' -f2,3 < \ "${tmp_dir}/mv" | \ sort -u | \ while read -r from_repo to_repo; do grep " $(str_to_regex "${from_repo}") $(str_to_regex "${to_repo}")\$" "${tmp_dir}/mv" | \ sed ' s/-[^-]\+ \S\+ \S\+$// ' | \ xargs -r "${base_dir}/bin/repo-copy" \ "${tmp_dir}/dbs/${from_repo}/${from_repo}.db.tar.gz" \ "${tmp_dir}/dbs/${to_repo}/${to_repo}.db.tar.gz" grep " $(str_to_regex "${from_repo}") $(str_to_regex "${to_repo}")\$" "${tmp_dir}/mv" | \ sed ' s/\(-[^-]\+\)\{3\} \S\+ \S\+$// ' | \ xargs -r repo-remove -q \ "${tmp_dir}/dbs/${from_repo}/${from_repo}.db.tar.gz" done # move the packages remotely via sftp { sed ' s,^\(\S\+\) \(\S\+\)$,rm "i686/\2/\1"\nrm "i686/\2/\1.sig", ' "${tmp_dir}/rm" sed ' s,^\(\S\+\) \(\S\+\) \(\S\+\)$,rename "i686/\2/\1" "i686/\3/\1"\nrename "i686/\2/\1.sig" "i686/\3/\1.sig", ' "${tmp_dir}/mv" echo 'quit' } | \ if ${no_action}; then sed 's|^|sftp: |' else ${master_mirror_sftp_command} fi if ${no_action}; then continue fi # and push our local *.db.tar.gz via rsync while read -r repo; do # shellcheck disable=SC2086 ${master_mirror_rsync_command} \ "${tmp_dir}/dbs/${repo}/${repo}.db."* \ "${tmp_dir}/dbs/${repo}/${repo}.files."* \ "${master_mirror_rsync_directory}/i686/${repo}/" done < \ "${tmp_dir}/repositories" # shellcheck disable=SC2016 { printf 'CREATE TEMPORARY TABLE `replaced_binary_packages` (`id` BIGINT, UNIQUE KEY (`id`));\n' printf 'CREATE TEMPORARY TABLE `moved_binary_packages` (`id` BIGINT, `new_repository` MEDIUMINT, UNIQUE KEY (`id`));\n' printf 'LOAD DATA LOCAL INFILE "%s" INTO TABLE `%s` COLUMNS TERMINATED BY " ";\n' \ "${tmp_dir}/mv.id" 'moved_binary_packages' \ "${tmp_dir}/rm.id" 'replaced_binary_packages' printf 'DELETE `binary_packages` FROM `binary_packages`' printf ' JOIN `replaced_binary_packages` ON `binary_packages`.`id`=`replaced_binary_packages`.`id`;\n' printf 'UPDATE `binary_packages`' printf ' JOIN `moved_binary_packages` ON `binary_packages`.`id`=`moved_binary_packages`.`id`' printf ' SET `binary_packages`.`repository`=`moved_binary_packages`.`new_repository`;\n' } | \ mysql_run_query sed_apply_rm=$( while read -r pkg _ _; do printf '/^%s$/d\n' "$(str_to_regex "${pkg}")" done < \ "${tmp_dir}/rm" ) sed_apply_remove_part_of_mv=$( while read -r pkg _ _; do printf '/^%s$/d\n' "$(str_to_regex "${pkg}")" done < \ "${tmp_dir}/mv" ) sed_apply_move_part_of_mv=$( while read -r pkg _ _; do printf '/^%s$/{\n' "$(str_to_regex "${pkg}")" printf 'w %%s\n' printf 'd\n' printf '}\n' done < \ "${tmp_dir}/mv" ) find "${work_dir}/package-states/" \ \( \ -name '*.done' -o \ -name '*.testing' -o \ -name '*.tested' \ \) \ -exec sed -i "${sed_apply_rm}" '{}' \; find "${work_dir}/package-states/" \ -name '*.tested' \ -exec sed -i "${sed_apply_remove_part_of_mv}" '{}' \; find "${work_dir}/package-states/" \ -name '*.done' \ -exec grep -qxF "$(cut -d' ' -f1 < "${tmp_dir}/mv")" '{}' \; \ -printf '%p\n' | \ while read -r sf; do sed -i "$( printf '%s\n' "${sed_apply_move_part_of_mv}" | \ sed ' s,%s,'"${sf%.done}.testing"',g ' )" "${sf}" done find "${work_dir}/package-states/" \ \( \ -name '*.done' -o \ -name '*.testing' -o \ -name '*.tested' \ \) \ -type f \ -not -exec test -s '{}' \; \ -delete done trigger_mirror_refreshs