index : builder | |
Archlinux32 build system | gitolite user |
summaryrefslogtreecommitdiff |
@@ -5,10 +5,18 @@ This includes scripts to be run on the build master as well as scripts to be run ## requirements * `moreutils` ### build master only -* some ssh-server -* `git` -* `pkgbuild-introspection` +* `base-devel` * `bc` +* `git` +* `graphviz` +* `ii` +* some mysql-server +* `nginx` or equivalent +* `php-gd` +* `php-fpm` or quivalent +* `rsync` +* `screen` +* some ssh-server ### build slave only * some ssh-client * `wget` diff --git a/bin/bootstrap-mysql b/bin/bootstrap-mysql index 46678f7..86fa645 100755 --- a/bin/bootstrap-mysql +++ b/bin/bootstrap-mysql @@ -3,6 +3,10 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" +# TODO: this should become some sort of mysql dump to bootstrap the +# tables and stored functions only - when the database is gone, we're +# pretty much screwed anyway :-D + tmp_dir=$(mktemp -d 'tmp.bootstrap-mysql.XXXXXXXXXX' --tmpdir) trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT @@ -32,6 +36,8 @@ done | \ sort -k2,2 > \ "${tmp_dir}/master-mirror-listing" +# TODO: include hash of binary package in mysql database + if [ ! "$1" = 'slim' ]; then tables=$( printf '%s\n' \ @@ -61,6 +67,7 @@ if [ ! "$1" = 'slim' ]; then ' sub_pkgrel MEDIUMINT' \ ' has_issues BIT' \ ' is_tested BIT' \ + ' is_to_be_deleted BIT' \ ' pkgname VARCHAR(64)' \ ' architecture SMALLINT :architectures' \ ' UNIQUE content build_assignment sub_pkgrel pkgname architecture repository' \ @@ -81,7 +88,9 @@ if [ ! "$1" = 'slim' ]; then ' package_source BIGINT :package_sources' \ ' architecture SMALLINT :architectures' \ ' is_blocked VARCHAR(128) NULL' \ + ' is_black_listed TEXT NULL' \ ' is_broken BIT' \ + ' priority SMALLINT' \ ' UNIQUE content package_source architecture' \ 'build_dependency_loops BIGINT' \ ' loop MEDIUMINT' \ @@ -99,6 +108,8 @@ if [ ! "$1" = 'slim' ]; then ' git_revision VARCHAR(40)' \ ' mod_git_revision VARCHAR(40)' \ ' upstream_package_repository SMALLINT :upstream_repositories' \ + ' uses_upstream BIT' \ + ' uses_modification BIT' \ ' UNIQUE content pkgbase git_revision mod_git_revision' \ 'upstream_repositories SMALLINT' \ ' name VARCHAR(64)' \ @@ -122,9 +133,40 @@ if [ ! "$1" = 'slim' ]; then ' build_assignment BIGINT :build_assignments' \ ' date DATETIME' \ ' reason SMALLINT :fail_reasons' \ - ' log_file VARCHAR(512)' + ' log_file VARCHAR(512)' \ + 'todos MEDIUMINT' \ + ' file VARCHAR(64)' \ + ' line MEDIUMINT' \ + ' description VARCHAR(512)' + 'todo_links MEDIUMINT' \ + ' dependent MEDIUMINT' \ + ' depending_on MEDIUMINT' \ + 'repository_stability_relations MEDIUMINT' \ + ' more_stable MEDIUMINT :repository_stabilities' \ + ' less_stable MEDIUMINT :repository_stabilities' \ + 'repository_moves MEDIUMINT' \ + ' from_repository MEDIUMINT :repositories' \ + ' to_repository MEDIUMINT :repositories' \ + ' upstream_package_repository SMALLINT :upstream_repositories' \ + ' UNIQUE source from_repository upstream_package_repository' \ + 'statistics BIGINT' \ + ' date DATETIME' \ + ' stable_packages_count MEDIUMINT' \ + ' pending_tasks_count MEDIUMINT' \ + ' pending_packages_count MEDIUMINT' \ + ' staging_packages_count MEDIUMINT' \ + ' testing_packages_count MEDIUMINT' \ + ' tested_packages_count MEDIUMINT' \ + ' broken_tasks_count MEDIUMINT' \ + ' dependency_loops_count MEDIUMINT' \ + ' dependency_looped_tasks_count MEDIUMINT' \ + ' locked_tasks_count MEDIUMINT' \ + ' blocked_tasks_count MEDIUMINT' \ + ' next_tasks_count MEDIUMINT' \ + ' UNIQUE date' ) + # shellcheck disable=SC2016 { printf '%s\n' \ '/*!40014 SET UNIQUE_CHECKS=0 */;' \ @@ -137,14 +179,11 @@ if [ ! "$1" = 'slim' ]; then while read -r table size; do case "${turn}" in 'drop') - # shellcheck disable=SC2016 printf 'DROP TABLE IF EXISTS `%s`;\n' "${table}" ;; 'create'|'link') if [ "${turn}" = 'create' ]; then - # shellcheck disable=SC2016 printf 'CREATE TABLE `%s` (\n' "${table}" - # shellcheck disable=SC2016 printf ' `id` %s NOT NULL AUTO_INCREMENT,\n' "${size}" fi echo "${tables}" | \ @@ -167,16 +206,14 @@ if [ ! "$1" = 'slim' ]; then if [ -z "${rest}" ]; then rest="${type}" fi - # shellcheck disable=SC2016 printf ' UNIQUE KEY `%s` (' "${type}" - # shellcheck disable=SC2016,SC2086 + # shellcheck disable=SC2086 printf '`%s`,' ${rest} | \ sed 's|,$||' printf ')' ;; *) if [ "${turn}" = 'create' ]; then - # shellcheck disable=SC2016 printf ' `%s` %s' "${column}" "${type}" if ! echo "${rest}" | \ grep -qwF 'NULL'; then @@ -187,7 +224,6 @@ if [ ! "$1" = 'slim' ]; then tr ' ' '\n' | \ sed -n 's/^://;T;p' | \ while read -r link; do - # shellcheck disable=SC2016 printf 'ALTER TABLE `%s` ADD FOREIGN KEY (`%s`) REFERENCES `%s` (`id`);\n' \ "${table}" \ "${column}" \ @@ -201,7 +237,6 @@ if [ ! "$1" = 'slim' ]; then fi done if [ "${turn}" = 'create' ]; then - # shellcheck disable=SC2016 printf ' PRIMARY KEY (`id`));\n' fi ;; @@ -214,28 +249,318 @@ if [ ! "$1" = 'slim' ]; then printf '%s\n' \ '/*!40014 SET UNIQUE_CHECKS=1 */;' \ '/*!40014 SET FOREIGN_KEY_CHECKS=1 */;' + + # show_broken_packages_and_dependencies + # gives a list of broken packages and their dependencies if they're + # still on the build- or deletion-list + printf 'DROP PROCEDURE IF EXISTS show_broken_packages_and_dependencies;\n' + printf 'DELIMITER //\n' + printf 'CREATE PROCEDURE show_broken_packages_and_dependencies()\n' + printf 'BEGIN\n' + printf 'CREATE TEMPORARY TABLE `%s` (`id` BIGINT, UNIQUE KEY (`id`));\n' \ + 'broken_packages_and_dependencies' 'broken_packages_and_dependencies_old' + printf 'INSERT IGNORE INTO `broken_packages_and_dependencies` (`id`)' + printf ' SELECT `build_assignments`.`id`' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_build_assignments + printf ' AND `build_assignments`.`is_broken`' + mysql_join_binary_packages_repositories + printf ' AND `repositories`.`name`="build-list";\n' + printf 'REPEAT\n' + printf 'INSERT IGNORE INTO `broken_packages_and_dependencies_old` (`id`)' + printf ' SELECT `broken_packages_and_dependencies`.`id` FROM `broken_packages_and_dependencies`;\n' + printf 'INSERT IGNORE INTO `broken_packages_and_dependencies` (`id`)' + printf ' SELECT `new_bp`.`build_assignment`' + printf ' FROM `broken_packages_and_dependencies_old`' + mysql_join_build_assignments_binary_packages 'broken_packages_and_dependencies_old' 'old_bp' + mysql_join_binary_packages_dependencies 'old_bp' + mysql_join_dependencies_dependency_types + printf ' AND `dependency_types`.`relevant_for_building`' + mysql_join_dependencies_install_target_providers + mysql_join_install_target_providers_binary_packages '' 'new_bp' + mysql_join_binary_packages_repositories 'new_bp' 'new_repo' + printf ' AND `new_repo`.`name` IN ("build-list","deletion-list");\n' + printf 'UNTIL ROW_COUNT()=0\n' + printf 'END REPEAT;\n' + printf 'SELECT ' + mysql_query_select_pkgbase_and_revision + printf ' JOIN `broken_packages_and_dependencies` ON `broken_packages_and_dependencies`.`id`=`build_assignments`.`id`;\n' + printf 'END\n' + printf '//\n' + printf 'DELIMITER ;\n' + + # calculate_dependencies_of_package_upto_first_built_one + # save binary packages (only their `id`) in `relevant_binary_packages` and `relevant_binary_packages_copy` + printf 'DROP PROCEDURE IF EXISTS calculate_dependencies_of_package_upto_first_built_one;\n' + printf 'DELIMITER //\n' + printf 'CREATE PROCEDURE calculate_dependencies_of_package_upto_first_built_one(IN `target_pkgbase` VARCHAR(64))\n' + printf 'BEGIN\n' + printf 'INSERT IGNORE INTO `relevant_binary_packages` (`id`)' + printf ' SELECT `binary_packages`.`id`' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_repositories + printf ' AND `repositories`.`name`="build-list"' + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_package_sources + printf ' WHERE `package_sources`.`pkgbase`=`target_pkgbase`;\n' + printf 'REPEAT\n' + printf 'INSERT IGNORE INTO `relevant_binary_packages_copy` (`id`)' + printf ' SELECT `relevant_binary_packages`.`id` FROM `relevant_binary_packages`;\n' + printf 'INSERT IGNORE INTO `relevant_binary_packages` (`id`)' + printf ' SELECT `install_target_providers`.`package`' + printf ' FROM `relevant_binary_packages_copy`' + printf ' JOIN `binary_packages` ON `relevant_binary_packages_copy`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_repositories + printf ' AND `repositories`.`name`="build-list"' + mysql_join_binary_packages_dependencies + mysql_join_dependencies_dependency_types + printf ' AND `dependency_types`.`relevant_for_building`' + mysql_join_dependencies_install_target_providers + printf ';\n' + printf 'UNTIL ROW_COUNT()=0\n' + printf 'END REPEAT;\n' + printf 'END\n' + printf '//\n' + printf 'DELIMITER ;\n' + + # calculate_maximal_moveable_set + # stores results in `moveable_binary_packages` and `replaced_binary_packages` + # Give a maximal list of packages to be moved, while implementing the + # condition from db-update: + + # Every package which is replaced[1], must have its provided install_targets: + # a) provided by another moved or not-replaced package or + # b) not required by any not-replaced package. + + # Every package being moved needs to have all dependencies + # installable in the target repository. + + # TODO: [1] A "replaced" package may also be in a different repository + # e.g. if a-2 is moved from [staging] to [testing] and there is only + # a-1 in [core], then this will be "replaced" by a-2 on a system + # running on [testing] repositories. + + printf 'DROP PROCEDURE IF EXISTS calculate_maximal_moveable_set;\n' + printf 'DELIMITER //\n' + printf 'CREATE PROCEDURE calculate_maximal_moveable_set(IN `from_stability` VARCHAR(32))\n' + printf 'BEGIN\n' + # variables to store count of changed rows + printf 'DECLARE row_count_saved INT DEFAULT 0;\n' + for copy in '' '_copy' '_copy2'; do + printf 'DROP TEMPORARY TABLE IF EXISTS `%s_binary_packages%s`;\n' \ + 'moveable' "${copy}" \ + 'replaced' "${copy}" + printf 'CREATE TEMPORARY TABLE `replaced_binary_packages%s` (`id` BIGINT, `replaced_by` BIGINT, UNIQUE KEY (`id`));\n' \ + "${copy}" + if [ "${copy}" = '_copy2' ]; then + continue + fi + printf 'CREATE TEMPORARY TABLE `moveable_binary_packages%s` (`id` BIGINT, `to_repository` MEDIUMINT, UNIQUE KEY (`id`));\n' \ + "${copy}" + done + # these packages are considered for moving: + 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 + mysql_join_repositories_repository_stabilities + 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`' + # correct stability: "testing"/"staging" - as chosen + printf ' WHERE `repository_stabilities`.`name` = `from_stability`' + printf ' AND (' + printf '`from_stability`="staging"' + # "testing" packages must be tested + printf ' OR `binary_packages`.`is_tested`' + printf ')' + # no open issues + printf ' AND NOT `binary_packages`.`has_issues`' + printf ';\n' + # these packages are considered for being replaced: + # for each moved package + printf 'INSERT IGNORE INTO `replaced_binary_packages` (`id`,`replaced_by`)' + printf ' SELECT `r_bp`.`id`,`m_bp`.`id`' + printf ' FROM `moveable_binary_packages`' + printf ' JOIN `binary_packages` AS `m_bp` ON `m_bp`.`id`=`moveable_binary_packages`.`id`' + mysql_join_binary_packages_repositories 'm_bp' 'm_r' + mysql_join_binary_packages_build_assignments 'm_bp' 'm_ba' + mysql_join_build_assignments_package_sources 'm_ba' 'm_ps' + mysql_join_package_sources_upstream_repositories 'm_ps' 'm_ur' + mysql_join_upstream_repositories_repository_moves 'm_ur' + printf ' AND `repository_moves`.`from_repository`=`m_r`.`id`' + # in its target repository + printf ' JOIN `repositories` AS `r_r` ON `repository_moves`.`to_repository`=`r_r`.`id`' + mysql_join_repositories_binary_packages 'r_r' 'r_bp' + # all packages with identical names + printf ' AND `r_bp`.`pkgname`=`m_bp`.`pkgname`;\n' + # now we delete all unreplaceable and unmoveable packages from the respective + # list until no further changes are required + printf 'REPEAT\n' + # create copies of our temporary tables *yuck* + for table in 'replaced' 'moveable'; do + for copy in '' '2'; do + if [ "${table}" = 'moveable' ] && \ + [ "${copy}" = '2' ]; then + continue + fi + printf 'DELETE FROM `%s_binary_packages_copy%s`;\n' "${table}" "${copy}" + printf 'INSERT IGNORE INTO `%s_binary_packages_copy%s`' "${table}" "${copy}" + printf ' SELECT `%s_binary_packages`.*' "${table}" + printf ' FROM `%s_binary_packages`;\n' "${table}" + done + done + # a package is not moveable if its dependencies are not provided ... + printf 'DELETE `replaced_binary_packages`,`moveable_binary_packages`' + printf ' FROM `replaced_binary_packages`' + printf ' RIGHT JOIN `moveable_binary_packages`' + printf ' ON `moveable_binary_packages`.`id`=`replaced_binary_packages`.`replaced_by`' + printf ' JOIN `binary_packages` ON `binary_packages`.`id`=`moveable_binary_packages`.`id`' + printf ' JOIN `repositories` AS `target_repositories` ON `moveable_binary_packages`.`to_repository`=`target_repositories`.`id`' + mysql_join_binary_packages_dependencies + mysql_join_dependencies_dependency_types + printf ' AND `dependency_types`.`relevant_for_binary_packages`' + # ... by a not-deleted, "more stable" package already in place or ... + printf ' WHERE NOT EXISTS (' + printf 'SELECT * FROM `install_target_providers`' + mysql_join_install_target_providers_binary_packages '' 'prov_bp' + mysql_join_binary_packages_repositories 'prov_bp' 'prov_r' + printf ' JOIN `repository_stability_relations` ON `prov_r`.`stability`=`repository_stability_relations`.`more_stable`' + printf ' WHERE `install_target_providers`.`install_target`=`dependencies`.`depending_on`' + printf ' AND `target_repositories`.`stability`=`repository_stability_relations`.`less_stable`' + printf ' AND NOT EXISTS (' + printf 'SELECT * FROM `replaced_binary_packages_copy`' + printf ' WHERE `replaced_binary_packages_copy`.`id`=`prov_bp`.`id`' + printf ')' + # ... by a moved package + printf ') AND NOT EXISTS (' + printf 'SELECT * FROM `install_target_providers`' + printf ' JOIN `moveable_binary_packages_copy` ON `moveable_binary_packages_copy`.`id`=`install_target_providers`.`package`' + printf ' WHERE `install_target_providers`.`install_target`=`dependencies`.`depending_on`' + printf ');\n' + printf 'SET @row_count_saved = ROW_COUNT();\n' + + # refresh copies of our temporary tables *yuck* + for table in 'replaced' 'moveable'; do + for copy in '' '2'; do + if [ "${table}" = 'moveable' ] && \ + [ "${copy}" = '2' ]; then + continue + fi + printf 'DELETE FROM `%s_binary_packages_copy%s`;\n' "${table}" "${copy}" + printf 'INSERT IGNORE INTO `%s_binary_packages_copy%s`' "${table}" "${copy}" + printf ' SELECT `%s_binary_packages`.*' "${table}" + printf ' FROM `%s_binary_packages`;\n' "${table}" + done + done + + # packages which should not be replaced: ones providing something that is: + # a) still needed by a not-replaced package x "less stable" than the target repository and + # b) not provided by: + # 1) a moved package or + # 2) any current, not-replaced package in a repository more stable than x's repository + # + # Note, that this is not 100% clean from an academic point of view: + # It might require _downgrading_ of a package to keep fulfilling a dependency + # and it might require to do so _without_ any chance for the user to notice, + # because there may be more "dependencies" in the database than in the package files. + # + # However, in practice both should not happen. + # + printf 'DELETE `replaced_binary_packages`,`moveable_binary_packages` FROM `replaced_binary_packages`' + printf ' JOIN `moveable_binary_packages` ON `replaced_binary_packages`.`replaced_by`=`moveable_binary_packages`.`id`' + printf ' JOIN `binary_packages` AS `repl_bp` ON `repl_bp`.`id`=`replaced_binary_packages`.`id`' + mysql_join_binary_packages_install_target_providers 'repl_bp' + mysql_join_install_target_providers_dependencies + printf ' AND NOT EXISTS (' + # dependencies of replaced packages don't matter + printf 'SELECT * FROM `replaced_binary_packages_copy`' + printf ' WHERE `replaced_binary_packages_copy`.`id`=`dependencies`.`dependent`' + printf ')' + mysql_join_dependencies_dependency_types + # consider only runtime dependencies + printf ' AND `dependency_types`.`relevant_for_binary_packages`' + mysql_join_dependencies_binary_packages '' 'req_bp' + # we need to check wether req_bp's dependency is (un)critical + mysql_join_binary_packages_repositories 'repl_bp' 'repl_r' + mysql_join_binary_packages_repositories 'req_bp' 'req_r' + # dependent package is "less stable" than dependency + printf ' JOIN `repository_stability_relations` AS `repl_rr`' + printf ' ON `repl_rr`.`more_stable`=`repl_r`.`stability`' + printf ' AND `repl_rr`.`less_stable`=`req_r`.`stability`' + # a) ^ + printf ' WHERE NOT EXISTS (' + # no moved package ... + printf 'SELECT *' + printf ' FROM `moveable_binary_packages_copy`' + mysql_join_binary_packages_install_target_providers 'moveable_binary_packages_copy' 'subst_itp' + # ... provides the same + printf ' WHERE `subst_itp`.`install_target`=`install_target_providers`.`install_target`' + # b) 1) ^ + printf ') AND NOT EXISTS (' + # no current package ... + printf 'SELECT *' + printf ' FROM `binary_packages` AS `subst_bp`' + mysql_join_binary_packages_install_target_providers 'subst_bp' 'subst_itp' + # ... in a repository ... + mysql_join_binary_packages_repositories 'subst_bp' 'subst_r' + # ... more stable ... + printf ' JOIN `repository_stability_relations` AS `subst_rr`' + printf ' ON `subst_rr`.`more_stable`=`subst_r`.`stability`' + # ... than x's repository ... + printf ' WHERE `subst_rr`.`less_stable`=`repl_r`.`stability`' + printf ' AND NOT EXISTS (' + # ... and which is not replaced ... + printf 'SELECT * FROM `replaced_binary_packages_copy2`' + printf ' WHERE `replaced_binary_packages_copy2`.`id`=`subst_bp`.`id`' + printf ')' + # ... and provides the same + printf ' AND `subst_itp`.`install_target`=`install_target_providers`.`install_target`' + # b) 2) ^ + printf ');\n' + printf 'UNTIL row_count_saved=0 AND ROW_COUNT()=0\n' + printf 'END REPEAT;\n' + for table in 'moveable' 'replaced'; do + for copy in '' '2'; do + if [ "${table}" = 'moveable' ] && \ + [ "${copy}" = '2' ]; then + continue + fi + printf 'DROP TEMPORARY TABLE `%s_binary_packages_copy%s`;\n' \ + "${table}" "${copy}" + done + done + printf 'END\n' + printf '//\n' + printf 'DELIMITER ;\n' + printf 'GRANT %s ON %s TO '"'"'buildmaster'"'"'@'"'"'localhost'"'"';\n' \ + 'CREATE ROUTINE' 'buildmaster.*' \ 'CREATE TEMPORARY TABLES' 'buildmaster.*' \ - 'SHOW VIEW' 'buildmaster.*' \ + 'EXECUTE' 'buildmaster.*' \ + 'RELOAD' '*.*' \ 'SELECT' 'buildmaster.*' \ - 'UPDATE' 'buildmaster.*' \ - 'RELOAD' '*.*' - printf 'GRANT %s ON %s TO '"'"'http'"'"'@'"'"'localhost'"'"';\n' \ + 'SELECT' 'mysql.proc' \ 'SHOW VIEW' 'buildmaster.*' \ - 'SELECT' 'buildmaster.*' + 'UPDATE' 'buildmaster.*' + printf 'GRANT %s ON %s TO '"'"'webserver'"'"'@'"'"'localhost'"'"';\n' \ + 'CREATE TEMPORARY TABLES' 'buildmaster.*' \ + 'SELECT' 'buildmaster.*' \ + 'SHOW VIEW' 'buildmaster.*' printf 'FLUSH PRIVILEGES;\n' } | \ - mysql -u root -p buildmaster + mysql_run_query -u root -p fi +# shellcheck disable=SC2016 { - # shellcheck disable=SC2016 printf 'INSERT IGNORE INTO `architectures` (`name`) VALUES ' printf '("%s"),' \ 'any' 'i686' | \ sed 's|,$||' printf ';\n' - # shellcheck disable=SC2016 printf 'INSERT IGNORE INTO `fail_reasons` (`identifier`,`name`,`severity`) VALUES ' printf '(from_base64("%s"),"%s",%s),' \ "$( @@ -276,7 +601,6 @@ fi )" 'unknown' 100 | \ sed 's|,$||' printf ';\n' - # shellcheck disable=SC2016 printf 'INSERT IGNORE INTO `git_repositories` (`name`,`url`,`directory`,`head`) VALUES' { for repo in ${repo_names}; do @@ -298,8 +622,8 @@ fi base64 -w0 )" \ "$( - base64_encode_each < \ - "${work_dir}/${repo}.revision" + git -C "${repo_path}" rev-parse HEAD | \ + base64_encode_each )" | \ sed 's|,$|),|' done @@ -314,9 +638,7 @@ fi 'extra:packages' \ 'multilib:packages' \ 'community:community'; do - # shellcheck disable=SC2016 printf 'INSERT IGNORE INTO `upstream_repositories` (`name`,`git_repository`) SELECT\n' - # shellcheck disable=SC2016 printf ' from_base64("%s"),`id` FROM `git_repositories` WHERE `name` = from_base64("%s");\n' \ "$( printf '%s' "${repo%:*}" | \ @@ -328,7 +650,6 @@ fi )" done - # shellcheck disable=SC2016 printf 'INSERT IGNORE INTO `build_slaves` (`name`,`ssh_key`,`operator`,`last_connection`) VALUES' { sed -n ' @@ -338,10 +659,13 @@ fi ' ~/.ssh/authorized_keys | \ while read -r name key; do case "${name}" in - 'nlopc'*|'rechenknecht'|'buildknecht'*) + 'nlopc'*|'rechenknecht') operator='deep42thought' ;; - 'eurobuild') + 'buildknecht'*) + operator='deep42thought/vollzornbrot' + ;; + 'eurobuild3') operator='abaumann' ;; *) @@ -368,20 +692,69 @@ fi } | \ sed 's|,;|;|' - # shellcheck disable=SC2016 printf 'INSERT IGNORE INTO `repository_stabilities` (`name`,`bugtracker_category`) VALUES' { printf '\n ("%s",%s),' \ - 'stable' '"Packages:Stable"' \ - 'testing' '"Packages:Testing"' \ + 'stable' '"Packages: Stable"' \ + 'testing' '"Packages: Testing"' \ 'staging' 'NULL' \ 'standalone' 'NULL' \ - 'unbuilt' '"Packages:Build-list"' \ + 'unbuilt' '"Packages: Build-list"' \ 'forbidden' 'NULL' printf ';\n' } | \ sed 's|,;|;|' + { + printf 'INSERT IGNORE INTO `repository_stability_relations` (`more_stable`,`less_stable`)' + printf ' SELECT `ms`.`id`,`ls`.`id`' + printf ' FROM `repository_stabilities` AS `ms` JOIN `repository_stabilities` AS `ls`' + printf ' WHERE ' + printf '(`ms`.`name`="%s" AND `ls`.`name`="%s") OR ' \ + 'stable' 'stable' \ + 'stable' 'testing' \ + 'stable' 'staging' \ + 'stable' 'standalone' \ + 'stable' 'unbuilt' \ + 'stable' 'forbidden' \ + 'testing' 'testing' \ + 'testing' 'staging' \ + 'testing' 'standalone' \ + 'testing' 'unbuilt' \ + 'testing' 'forbidden' \ + 'staging' 'staging' \ + 'staging' 'standalone' \ + 'staging' 'unbuilt' \ + 'staging' 'forbidden' \ + 'unbuilt' 'forbidden' \ + 'standalone' 'standalone' + printf ';\n' + } | \ + sed 's| OR ;|;|' + + { + printf 'INSERT IGNORE INTO `repository_moves` (`from_repository`,`to_repository`,`upstream_package_repository`)' + printf ' SELECT `f`.`id`,`t`.`id`,`u`.`id`' + printf ' FROM' + printf ' `repositories` AS `%s` JOIN' \ + 'f' 't' + printf ' `upstream_repositories` AS `u`' + printf ' WHERE ' + printf '(`f`.`name`="%s" AND `t`.`name`="%s" AND `u`.`name`="%s") OR ' \ + 'staging' 'testing' 'core' \ + 'staging' 'testing' 'extra' \ + 'staging' 'testing' 'multilib' \ + 'community-staging' 'community-testing' 'community' \ + 'community-staging' 'community-testing' 'multilib' \ + 'testing' 'core' 'core' \ + 'testing' 'extra' 'extra' \ + 'testing' 'extra' 'multilib' \ + 'community-testing' 'community' 'community' \ + 'community-testing' 'community' 'multilib' + printf ';\n' + } | \ + sed 's| OR ;|;|' + for repo in \ 'core:stable:AQ==' \ 'extra:stable:AQ==' \ @@ -393,9 +766,7 @@ fi 'community-staging:staging:AQ==' \ 'build-list:unbuilt:AA==' \ 'deletion-list:forbidden:AA=='; do - # shellcheck disable=SC2016 printf 'INSERT IGNORE INTO `repositories` (`name`,`stability`,`is_on_master_mirror`) SELECT' - # shellcheck disable=SC2016 printf ' from_base64("%s"),`id`,from_base64("%s") FROM `repository_stabilities` WHERE `name`=from_base64("%s");\n' \ "$( printf '%s' "${repo}" | \ @@ -413,80 +784,18 @@ fi )" done - # shellcheck disable=SC2016 printf 'INSERT IGNORE INTO `dependency_types` (`name`,`relevant_for_building`,`relevant_for_binary_packages`) VALUES' { printf '\n ("%s",%s,%s),' \ - 'build' '1' '0' \ + 'make' '1' '0' \ + 'check' '0' '0' \ + 'link' '0' '1' \ 'run' '1' '1' printf ';\n' } | \ sed 's|,;|;|' } | \ - mysql buildmaster - -find "${work_dir}/package-states" \ - -maxdepth 1 \ - -mindepth 1 \ - \( \ - -name '*.done' -o \ - -name '*.testing' -o \ - -name '*.tested' \ - \) \ - -exec sed ' - 1!d - s@^.*-\([^-]\+\)-\([^-.]\+\)\.pkg\.tar\.xz$@{} \1 \2 \0@ - s@^\S*/@@ - s/^\(\S\+\)\.\(done\|testing\|tested\) /\1 / - s/ \([0-9]\+\) / \1.0 / - s/ [0-9]\+\.\([0-9]\+\) / \1 / - ' {} \; | \ - while read -r state_file sub_pkgrel arch package_file; do - printf '%s ' "${state_file}" - repository=$( - grep '^\S\+ '"$(str_to_regex "${package_file}")"'$' "${tmp_dir}/master-mirror-listing" | \ - cut -d' ' -f1 - ) - if [ "$(echo "${repository}" | grep -cx '\S\+')" -ne 1 ]; then - printf 'not found exactly once on the master mirror.\n' - >&2 printf '"%s"\n' "${state_file}" "${sub_pkgrel}" "${arch}" "${package_file}" - continue - fi - mysql_generate_package_metadata "${sub_pkgrel}" "${repository}" "${state_file}" - printf ' ok\n' - done - -tr ' ' '.' < \ - "${work_dir}/build-list" | \ - while read -r state_file; do - mysql_generate_package_metadata "${state_file}" - done - -# shellcheck disable=SC2016 -find "${work_dir}/package-states" -name '*.blocked' -printf '%p %f\n' | \ - sed ' - s|\.\([^. ]\+\)\.\([^. ]\+\)\.\([^. ]\+\)\.blocked$| \1 \2 \3| - ' | \ - while read -r state_file pkgbase git_revision mod_git_revision repository; do - printf 'UPDATE `build_assignments`' - printf ' JOIN `%s` ON `%s`.`id`=`%s`.`%s`' \ - 'package_sources' 'package_sources' 'build_assignments' 'package_source' \ - 'upstream_repositories' 'upstream_repositories' 'package_sources' 'upstream_package_repository' - printf ' SET `build_assignments`.`is_blocked`=from_base64("%s")' \ - "$( - tr -d '\n' < \ - "${state_file}" | \ - base64 -w0 - )" - printf ' WHERE' - printf ' `package_sources`.`%s`=from_base64("%s") AND' \ - 'pkgbase' "$(printf '%s' "${pkgbase}" | base64 -w0)" \ - 'git_revision' "$(printf '%s' "${git_revision}" | base64 -w0)" \ - 'mod_git_revision' "$(printf '%s' "${mod_git_revision}" | base64 -w0)" - printf ' `upstream_repositories`.`name`=from_base64("%s");\n' \ - "$(printf '%s' "${repository}" | base64 -w0)" - done | \ - ${mysql_command} + mysql_run_query grep '^\('"$( # shellcheck disable=SC2086 @@ -520,10 +829,9 @@ grep '^\('"$( "${tmp_dir}/new-stable-packages" if [ -s "${tmp_dir}/new-stable-packages" ]; then + # shellcheck disable=SC2016 { - # shellcheck disable=SC2016 printf 'CREATE TEMPORARY TABLE `stable_packages` (' - # shellcheck disable=SC2016 printf '`%s` %s,' \ 'pkgname' 'VARCHAR(64)' \ 'epoch' 'MEDIUMINT' \ @@ -533,9 +841,7 @@ if [ -s "${tmp_dir}/new-stable-packages" ]; then 'architecture' 'VARCHAR(16)' \ 'repository' 'VARCHAR(64)' \ 'build_assignment' 'BIGINT NOT NULL AUTO_INCREMENT' - # shellcheck disable=SC2016 printf 'PRIMARY KEY (`build_assignment`));\n' - # shellcheck disable=SC2016 sed ' 1~10 ! b not_start s/^/INSERT IGNORE INTO `stable_packages` (`pkgname`,`epoch`,`pkgver`,`pkgrel`,`sub_pkgrel`,`architecture`,`repository`) VALUES \n/ @@ -546,29 +852,25 @@ if [ -s "${tmp_dir}/new-stable-packages" ]; then :end s/,$/;/ ' "${tmp_dir}/new-stable-packages" - # shellcheck disable=SC2016 - printf 'INSERT IGNORE INTO `binary_packages` (`pkgname`,`epoch`,`pkgver`,`pkgrel`,`sub_pkgrel`,`architecture`,`repository`,`build_assignment`,`has_issues`,`is_tested`)' + printf 'INSERT IGNORE INTO `binary_packages` (`pkgname`,`epoch`,`pkgver`,`pkgrel`,`sub_pkgrel`,`architecture`,`repository`,`build_assignment`,`has_issues`,`is_tested`,`is_to_be_deleted`)' printf ' SELECT ' - # shellcheck disable=SC2016 printf '`stable_packages`.`%s`,' \ 'pkgname' \ 'epoch' \ 'pkgver' \ 'pkgrel' \ 'sub_pkgrel' - # shellcheck disable=SC2016 printf '`%s`.`id`,' \ 'architectures' \ 'repositories' - # shellcheck disable=SC2016 - printf -- '-`build_assignment`,0,1 FROM `stable_packages`' - # shellcheck disable=SC2016 + printf -- '-`build_assignment`,0,1,0 FROM `stable_packages`' printf ' JOIN `%s` ON `stable_packages`.`%s`=`%s`.`name`' \ 'repositories' 'repository' 'repositories' \ 'architectures' 'architecture' 'architectures' printf ';\n' - # shellcheck disable=SC2016 printf 'DROP TABLE `stable_packages`;\n' } | \ - ${mysql_command} + mysql_run_query fi + +mysql_repair_binary_packages_without_build_assignment diff --git a/bin/build-master-status b/bin/build-master-status index 4d47976..c93a9aa 100755 --- a/bin/build-master-status +++ b/bin/build-master-status @@ -5,37 +5,31 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" +# TODO: replace by build-master-status-from-mysql + usage() { >&2 echo '' >&2 echo 'build-master-status: report about status of build master' >&2 echo '' >&2 echo 'possible options:' - >&2 echo ' -w|--web:' - >&2 echo ' Output to webserver instead of stdout.' >&2 echo ' -h|--help:' >&2 echo ' Show this help and exit.' [ -z "$1" ] && exit 1 || exit "$1" } eval set -- "$( - getopt -o hw \ + getopt -o h \ --long help \ - --long web \ -n "$(basename "$0")" -- "$@" || \ echo usage )" -web=false - while true do case "$1" in -h|--help) usage 0 ;; - -w|--web) - web=true - ;; --) shift break @@ -61,538 +55,177 @@ fi tmp_dir=$(mktemp -d 'tmp.build-master-status.XXXXXXXXXX' --tmpdir) trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT -stable=$( - ls_master_mirror 'i686' | \ - grep -v 'testing$\|staging$\|-unstable$' | \ - while read -r dir; do - ls_master_mirror "i686/${dir}" - done | \ - grep -c '\.pkg\.tar\.xz$' -) -tasks=$( - grep -c '^\S\+ \S\+ \S\+ \S\+$' \ - "${work_dir}/build-list" -) || true -pending_packages=$( - grep '^\S\+ \S\+ \S\+ \S\+$' "${work_dir}/build-list" | \ - tr ' ' '.' | \ - while read -r package; do - generate_package_metadata "${package}" 2>&1 > /dev/null - cat "${work_dir}/package-infos/${package}.packages" - done | - wc -l -) -next_tasks=$( - { - cat "${work_dir}/build-list" - find "${work_dir}/package-states" -maxdepth 1 \ - \( -name '*.broken' -o -name '*.blocked' \) \ - -printf '%f\n' | \ - sed ' - s|\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)\.[^.]\+$| \1 \2 \3| - p - ' - } | \ - sort | \ - uniq -u | \ - while read -r package git_revision mod_git_revision repository; do - if [ -z "$(find_dependencies_on_build_list "${package}" "${git_revision}" "${mod_git_revision}" "${repository}")" ]; then - echo "${package}" "${git_revision}" "${mod_git_revision}" "${repository}" - fi - done | \ - wc -l -) -staging=$( - find "${work_dir}/package-states" -name '*.done' \ - -exec cat '{}' \; | \ - sort -u | \ - wc -l -) -testing=$( - find "${work_dir}/package-states" -name '*.testing' \ - -exec cat '{}' \; | \ - sort -u | \ - wc -l -) -tested=$( - find "${work_dir}/package-states" -name '*.tested' \ - -exec cat '{}' \; | \ - sort -u | \ - wc -l -) { - find "${work_dir}/package-states/" -maxdepth 1 -name '*.broken' -printf '%f\n' | \ - sed 's|\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)\.[^.]\+$| \1 \2 \3|' | \ - while read -r pkg rev mod_rev repo; do - if [ -z "$(find_dependencies_on_build_list "${pkg}" "${rev}" "${mod_rev}" "${repo}")" ]; then - echo "${pkg}" - fi - done - { - find "${work_dir}/build-list.loops" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' \ - -exec cat '{}' \; | \ - sort -u - find "${work_dir}/package-states/" -maxdepth 1 -name '*.broken' -printf '%f\n' | \ - sed 's|\(\.[^.]\+\)\{4\}||' | \ - sort -u - } | \ - sort | \ - uniq -d -} | \ - sort -u > \ - "${tmp_dir}/broken-packages-names" -broken=$( - wc -l < \ - "${tmp_dir}/broken-packages-names" -) -blocked=$( - find "${work_dir}/package-states/" -maxdepth 1 -name '*.blocked' | \ - wc -l -) -locked=$( - find "${work_dir}/package-states/" -maxdepth 1 -name '*.locked' | \ - wc -l -) -loops=$( - find "${work_dir}/build-list.loops" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' | \ - wc -l -) -looped_packages=$( - find "${work_dir}/build-list.loops" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' \ - -exec cat '{}' \; | \ - sort -u | \ - wc -l -) - -{ - printf 'The mirror master contains %d stable packages (vs. ca. %d planned).\n' \ - "${stable}" \ - "$((staging+testing+tested+pending_packages))" - printf 'The build list contains %d tasks (incl. broken: %d, leading to %d packages), of which %s can be built immediately.\n' \ - "$((tasks-broken))" \ - "${tasks}" \ - "${pending_packages}" \ - "${next_tasks}" - printf 'There are %d testing (of which are %s tested) and %d staging packages.\n' \ - "$((testing+tested))" \ - "${tested}" \ - "${staging}" - printf 'There are %d broken package builds.\n' \ - "${broken}" - if [ "${loops}" -ne 0 ]; then - printf 'There are %d loops containing %d package builds.\n' \ - "${loops}" \ - "${looped_packages}" - fi - if [ $((broken+testing+tested+staging)) -ne 0 ]; then - printf '%.1f%% of all packages are broken.\n' \ - "$( - echo "scale=10; 100*${broken}/(${broken}+${testing}+${tested}+${staging})" | \ - bc - )" - fi - if [ $((testing+tested+staging+pending_packages-broken)) -ne 0 ]; then - printf '%.1f%% of the planned work has been done.\n' \ - "$( - echo "scale=10; 100*(${testing}+${staging})/(${testing}+${tested}+${staging}+${pending_packages}-${broken})" | \ - bc - )" - fi -} > \ - "${tmp_dir}/build-master-status.html" - -if ${web}; then - "${base_dir}/bin/calculate-dependent-packages" - { - printf '%s\n' \ - '<html>' \ - '<head>' \ - '<title>Status of archlinux32 build master</title>' \ - '<link rel="stylesheet" type="text/css" href="/static/style.css">' \ - '</head>' \ - '<body>' - sed 's|$|<br>|' "${tmp_dir}/build-master-status.html" - printf '%s\n' \ - '<br>' \ - 'currently building packages:<br>' \ - '<table>' - printf '<tr>' - printf '<th>%s</th>' \ - 'since (UTC)' \ - 'pkgname' \ - 'git revision' \ - 'modification git revision' \ - 'package repository' \ - 'build slave' - printf '</tr>' - find "${work_dir}/package-states" -maxdepth 1 -name '*.locked' \ - -printf '%T@ %TY-%Tm-%Td %TH:%TM %f ' \ - -execdir sed ' - :a - $!{ - N - s/\n/, / - ba - } - ' '{}' \; | \ - sort -k1n,1 | \ - sed ' - s|^\S\+ || - s|\.locked | | - s|\.\([^.]\+\)$| \1| - s|\.\([^.]\+\)$| \1| - s|\.\([^.]\+\)$| \1| - ' | \ - while read -r date time pkg rev mod_rev repo slaves; do - printf '<tr>' - printf '<td>%s</td>' \ - "${date} ${time}" \ - "${pkg}" \ - "<p style=\"font-size:8px\">${rev}</p>" \ - "<p style=\"font-size:8px\">$(modification_revision_link "${mod_rev}" "${repo}" "${pkg}")</p>" \ - "${repo}" \ - "${slaves}" - printf '</tr>\n' - done - printf '%s\n' \ - '</table>' \ - '</body>' \ - '</html>' - } | \ - sponge "${tmp_dir}/build-master-status.html" - end=$(($(date +%s)-7*24*60*60)) - { - [ -f "${webserver_directory}/statistics" ] && \ - cat "${webserver_directory}/statistics" - printf '%s ' \ - "$(date +%s)" \ - "${stable}" \ - "${tasks}" \ - "${pending_packages}" \ - "${staging}" \ - "${testing}" \ - "${broken}" \ - "${loops}" \ - "${looped_packages}" \ - "${locked}" \ - "${blocked}" \ - "${next_tasks}" \ - "${tested}" | \ - sed 's| $|\n|' - echo "${end}" - } | \ - sort -k1nr,1 | \ - sed -n " - /^${end}\$/q + printf '%s\n' \ + '<html>' \ + '<head>' \ + '<title>Todos in the build scripts</title>' \ + '</head>' \ + '<body>' + find "${base_dir}/bin/" "${base_dir}/conf/" -type f \ + -exec grep -nHF '' '{}' \; | \ + awk ' + { print $0 } + /^[^:]+:[0-9]+:\s*#\s*TODO:/{print ++i} + ' | \ + sed -n ' + s/^\([^:]\+\):\([0-9]\+\):\s*#\s*TODO:\s*/\1\n\2\n/ + T + N + s/\n\(.*\)\n\([0-9]\+\)$/\n\2\n\1/ + :a + N + s/\n[^:\n]\+:[0-9]\+:[ \t]*#[ \t]*\(\S[^\n]*\)$/\n\1/ + ta + s/\n[^:\n]\+:[0-9]\+:[^\n]*$/\n/ p - " | \ - tac > \ - "${tmp_dir}/statistics" - - find "${build_log_directory}/error" -maxdepth 1 -type f -name '*.build-log.gz' \( \ - \( \ - -exec zgrep -q '^==> ERROR: A failure occurred in build()\.$' {} \; \ - -printf '%f build()\n' \ - \) -o \ - \( \ - -exec zgrep -q '^==> ERROR: A failure occurred in check()\.$' {} \; \ - -printf '%f check()\n' \ - \) -o \ - \( \ - -exec zgrep -q '^==> ERROR: A failure occurred in prepare()\.$' {} \; \ - -printf '%f prepare()\n' \ - \) -o \ - \( \ - -exec zgrep -q '^==> ERROR: A failure occurred in package\(_\S\+\)\?()\.$' {} \; \ - -printf '%f package()\n' \ - \) -o \ - \( \ - -exec zgrep -q '^==> ERROR: Could not download sources\.$' {} \; \ - -printf '%f source\n' \ - \) -o \ - \( \ - -exec zgrep -q '^==> ERROR: '"'"'pacman'"'"' failed to install missing dependencies\.$' {} \; \ - -printf '%f dependencies\n' \ - \) -o \ - \( \ - -exec zgrep -q 'error: failed to commit transaction (invalid or corrupted package)$' {} \; \ - -printf '%f package-cache\n' \ - \) -o \ - \( \ - -exec zgrep -q '^==> ERROR: Running makepkg as root is not allowed as it can cause permanent,' {} \; \ - -printf '%f run-as-root\n' \ - \) -o \ - -printf '%f unknown\n' \ - \) | \ - sed ' - s|\(\.[^.]\+\)\{3\} | | ' | \ - sort -u | \ + tee "${tmp_dir}/todos" | \ sed ' :a - $!N - s/^\(\S\+\) \([^\n]\+\)\n\1 /\1 \2,/ - ta - P - D + N + /\n$/!ba + s|^[^\n]*/\([^/\n]\+/[^/\n]\+\)\n\([0-9]\+\)\n\([0-9]\+\)\n|<a href="#TODO\2" name="TODO\2">TODO #\2</a> - <a href="https://github.com/archlinux32/builder/blob/master/\1#L\3">\1 (line \3)</a>:\n| ' | \ - sort -k1,1 > \ - "${tmp_dir}/broken-packages.reason" - - { - printf '%s\n' \ - '<html>' \ - '<head>' \ - '<title>List of broken package builds</title>' \ - '<link rel="stylesheet" type="text/css" href="/static/style.css">' \ - '</head>' \ - '<body>' \ - '<a href="build-logs/">build logs</a><br>' \ - '<table>' \ - '<tr>' - printf '<th>%s</th>' \ - 'package' \ - 'git revision' \ - 'modification git revision' \ - 'package repository' \ - 'compilations' \ - 'dependent' \ - 'build error' \ - 'blocked' - printf '</tr>\n' - find "${work_dir}/package-states" -maxdepth 1 -name '*.broken' -printf '%f\n' | \ - sed 's|\.broken$||' | \ - sort -k1,1 | \ - join -j 1 - "${tmp_dir}/broken-packages.reason" | \ - sed 's|^\(\(.\+\)\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)\) \(\S\+\)$|\1 \2 \3 \4 \5 \6|' | \ - while read -r sf pkg rev mod_rev repo build_error; do - if grep -qxF "${pkg}" "${tmp_dir}/broken-packages-names"; then - printf '1 ' - else - printf '0 ' - fi - printf '%s ' \ - "${pkg}" \ - "${rev}" \ - "${mod_rev}" \ - "${repo}" \ - "$(wc -l < "${work_dir}/package-states/${sf}.broken")" \ - "$( - # shellcheck disable=SC2010 - ls -t "${webserver_directory}/build-logs/error" | \ - grep -m1 '^'"$(str_to_regex "${sf}.")"'[^.]\+\.build-log\.gz$' - )" \ - "$( - { - grep -m1 "^$(str_to_regex "${sf}") " "${work_dir}/dependent-count" || \ - echo 'x ' - } | \ - cut -d' ' -f2 - )" \ - "${build_error}" - if [ -f "${work_dir}/package-states/${sf}.blocked" ]; then - sed ' - s|\s\(wait for \)|\n\1|g - ' "${work_dir}/package-states/${sf}.blocked" | \ - while read -r blocked_reason; do - if echo "${blocked_reason}" | \ - grep -q '^wait for '; then - printf 'wait for ' - echo "${blocked_reason}" | \ - sed ' - s|^wait for || - s@\( and \| or \)@\n\1\n@ - ' | \ - while read -r reason; do - if [ "FS#${reason#FS#}" = "${reason}" ]; then - printf '<a href="https://bugs.archlinux.org/task/%s">%s</a>' \ - "${reason#FS#}" \ - "${reason}" - elif [ "FS32#${reason#FS32#}" = "${reason}" ]; then - printf '<a href="https://bugs.archlinux32.org/index.php?do=details&task_id=%s">%s</a>' \ - "${reason#FS32#}" \ - "${reason}" - elif grep -q "^$(str_to_regex "${reason}") " "${work_dir}/build-list"; then - printf '<a href="graphs/%s.png">%s</a>' \ - "${reason}" \ - "${reason}" - elif [ "${reason% *}" != "${reason}" ]; then - printf '%s' \ - "${reason}" - else - printf '<font color="red">%s</font>' \ - "${reason}" - fi - if read -r operator; then - printf ' %s ' "${operator}" - fi - done - else - echo "${blocked_reason}" - fi - done | \ - tr '\n' ' ' - else - printf ' ' - fi - printf '\n' - done | \ - sort -k6n,6 | \ - while read -r buildable pkg rev mod_rev repo count log_file dependent build_error reason; do - if [ "${buildable}" -eq 0 ]; then - left='(' - right=')' - else - unset left - unset right - fi - printf '<tr>' - mod_rev=$( - modification_revision_link "${mod_rev}" "${repo}" "${pkg}" - ) - build_error=$( - echo "${build_error}" | \ - sed 's|,|, |g' - ) - printf '<td>%s</td>' \ - '<a href="graphs/'"${pkg}"'.png">'"${left}${pkg}${right}"'</a>' \ - "<p style=\"font-size:8px\">${rev}</p>" \ - "<p style=\"font-size:8px\">${mod_rev}</p>" \ - "${repo}" \ - '<a href="build-logs/error/'"${log_file}"'">'"${count}"'</a>' \ - "${dependent}" \ - "${build_error}" \ - "${reason}" - printf '</tr>\n' - done - printf '%s\n' \ - '</table>' \ - '</body>' \ - '</html>' - } > \ - "${tmp_dir}/broken-packages.html" - - rm -f "${tmp_dir}/broken-packages-names" "${tmp_dir}/broken-packages.reason" - + sed ' + s|$|<br>| + ' + printf '%s\n' \ + '</body>' \ + '</html>' +} > \ + "${tmp_dir}/todos.html" + +if [ -s "${tmp_dir}/todos" ]; then + sed ' + :a + N + /\n$/!ba + s|^[^\n]*/\([^/\n]\+/[^/\n]\+\)\n\([0-9]\+\)\n\([0-9]\+\)\n|\1 \3 | + s/\n$// + s/\n/\\n/g + ' -i "${tmp_dir}/todos" + while read -r file line desc; do + printf '%s %s %s\n' \ + "$(printf '%s' "${file}" | base64 -w0)" \ + "$(printf '%s' "${line}" | base64 -w0)" \ + "$(printf '%s' "${desc}" | base64 -w0)" + done < \ + "${tmp_dir}/todos" | \ + sponge "${tmp_dir}/todos" + # update todos + # shellcheck disable=SC2016 + while read -r file line desc; do + printf 'UPDATE IGNORE `todos`' + printf ' SET `todos`.`line`=from_base64("%s")' \ + "${line}" + printf ' WHERE `todos`.`file`=from_base64("%s")' \ + "${file}" + printf ' AND `todos`.`description`=from_base64("%s");\n' \ + "${desc}" + + printf 'UPDATE IGNORE `todos`' + printf ' SET `todos`.`description`=from_base64("%s")' \ + "${desc}" + printf ' WHERE `todos`.`file`=from_base64("%s")' \ + "${file}" + printf ' AND `todos`.`line`=from_base64("%s");\n' \ + "${line}" + done < \ + "${tmp_dir}/todos" | \ + mysql_run_query + # insert unfound todos + # shellcheck disable=SC2016 { - printf '%s\n' \ - '<html>' \ - '<head>' \ - '<title>Todos in the build scripts</title>' \ - '</head>' \ - '<body>' - find "${base_dir}/bin/" "${base_dir}/conf/" -type f \ - -exec grep -nHF '' '{}' \; | \ - awk ' - { print $0 } - /^[^:]+:[0-9]+:\s*#\s*TODO:/{print ++i} - ' | \ - sed -n ' - s/^\([^:]\+\):\([0-9]\+\):\s*#\s*TODO:\s*/\1\n\2\n/ - T - N - s/\n\(.*\)\n\([0-9]\+\)$/\n\2\n\1/ - :a - N - s/\n[^:\n]\+:[0-9]\+:[ \t]*#[ \t]*\(\S[^\n]*\)$/\n\1/ - ta - s/\n[^:\n]\+:[0-9]\+:[^\n]*$/\n/ - p - ' | \ + printf 'SHOW CREATE TABLE `todos`' | \ + mysql_run_query | \ sed ' - :a - N - /\n$/!ba - s|^[^\n]*/\([^/\n]\+/[^/\n]\+\)\n\([0-9]\+\)\n\([0-9]\+\)\n|<a href="#TODO\2" name="TODO\2">TODO #\2</a> - <a href="https://github.com/archlinux32/builder/blob/master/\1#L\3">\1 (line \3)</a>:\n| - ' | \ - sed ' - s|$|<br>| + 1s/^\S\+\s\+CREATE TABLE `todos` /CREATE TEMPORARY TABLE `td` / ' - printf '%s\n' \ - '</body>' \ - '</html>' - } > \ - "${tmp_dir}/todos.html" - - { - printf '%s\n' \ - '<html>' \ - '<head>' \ - '<title>Blacklisted packages</title>' \ - '<link rel="stylesheet" type="text/css" href="/static/style.css">' \ - '</head>' \ - '<body>' \ - '<table>' - printf '<tr>' - printf '<th>%s</th>' \ - 'package' \ - 'reason' - printf '</tr>\n' - git -C "${repo_paths__archlinux32}" archive "$(cat "${work_dir}/archlinux32.revision")" -- 'blacklist' | \ - tar -Ox | \ + printf ';\n' + printf 'INSERT INTO `td` (`file`,`line`,`description`) VALUES ' + while read -r file line desc; do + printf '(' + printf 'from_base64("%s"),' \ + "${file}" \ + "${line}" \ + "${desc}" | \ + sed 's/,$/),/' + done < \ + "${tmp_dir}/todos" | \ sed ' - s@FS#\([0-9]\+\)@<a href="https://bugs.archlinux.org/task/\1">\0</a>@ - s@FS32#\([0-9]\+\)@<a href="https://bugs.archlinux32.org/index.php?do=details\&task_id=\1">\0</a>@ - /.#/!s/$/#/ - s|\(.\)#|\1</td><td>| - /^\s*#/{ - s/^\s*#\s*// - s|\s*\(</td><td>\)|</font></s>\1| - s/^/<s><font color="#808080">/ - } - s|^|<tr><td>| - s|$|</td></tr>| + s/,$// ' - printf '%s\n' \ - '</table>' \ - '</body>' \ - '</html>' - } > \ - "${tmp_dir}/blacklist.html" - - { - printf '%s\n' \ - '<html>' \ - '<head>' \ - '<title>log of ssh connections from build slaves</title>' \ - '</head>' \ - '<body>' \ - '<table>' - printf '<tr>' - printf '<th>%s</th>' \ - 'time' \ - 'build slave' \ - 'command' \ - 'arguments' - printf '</tr>\n' - if [ -r "${work_dir}/ssh-log" ]; then - tac "${work_dir}/ssh-log" | \ - while read -r date time slave command arguments; do - printf '<tr>' - printf '<td>%s</td>' \ - "${date} ${time}" \ - "${slave}" \ - "${command}" \ - "${arguments}" - printf '</tr>\n' - done - fi - printf '%s\n' \ - '</table>' \ - '</body>' \ - '</html>' - } > \ - "${tmp_dir}/ssh-log.html" + printf ';\n' + printf 'INSERT IGNORE INTO `todos` (`file`,`line`,`description`) ' + printf 'SELECT `td`.`file`,`td`.`line`,`td`.`description` ' + printf 'FROM `td` ' + printf 'WHERE NOT EXISTS (' + printf 'SELECT * FROM `todos`' + printf ' AND `td`.`%s`=`todos`.`%s`' \ + 'file' 'file' \ + 'line' 'line' \ + 'description' 'description' | \ + sed 's/^ AND / WHERE /' + printf ');\n' + + printf 'DELETE FROM `todos` WHERE NOT EXISTS (' + printf 'SELECT * FROM `td`' + printf ' AND `td`.`%s`=`todos`.`%s`' \ + 'file' 'file' \ + 'line' 'line' \ + 'description' 'description' | \ + sed 's/^ AND / WHERE /' + printf ');' + printf 'DROP TABLE `td`;\n' + printf 'DELETE FROM `todo_links` WHERE NOT EXISTS (' + printf 'SELECT * FROM `todos` ' + printf 'WHERE `todos`.`id`=`todo_links`.`depending_on`' + printf ') OR NOT EXISTS (' + printf 'SELECT * FROM `todos` ' + printf 'WHERE `todos`.`id`=`todo_links`.`dependent`' + printf ');\n' + } | \ + mysql_run_query + rm -f "${tmp_dir}/todos" +fi - find "${tmp_dir}" -maxdepth 1 -type f | \ - while read -r file; do - cat "${file}" > \ - "${webserver_directory}/${file##*/}" - done +{ + printf '%s\n' \ + '<html>' \ + '<head>' \ + '<title>log of ssh connections from build slaves</title>' \ + '</head>' \ + '<body>' \ + '<table>' + printf '<tr>' + printf '<th>%s</th>' \ + 'time' \ + 'build slave' \ + 'command' \ + 'arguments' + printf '</tr>\n' + if [ -r "${work_dir}/ssh-log" ]; then + tac "${work_dir}/ssh-log" | \ + while read -r date time slave command arguments; do + printf '<tr>' + printf '<td>%s</td>' \ + "${date} ${time}" \ + "${slave}" \ + "${command}" \ + "${arguments}" + printf '</tr>\n' + done + fi + printf '%s\n' \ + '</table>' \ + '</body>' \ + '</html>' +} > \ + "${tmp_dir}/ssh-log.html" -else - cat "${tmp_dir}/build-master-status.html" -fi +find "${tmp_dir}" -maxdepth 1 -type f | \ + while read -r file; do + cat "${file}" > \ + "${webserver_directory}/${file##*/}" + done diff --git a/bin/build-master-status-from-mysql b/bin/build-master-status-from-mysql index a0c2b4f..962b12e 100755 --- a/bin/build-master-status-from-mysql +++ b/bin/build-master-status-from-mysql @@ -5,30 +5,23 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" +# TODO: add all (necessary) features from build-master-status + +tmp_dir=$(mktemp -d 'tmp.build-master-status-from-mysql.XXXXXXXXXX' --tmpdir) +trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT + +# do not block if locked +exec 9> "${sanity_check_lock_file}" +if ! flock -n 9; then + >&2 echo 'Mysql-Sanity check skipped, cannot acquire lock.' + exit +fi + +# shellcheck disable=SC2119 +mysql_cleanup + { mysql_sanity_check || true - { - # shellcheck disable=SC2016 - { - printf 'SELECT `package_sources`.`pkgbase`,`package_sources`.`git_revision`,`package_sources`.`mod_git_revision`,`upstream_repositories`.`name`' - printf ' FROM `package_sources`' - printf ' JOIN `%s` ON `%s`.`%s`=`%s`.`id`' \ - 'upstream_repositories' 'package_sources' 'upstream_package_repository' 'upstream_repositories' \ - 'build_assignments' 'build_assignments' 'package_source' 'package_sources' \ - 'binary_packages' 'binary_packages' 'build_assignment' 'build_assignments' \ - 'repositories' 'binary_packages' 'repository' 'repositories' - printf ' WHERE `repositories`.`name`="build-list"' - } | \ - ${mysql_command} --batch | \ - sed ' - 1d - y/\t/ / - s/^/+ / - ' - sed 's/^/- /' "${work_dir}/build-list" - } | \ - sort -k2 -k1,1 | \ - uniq -uf1 } | \ sed ' s,^-.*$,<font color="#FF0000">\0</font>, @@ -38,3 +31,118 @@ $ a </body></html> ' | \ sponge "${webserver_directory}/mysql-sanity.html" + +if [ -s "${webserver_directory}/mysql-sanity.html" ] && \ + [ ! -s "${work_dir}/build-master-sanity" ]; then + printf 'girls, my database is dirty again ...\n' | \ + irc_say + echo 'build master is insane' > \ + "${work_dir}/build-master-sanity" +fi + +if [ ! -s "${work_dir}/build-master-sanity" ]; then + # shellcheck disable=SC2016 + { + printf 'INSERT IGNORE INTO `statistics` (' + printf '`%s`,' \ + 'date' \ + 'stable_packages_count' \ + 'pending_tasks_count' \ + 'pending_packages_count' \ + 'staging_packages_count' \ + 'testing_packages_count' \ + 'tested_packages_count' \ + 'broken_tasks_count' \ + 'dependency_loops_count' \ + 'dependency_looped_tasks_count' \ + 'locked_tasks_count' \ + 'blocked_tasks_count' \ + 'next_tasks_count' | \ + sed 's/,$//' + printf ') VALUES (' + # date + printf 'NOW(),' + # stable_packages_count + printf '(SELECT COUNT(DISTINCT `binary_packages`.`id`) FROM' + printf ' `binary_packages`' + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + printf ' WHERE `repository_stabilities`.`name`="stable"),' + # pending_tasks_count + printf '(SELECT COUNT(DISTINCT `build_assignments`.`id`) FROM' + printf ' `build_assignments`' + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="build-list"),' + # pending_packages_count + printf '(SELECT COUNT(DISTINCT `binary_packages`.`id`) FROM' + printf ' `binary_packages`' + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="build-list"),' + # staging_packages_count + printf '(SELECT COUNT(DISTINCT `binary_packages`.`id`) FROM' + printf ' `binary_packages`' + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + printf ' WHERE `repository_stabilities`.`name`="staging"),' + # testing_packages_count + printf '(SELECT COUNT(DISTINCT `binary_packages`.`id`) FROM' + printf ' `binary_packages`' + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + printf ' WHERE `repository_stabilities`.`name`="testing"' + printf ' AND NOT `binary_packages`.`is_tested`),' + # tested_packages_count + printf '(SELECT COUNT(DISTINCT `binary_packages`.`id`) FROM' + printf ' `binary_packages`' + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + printf ' WHERE `repository_stabilities`.`name`="testing"' + printf ' AND `binary_packages`.`is_tested`),' + # broken_tasks_count + printf '(SELECT COUNT(DISTINCT `build_assignments`.`id`) FROM' + printf ' `build_assignments`' + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="build-list"' + printf ' AND `build_assignments`.`is_broken`),' + # dependency_loops_count + printf '(SELECT COUNT(DISTINCT `build_dependency_loops`.`loop`) FROM' + printf ' `build_dependency_loops`),' + # dependency_looped_tasks_count + printf '(SELECT COUNT(DISTINCT `build_dependency_loops`.`build_assignment`) FROM' + printf ' `build_dependency_loops`),' + # locked_tasks_count + printf '(SELECT COUNT(DISTINCT `build_slaves`.`currently_building`) FROM' + printf ' `build_slaves`' + mysql_join_build_slaves_build_assignments + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="build-list"),' + # blocked_tasks_count + printf '(SELECT COUNT(DISTINCT `build_assignments`.`id`) FROM' + printf ' `build_assignments`' + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="build-list"' + printf ' AND `build_assignments`.`is_blocked` IS NOT NULL),' + # next_tasks_count + printf '(SELECT COUNT(DISTINCT `build_assignments`.`id`) FROM' + printf ' `build_assignments`' + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="build-list"' + printf ' AND NOT EXISTS (' + printf 'SELECT * FROM `dependencies`' + mysql_join_dependencies_dependency_types + printf ' AND `dependency_types`.`relevant_for_binary_packages`' + mysql_join_dependencies_install_target_providers + mysql_join_install_target_providers_binary_packages '' 'prov_bp' + mysql_join_binary_packages_repositories 'prov_bp' 'prov_r' + printf ' WHERE `prov_r`.`name`="build-list"' + printf ' AND `dependencies`.`dependent`=`binary_packages`.`id`' + printf '))' + printf ');\n' + } | \ + mysql_run_query +fi diff --git a/bin/build-packages b/bin/build-packages index 81afa7f..02334e9 100755 --- a/bin/build-packages +++ b/bin/build-packages @@ -7,8 +7,7 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" -# TODO: report back in regular intervals, so the build master can -# abort if the package is outdated and/or someone else finished it. +# TODO: build other 'architectures', too (pentium4, i486) # shellcheck disable=SC2016 usage() { @@ -220,6 +219,11 @@ while [ "${count}" -ne 0 ]; do if [ "${mod_git_revision}" = 'work-tree' ]; then mod_git_revision=$( # we can't just create an empty index-file with mktemp, because git doesn't like it + find . \ + -mindepth 1 \ + -maxdepth 1 \ + -name 'tmp.build-packages.git.*' \ + -exec rm -rf --one-file-system {} \; tmp_subdir=$(mktemp -d 'tmp.build-packages.git.XXXXXXXXXX' --tmpdir) trap 'rm -rf --one-file-system "${tmp_subdir}"' EXIT export GIT_INDEX_FILE="${tmp_subdir}/index.new" @@ -251,8 +255,18 @@ while [ "${count}" -ne 0 ]; do fi cd "${base_dir}" recursively_umount_and_rm "${tmp_dir}" + flock -u 9 || true exit "${err}" } + find "${work_dir}" \ + -mindepth 1 \ + -maxdepth 1 \ + -name 'tmp.build-packages.??????' \ + -printf '%p\n' | \ + while read -r old_tmp_dir; do + find "${old_tmp_dir}" -xdev -exec chmod 777 {} \; + rm -rf --one-file-system "${old_tmp_dir}" + done tmp_dir=$(mktemp -d "${work_dir}/tmp.build-packages.XXXXXX") trap bail_out EXIT @@ -295,7 +309,19 @@ while [ "${count}" -ne 0 ]; do # we can't improve anything continue fi - tar -xz --overwrite -f "${source_name}" --exclude PKGBUILD --strip-components=1 || true + # shellcheck disable=SC2046 + tar -xz --overwrite \ + -f "${source_name}" \ + --exclude PKGBUILD \ + $( + if [ -n "${PKGBUILD_mod}" ]; then + git -C "${repo_paths__archlinux32}/${PKGBUILD_mod%/*}" archive "${mod_git_revision}" -- . | \ + tar -t | \ + sed 's/^/--exclude /' + fi + ) \ + --strip-components=1 \ + || true fi if echo "${straw}" | \ @@ -321,19 +347,39 @@ while [ "${count}" -ne 0 ]; do if echo "${straw}" | \ grep -qF ':clean_chroot:'; then - parameters='-c' + outerParameters='-c' else - parameters='' + outerParameters='' fi if echo "${straw}" | \ grep -qF ':without_check:'; then - parameters="${parameters} -- -- --nocheck" + innerParameters='--nocheck' + else + innerParameters='' + fi + + if echo "${straw}" | \ + grep -qF ':with_/dev/fuse:'; then + middleParameters='-d /dev/fuse' + else + middleParameters='' fi find . -maxdepth 1 -type f \( -name '*.pkg.tar.xz' -o -name '*.pkg.tar.xz.sig' \) -exec \ rm {} \; + echo 'building' > "${tmp_dir}/.ping-build-master" + if [ -z "${forced_package}" ]; then + # we get a lock on "${work_dir}/ping-build-master.lock", + # if we release that lock, ping-to-master should stop _immediately_ + exec 9> "${work_dir}/ping-build-master.lock" + if ! flock -n 9; then + >&2 echo 'ERROR: Cannot lock ping-to-master - this should not happen.' + exit 2 + fi + "${base_dir}/bin/ping-to-master" "$$" "${tmp_dir}" & + fi >&2 printf '%s: building package "%s" (revisions %s %s, repository %s, straw %s) ...' \ "$(date +'%Y-%m-%d %T')" \ "${package}" \ @@ -344,7 +390,7 @@ while [ "${count}" -ne 0 ]; do # by piping the log, we don't see anything in the terminal, # but all ways to duplicate the logs seem pretty elaborate # shellcheck disable=SC2024,SC2086 - if sudo "${build_command}" ${parameters} > \ + if sudo "${build_command}" ${outerParameters} -- ${middleParameters} -- ${innerParameters} > \ "$( date -u --iso-8601=seconds | \ cut -d+ -f1 @@ -353,6 +399,7 @@ while [ "${count}" -ne 0 ]; do >&2 printf ' ok.\n' tar_content_dir=$(mktemp -d "${tmp_dir}/tar-content.XXXXXX") find . -maxdepth 1 -type f -name '*-debug-*.pkg.tar.xz*' -delete + echo 'post-build' > "${tmp_dir}/.ping-build-master" >&2 printf 'signing package(s)\n' find . -maxdepth 1 -type f -name '*.pkg.tar.xz' \ -execdir gpg --local-user="${package_key}" --detach-sign '{}' \; \ @@ -390,12 +437,50 @@ while [ "${count}" -ne 0 ]; do >&2 printf ' failed. Next ...\n' done done + >&2 printf 'searching for provided libraries\n' + find "${tar_content_dir}" -maxdepth 1 \ + -name '*.pkg.tar.xz' \ + -printf '%p\n' | \ + while read -r pkgfile; do + pacman -Qqlp "${pkgfile}" | \ + sed -n ' + s,^.*/,, + /\.so\(\..\+\)\?$/p + ' > \ + "${pkgfile}.so.provides" + done + >&2 printf 'searching for required libraries\n' + package_content_dir=$(mktemp -d "${tmp_dir}/package-content.XXXXXX") + find "${tar_content_dir}" -maxdepth 1 \ + -name '*.pkg.tar.xz' | \ + while read -r pkgfile; do + if printf '%s\n' "${pkgfile}" | \ + grep -vq -- '-any\.pkg\.tar\.xz$'; then + # we do not check "any" packages for linked libraries + # (why do they have them in the first place?) + mkdir "${package_content_dir}/${pkgfile##*/}" + tar -C "${package_content_dir}/${pkgfile##*/}" -xJf "${pkgfile}" 2>/dev/null + find "${package_content_dir}/${pkgfile##*/}" \ + -name 'opt' -prune , \ + -exec objdump -x '{}' \; 2>/dev/null | \ + grep -w 'NEEDED' | \ + awk '{print $2}' | \ + sed ' + /\.c32$/d + s,^.*/,, + ' + find "${package_content_dir:?}/${pkgfile##*/}" -xdev -exec chmod 777 '{}' \; + rm -rf --one-file-system "${package_content_dir:?}/${pkgfile##*/}" + fi | \ + sort -u > \ + "${pkgfile}.so.needs" + done + >&2 printf 'running namcap ...' if [ "${repository}" = 'multilib' ]; then x86_64_build_command='multilib-build' else x86_64_build_command='extra-x86_64-build' fi - >&2 printf 'running namcap ...' # this is a little hack: makepkg receives '--version', but namcap is run nevertheless # (and it only works with devtools32, because they are running namcap on *.pkg.tar.xz in the base directory, too) sudo "${x86_64_build_command}" -- -- --version > /dev/null 2>&1 || \ @@ -426,14 +511,31 @@ while [ "${count}" -ne 0 ]; do fi done >&2 printf ' ok.\n' + echo 'uploading' > "${tmp_dir}/.ping-build-master" if ${upload_to_build_master}; then - find "${tar_content_dir}/" -maxdepth 1 -name '*.pkg.tar.xz-namcap.log' -execdir gzip '{}' \; + find "${tar_content_dir}/" -maxdepth 1 \ + \( \ + -name '*.pkg.tar.xz-namcap.log' -o \ + -name '*.pkg.tar.xz.so.needs' -o \ + -name '*.pkg.tar.xz.so.provides' \ + \) \ + -execdir gzip '{}' \; else - find "${tar_content_dir}/" -maxdepth 1 -name '*.pkg.tar.xz-namcap.log' -execdir grep -HF '' '{}' \; + find "${tar_content_dir}/" -maxdepth 1 \ + -name '*.pkg.tar.xz-namcap.log' \ + -execdir grep -HF '' '{}' \; fi # shellcheck disable=SC2046 tar -cf 'package.tar' -C "${tar_content_dir}" -- $( - find "${tar_content_dir}/" -maxdepth 1 \( -name '*.pkg.tar.xz' -o -name '*.pkg.tar.xz.sig' -o -name '*.pkg.tar.xz-namcap.log.gz' \) -printf '%f\n' + find "${tar_content_dir}/" -maxdepth 1 \ + \( \ + -name '*.pkg.tar.xz' -o \ + -name '*.pkg.tar.xz.sig' -o \ + -name '*.pkg.tar.xz-namcap.log.gz' -o \ + -name '*.pkg.tar.xz.so.needs.gz' -o \ + -name '*.pkg.tar.xz.so.provides.gz' \ + \) \ + -printf '%f\n' ) while ${upload_to_build_master}; do err=0 @@ -474,6 +576,7 @@ while [ "${count}" -ne 0 ]; do success=true break fi + echo 'failure' > "${tmp_dir}/.ping-build-master" >&2 printf ' failed.\n' done @@ -528,6 +631,7 @@ while [ "${count}" -ne 0 ]; do # clean up tmp_dir cd "${base_dir}" recursively_umount_and_rm "${tmp_dir}" + flock -u 9 || true trap - EXIT continue diff --git a/bin/calculate-dependent-packages b/bin/calculate-dependent-packages deleted file mode 100755 index bfb87a1..0000000 --- a/bin/calculate-dependent-packages +++ /dev/null @@ -1,184 +0,0 @@ -#!/bin/sh - -# shellcheck source=conf/default.conf -. "${0%/*}/../conf/default.conf" - -if [ -s "${work_dir}/build-master-sanity" ]; then - >&2 echo 'Build master is not sane.' - exit -fi - -tmp_dir=$(mktemp -d 'tmp.calculate-dependent-packages.XXXXXXXXXX' --tmpdir) -trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT - -sort -k1,1 -u "${work_dir}/build-list" > \ - "${tmp_dir}/build-list" - -while read -r pkg rev mod_rev repo; do - generate_package_metadata "${pkg}" "${rev}" "${mod_rev}" "${repo}" -done < \ - "${tmp_dir}/build-list" - -mkdir "${tmp_dir}/loops" -find "${work_dir}/build-list.loops" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' | \ - while read -r loop; do - sort -u "${loop}" | \ - join -j 1 - "${tmp_dir}/build-list" | \ - tr ' ' '.' > \ - "${tmp_dir}/loops/${loop##*/}" - done - -touch "${tmp_dir}/dependent-count" - -tr ' ' '.' < \ - "${tmp_dir}/build-list" | \ - sponge "${tmp_dir}/build-list" - -sums='' - -while [ -s "${tmp_dir}/build-list" ] && [ "${sums}" != "$(sha512sum "${tmp_dir}/dependent-count")" ]; do - - sums=$( - sha512sum "${tmp_dir}/dependent-count" - ) - - { - sed 's|^|? |' "${tmp_dir}/build-list" - sed 'p' "${tmp_dir}/dependent-count" - } | \ - sort -k2,2 | \ - uniq -uf1 | \ - cut -d' ' -f2 | \ - sponge "${tmp_dir}/build-list" - - sed ' - s|^|'"${work_dir}"'/package-infos/| - s|$|.build-depends| - ' "${tmp_dir}/build-list" | \ - xargs -r grep -hvx 'base\|base-devel' | \ - sort -u > \ - "${tmp_dir}/build-list.build-depends" - - sed ' - s|^\S\+ |'"${work_dir}"'/package-infos/| - s|$|.build-depends| - ' "${tmp_dir}/dependent-count" | \ - xargs -r grep -Hvx 'base\|base-devel' | \ - sed ' - s|^.*/|| - s|\.build-depends:| | - ' | \ - sort -k2,2 > \ - "${tmp_dir}/dependent-count.build-depends" - - rm -f "${tmp_dir}/dependent-count.new" - touch "${tmp_dir}/dependent-count.new" - - while read -r sf; do - if [ -n "$( - { - sort -u "${work_dir}/package-infos/${sf}.builds" - cat "${tmp_dir}/build-list.build-depends" - } | \ - sort | \ - uniq -d - )" ]; then - continue - fi - - count="/$( - sort -u "${work_dir}/package-infos/${sf}.builds" | \ - join -1 1 -2 2 -o 2.1 - "${tmp_dir}/dependent-count.build-depends" | \ - sort -u | \ - join -1 1 -2 2 -o 2.1,2.2 - "${tmp_dir}/dependent-count" | \ - tr '/ ' '\n' | \ - grep -vxF '' | \ - sort -u | \ - tr '\n' '/' - )" - printf '%s %s\n' \ - "${count}" \ - "${sf}" >> \ - "${tmp_dir}/dependent-count.new" - find "${tmp_dir}/loops" -type f -maxdepth 1 \ - -exec grep -qxF "${sf}" {} \; \ - -exec rm {} \; - done < \ - "${tmp_dir}/build-list" - - find "${tmp_dir}/loops" -maxdepth 1 | \ - while read -r loop; do - if [ ! -f "${loop}" ]; then - continue - fi - if [ -n "$( - { - sed ' - s|^|'"${work_dir}"'/package-infos/| - s|$|.builds| - ' "${loop}" | \ - xargs -r cat | \ - sort -u - { - sed 'p' "${loop}" - cat "${tmp_dir}/build-list" - } | \ - sort | \ - uniq -u | \ - sed ' - s|^|'"${work_dir}"'/package-infos/| - s|$|.build-depends| - ' | \ - xargs -r grep -hvx 'base\|base-devel' | \ - sort -u - } | \ - sort | \ - uniq -d - )" ]; then - continue - fi - - count="/$( - sed ' - s|^|'"${work_dir}"'/package-infos/| - s|$|.builds| - ' "${loop}" | \ - xargs -r cat | \ - sort -u | \ - join -1 1 -2 2 -o 2.1 - "${tmp_dir}/dependent-count.build-depends" | \ - sort -u | \ - join -1 1 -2 2 -o 2.1,2.2 - "${tmp_dir}/dependent-count" | \ - tr '/ ' '\n' | \ - grep -vxF '' | \ - sort -u | \ - tr '\n' '/' - )" - while read -r sf; do - printf '%s %s\n' "${count}" "${sf}" >> \ - "${tmp_dir}/dependent-count.new" - find "${tmp_dir}/loops" -maxdepth 1 -type f -not -name "${loop##*/}" \ - -exec grep -qxF "${sf}" {} \; \ - -exec rm {} \; - done < \ - "${loop}" - - rm "${loop}" - done - - cat "${tmp_dir}/dependent-count" "${tmp_dir}/dependent-count.new" | \ - sort -k2,2 | \ - sponge "${tmp_dir}/dependent-count" - -done - -while read -r count pkg; do - count=$( - echo "${count}" | \ - tr '/' '\n' | \ - grep -cvxF '' - ) || true - printf '%s %s\n' "${pkg}" "${count}" -done < \ - "${tmp_dir}/dependent-count" | \ - sponge "${work_dir}/dependent-count" diff --git a/bin/check-bugtracker b/bin/check-bugtracker index 46e78db..5bd05b4 100755 --- a/bin/check-bugtracker +++ b/bin/check-bugtracker @@ -1,33 +1,64 @@ #!/bin/sh # check the bug tracker for packages in testing and community-testing -# with issues and mark these packages as "testing" if they are currently -# marked as "tested" +# with issues and mark these packages as "has_issues" if they are faulty +# and vice versa # shellcheck disable=SC2039 # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" -tmp_dir=$(mktemp -d 'tmp.check-bugtracker.XXXXXXXXXX' --tmpdir) -trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT +bug_list=$( + curl -LSs 'https://bugs.archlinux32.org/index.php?export_list=Export%20Tasklist' | \ + sed -n ' + 1d + s/^[^,]\+,"// + T + s/^\([^"]\+\)"\(,[^,]\+\)\{2\},"\([^"]\+\)".*$/"\1" "\3"/ + T + p + ' +) -receive_buglist 'Testing' | \ - tr ' ,;' '\n' | \ - sed -n ' - s/^\[// - T - s/]$// - T - p - ' | \ - sort -u > \ - "${tmp_dir}/faulty-packages" - -find "${work_dir}/package-states" -name '*.tested' -printf '%f\n' | \ - sed ' - s|\.[^.]\+$|| - s|^\(.*\)\(\.[^.]\+\)\{3\}$|\1 \0| - ' | \ - sort -k1,1 | \ - join -1 1 -2 1 -o 1.2 - "${tmp_dir}/faulty-packages" | \ - "${base_dir}/bin/modify-package-state" -n --faulty /dev/stdin +# shellcheck disable=SC2016 +{ + printf 'SELECT `repository_stabilities`.`id`,`repository_stabilities`.`bugtracker_category`' + printf ' FROM `repository_stabilities`' + printf ' WHERE NOT `repository_stabilities`.`bugtracker_category` IS NULL' +} | \ + mysql_run_query | \ + while read -r stability_id category; do + for has_issues in '1:' '0:NOT '; do + printf 'UPDATE `binary_packages`' + mysql_join_binary_packages_repositories + printf ' SET `has_issues`=%s' \ + "${has_issues%:*}" + printf ' WHERE `repositories`.`stability`=%s' \ + "${stability_id}" + printf ' AND `binary_packages`.`pkgname` %sIN (' \ + "${has_issues#*:}" + printf '%s\n' "${bug_list}" | \ + sed -n ' + s/^"'"$(str_to_regex "${category}")"'" // + T + :a + /\[.*]/ { + s/^[^[]*\[// + T + h + s/].*$// + p + x + ba + } + ' | \ + base64_encode_each | \ + sed ' + s/^/from_base64("/ + s/$/")/ + ' | \ + tr '\n' ',' + printf '"");\n' + done + done | \ + mysql_run_query diff --git a/bin/check-opcodes b/bin/check-opcodes index c7ab4a9..bd96cc6 100755 --- a/bin/check-opcodes +++ b/bin/check-opcodes @@ -101,7 +101,7 @@ log "Checking for architecture: $ARCH ($OPCODE_ARGS)" bsdtar --no-fflags -x -C $tmp_dir -f $PACKAGE # shellcheck disable=SC2044 -for absfile in $(find $tmp_dir -name '*.so*' -type f); do +for absfile in $(find $tmp_dir -regextype grep -regex '.*\.so\(\.[0-9.]\+\)\?' -type f); do file=$(basename $absfile) log "Checking shared library: $file" readelf -a $absfile > $tmp_dir/$file.elf diff --git a/bin/cleanup b/bin/cleanup index 78daece..c9a8d2b 100755 --- a/bin/cleanup +++ b/bin/cleanup @@ -5,6 +5,8 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" +# TODO: clean database, too + # we only clean if run interactive or if no one is logged in if ! tty -s && \ [ -n "$(users)" ]; then @@ -17,54 +19,24 @@ if [ -s "${work_dir}/build-master-sanity" ]; then exit fi -# remove blocked/broken/locked markes of packages not on the buildlist anymore - -{ - find "${work_dir}/package-states" -maxdepth 1 \( -name '*.broken' -o -name '*.locked' -o -name '*.blocked' \) -printf '%f\n' | \ - sed ' - s|^\(.*\)\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)$|state \0 \1 \2 \3 \4 \5| - ' - awk '{ - print "order " $1 "." $2 "." $3 "." $4 " " $1 " " $2 " " $3 " " $4 " broken" - print "order " $1 "." $2 "." $3 "." $4 " " $1 " " $2 " " $3 " " $4 " blocked" - print "order " $1 "." $2 "." $3 "." $4 " " $1 " " $2 " " $3 " " $4 " locked" - }' "${work_dir}/build-list" -} | \ - sort -k3 | \ - uniq -uf2 | \ - grep '^state ' | \ - awk '{print $2}' | \ - sed "s|^|${work_dir}/package-states/|" | \ - xargs -rn1 rm - # remove logs where package is not broken/locked anymore { - find "${build_log_directory}/error" -maxdepth 1 -type f -printf '1 %f %f\n' | \ - sed 's|\.[^. ]\+\.build-log\.gz$||' - find "${work_dir}/package-states" -maxdepth 1 \( \ - -name '*.broken' -o \ - -name '*.done' -o \ - -name '*.locked' -o \ - -name '*.testing' \ - \) -printf '0 0 %f\n' | \ - sed 's|\.[^.]\+$||' + find "${build_log_directory}/error" -maxdepth 1 -type f -printf 'file %f\n' + # shellcheck disable=SC2016 + printf 'SELECT "mysql",`failed_builds`.`log_file` FROM `failed_builds`;\n' | \ + mysql_run_query | \ + tr '\t' ' ' } | \ - sort -k3,3 -k1,2 | \ - uniq --group=prepend -f2 | \ - while read -r num file _; do - if [ "${num}" = '0' ]; then - while read -r line; do - if [ -z "${line}" ]; then - break - fi - done - continue - fi - if [ -z "${num}${file}" ]; then - continue - fi - rm -f "${build_log_directory}/error/${file}" + sort -k2,2 -k1,1 | \ + uniq -uf 1 | \ + sed -n ' + s/^file // + T + p + ' | \ + while read -r file; do + rm "${build_log_directory}/error/${file}" done # only keep 10 newest logs per failed package @@ -97,14 +69,4 @@ find "${build_log_directory}/success" -maxdepth 1 -type f -mtime +14 \ -not -exec zgrep -q '^+.*ELF file .* has text relocations' '{}' \; \ -delete -# remove old package meta data -delete_old_metadata - -# remove dependency graphs of packages on the deletion list -sed ' - s|^|'"${webserver_directory}"'/graphs/| - s|$|.png| -' "${work_dir}/deletion-list" | \ - xargs -rn1 rm -f - exit 0 diff --git a/bin/copy-to-build-support b/bin/copy-to-build-support new file mode 100755 index 0000000..404a2b9 --- /dev/null +++ b/bin/copy-to-build-support @@ -0,0 +1,187 @@ +#!/bin/sh + +# copy the given package(s) into build-support + +# shellcheck source=conf/default.conf +. "${0%/*}/../conf/default.conf" + +# shellcheck disable=SC2016 +usage() { + >&2 echo '' + >&2 echo 'copy-to-build-support [options] package-list:' + >&2 echo ' copy the packages listed in package-list into [build-support]' + >&2 echo '' + >&2 echo 'possible options:' + >&2 echo ' -h|--help:' + >&2 echo ' Show this help and exit.' + >&2 echo ' -w|--wait:' + >&2 echo ' Wait for lock if necessary.' + [ -z "$1" ] && exit 1 || exit "$1" +} + +eval set -- "$( + getopt -o hw \ + --long help \ + --long wait \ + -n "$(basename "$0")" -- "$@" || \ + echo usage + )" + +wait_for_lock='-n' + +while true +do + case "$1" in + -h|--help) + usage 0 + ;; + -w|--wait) + wait_for_lock='' + ;; + --) + shift + break + ;; + *) + >&2 echo 'Whoops, forgot to implement option "'"$1"'" internally.' + exit 42 + ;; + esac + shift +done + +if [ "$#" -ne 1 ]; then + >&2 echo 'No package-list was given.' + usage +fi + +exec 9> "${sanity_check_lock_file}" +flock -s ${wait_for_lock} 9 + +exec 8> "${package_database_lock_file}" +flock ${wait_for_lock} 8 + +tmp_dir=$(mktemp -d "${work_dir}/tmp.copy-to-build-support.0.XXXXXXXXXX") +trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT + +to_copy='build_assignment epoch pkgver pkgrel sub_pkgrel has_issues is_tested pkgname architecture' +sed -n ' + s/.\+/\0 \0/ + T + s/\.pkg\.tar\.xz$// + s/\(-[0-9]\+\)\(-[^- ]\+\)$/\1.0\2/ + s/-\([^-: ]\+\)\(\(-[^- ]\+\)\{2\}\)$/-0:\1\2/ + s/-\([^-: ]\+\):\([^-: ]\+\)-\([^-. ]\+\).\([^-. ]\+\)-\([^- ]\+\)$/ \1 \2 \3 \4 \5/ + p +' "$1" | \ + while read -r package pkgname epoch pkgver pkgrel sub_pkgrel architecture; do + + # shellcheck disable=SC2016 + id=$( + { + printf 'SELECT `binary_packages`.`id`,`repositories`.`name`' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_repositories + mysql_join_binary_packages_architectures + printf ' WHERE' + printf ' `binary_packages`.`%s`=from_base64("%s") AND' \ + 'epoch' "$(printf '%s' "${epoch}" | base64 -w0)" \ + 'pkgver' "$(printf '%s' "${pkgver}" | base64 -w0)" \ + 'pkgrel' "$(printf '%s' "${pkgrel}" | base64 -w0)" \ + 'sub_pkgrel' "$(printf '%s' "${sub_pkgrel}" | base64 -w0)" \ + 'pkgname' "$(printf '%s' "${pkgname}" | base64 -w0)" + printf ' `architectures`.`name`=from_base64("%s")' \ + "$(printf '%s' "${architecture}" | base64 -w0)" + printf ' LIMIT 1;\n' + } | \ + mysql_run_query | \ + tr '\t' ' ' + ) + if [ -z "${id}" ]; then + continue + fi + repository="${id#* }" + id="${id%% *}" + + printf '%s\n' "${package}" >> \ + "${tmp_dir}/packages" + for suffix in '' '.sig'; do + printf 'ln "i686/%s/%s%s" "i686/build-support/%s%s"\n' \ + "${repository}" \ + "${package}" \ + "${suffix}" \ + "${package}" \ + "${suffix}" + done >> \ + "${tmp_dir}/sftp-command" + printf '%s/i686/%s/%s\n' \ + "${master_mirror_rsync_directory}" \ + "${repository}" \ + "${package}" | \ + sed ' + p + s/$/.sig/ + ' >> \ + "${tmp_dir}/to-copy" + + # shellcheck disable=SC2016 + { + printf 'INSERT IGNORE INTO `binary_packages`' + printf ' (`repository`' + # shellcheck disable=SC2086 + printf ',`%s`' ${to_copy} + printf ')' + printf ' SELECT' + printf ' (SELECT `repositories`.`id` FROM `repositories` WHERE `repositories`.`name`="build-support")' + # shellcheck disable=SC2086 + printf ',`binary_packages`.`%s`' ${to_copy} + printf ' FROM `binary_packages`' + mysql_join_binary_packages_architectures + printf ' WHERE' + printf ' `binary_packages`.`id`=%s;\n' \ + "${id}" + } >> \ + "${tmp_dir}/mysql-command" + done + +${master_mirror_rsync_command} \ + "${master_mirror_rsync_directory}/i686/build-support/build-support.db."* \ + "${master_mirror_rsync_directory}/i686/build-support/build-support.files."* \ + "${tmp_dir}/" + +if [ -s "${tmp_dir}/to-copy" ]; then + mkdir "${tmp_dir}/transit/" + # shellcheck disable=SC2046 + ${master_mirror_rsync_command} \ + $(cat "${tmp_dir}/to-copy") \ + "${tmp_dir}/transit/" + repo-add "${tmp_dir}/build-support.db.tar.gz" \ + "${tmp_dir}/transit/"*".pkg.tar.xz" +fi + +if [ -s "${tmp_dir}/sftp-command" ]; then + ${master_mirror_sftp_command} < \ + "${tmp_dir}/sftp-command" +fi + +${master_mirror_rsync_command} \ + "${tmp_dir}/build-support.db."* \ + "${tmp_dir}/build-support.files."* \ + "${master_mirror_rsync_directory}/i686/build-support/" + +if [ -s "${tmp_dir}/mysql-command" ]; then + mysql_run_query < \ + "${tmp_dir}/mysql-command" +fi + +if [ -s "${tmp_dir}/packages" ]; then + while read -r package; do + remove_old_package_versions 'i686' 'build-support' "${package}" + done < \ + "${tmp_dir}/packages" +fi + +if [ -w "$1" ]; then + cat "${tmp_dir}/packages" > \ + "$1" +fi diff --git a/bin/db-update b/bin/db-update index fe45f41..6722f24 100755 --- a/bin/db-update +++ b/bin/db-update @@ -4,27 +4,13 @@ # additionally tested packages from testing to the respective stable # repository (if possible [1]) -# 1] Condition for moving a package A from staging (testing) to -# testing (stable) is that: -# a) nothing on the build-list run-depends on A and -# b) no done package B (in a not-more stable repository) which is -# not being moved run-depends on A +# 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: handle deletion of parts of a split package - -# TODO: handle dependencies of parts of a split package separately - -# TODO: improve tracking of packages: -# track packages in stable, too -# know _exactly_ in which repository the packages are - -# TODO: fully integrate database (in other scripts too) - e.g. rely on -# info in db instead of state-files - -# TODO: add possibility to manually clone a (binary) package (e.g. -# for build-support) - this should also be possible via email +# 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 @@ -34,369 +20,40 @@ usage() { >&2 echo '' >&2 echo 'db-update [options] [packages]:' - >&2 echo ' move possible packages from staging to testing.' >&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: If necessary, wait for lock blocking.' - >&2 echo ' -h|--help: Show this help and exit.' - >&2 echo ' -n|--no-action: Only print what would be moved.' - >&2 echo ' -s|--stabilize $package:' - >&2 echo ' Assume, package $package can be stabilized.' - >&2 echo ' -u|--unstage $package:' - >&2 echo ' Assume, package $package can be unstaged.' + >&2 echo ' -b|--block:' + >&2 echo ' If necessary, wait for lock blocking.' + >&2 echo ' -f|--force $package-id:' + >&2 echo ' Force movement of Package with given id and move nothing else.' + >&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" } -# move_packages file with one "$package $from_repository $to_repository" per line -# the existence of a directory $tmp_dir is assumed - -move_packages() { - - if [ -z "${tmp_dir}" ] || [ ! -d "${tmp_dir}" ]; then - >&2 echo 'move_packages: No tmp_dir provided.' - exit 2 - fi - - local package - local from_repo - local to_repo - local from_ending - local rm_ending - local to_ending - local repo - local part - local dummynator - local file - - if [ -e "${tmp_dir:?}/tmp" ]; then - rm -rf --one-file-system "${tmp_dir:?}/tmp" - fi - mkdir "${tmp_dir}/tmp" - - touch "${tmp_dir}/tmp/repos" - touch "${tmp_dir}/tmp/packages" - touch "${tmp_dir}/tmp/master-mirror-listing" - mkdir "${tmp_dir}/tmp/transit" - - if ${no_action}; then - dummynator='echo' - else - dummynator='' - fi - - ls_master_mirror 'i686' | \ - while read -r repo; do - ls_master_mirror "i686/${repo}" | \ - sed "s|^|i686/${repo}/|" >> \ - "${tmp_dir}/tmp/master-mirror-listing" - done - - while read -r package from_repo to_repo; do - if [ -z "${package}" ]; then - continue - fi - - if ${no_action}; then - printf \ - 'move "%s" from "%s" to "%s"\n' \ - "${package}" \ - "${from_repo}" \ - "${to_repo}" - fi - - echo "${package}" >> \ - "${tmp_dir}/tmp/packages" - - if echo "${from_repo}" | \ - grep -q 'staging$' && \ - echo "${to_repo}" | \ - grep -q 'testing$'; then - from_ending='done' - rm_ending='test\(ing\|ed\)' - to_ending='testing' - elif echo "${from_repo}" | \ - grep -q 'testing$' && \ - ! echo "${to_repo}" | \ - grep -q 'testing$\|staging$'; then - from_ending='tested' - rm_ending='' - to_ending='' - else - >&2 printf 'move_packages: Cannot move package "%s" from "%s" to "%s".\n' "${package}" "${from_repo}" "${to_repo}" - exit 2 - fi - - echo "${from_repo}" > \ - "${tmp_dir}/tmp/${package}.from_repo" - echo "${to_repo}" > \ - "${tmp_dir}/tmp/${package}.to_repo" - echo "${from_ending}" > \ - "${tmp_dir}/tmp/${package}.from_ending" - echo "${rm_ending}" > \ - "${tmp_dir}/tmp/${package}.rm_ending" - echo "${to_ending}" > \ - "${tmp_dir}/tmp/${package}.to_ending" - - if [ ! -f "${work_dir}/package-states/${package}.${from_ending}" ]; then - >&2 printf 'move_packages: Cannot find package state file "%s"\n' "${package}.${from_ending}" - exit 2 - fi - - cp \ - "${work_dir}/package-states/${package}.${from_ending}" \ - "${tmp_dir}/tmp/${package}.parts" - - sed \ - 's|\(-[^-]\+\)\{3\}\.pkg\.tar\.xz$||' \ - "${tmp_dir}/tmp/${package}.parts" > \ - "${tmp_dir}/tmp/${package}.parts_names" - - sed \ - 'p;s|$|.sig|' \ - "${tmp_dir}/tmp/${package}.parts" > \ - "${tmp_dir}/tmp/${package}.parts_and_signatures" - - while read -r part; do - if ! grep -qxF "i686/${from_repo}/${part}" "${tmp_dir}/tmp/master-mirror-listing"; then - >&2 printf \ - 'move_packages: Cannot find file "%s", part of package "%s".\n' \ - "i686/${from_repo}/${part}" \ - "${package}" - exit 2 - fi - done < \ - "${tmp_dir}/tmp/${package}.parts_and_signatures" - - mkdir -p "${tmp_dir}/tmp/${from_repo}" - mkdir -p "${tmp_dir}/tmp/${to_repo}" - - repos=$( - # shellcheck disable=SC2046 - printf '%s\n' "${from_repo}" "${to_repo}" $(cat "${tmp_dir}/tmp/repos") | \ - sort -u - ) - echo "${repos}" > \ - "${tmp_dir}/tmp/repos" - - done < \ - "$1" - - if ${no_action}; then - find "${tmp_dir}/tmp" -type f | \ - while read -r file; do - if [ "${file%.pkg.tar.xz}.pkg.tar.xz" = "${file}" ] || - [ "${file%.pkg.tar.xz.sig}.pkg.tar.xz.sig" = "${file}" ]; then - echo "'${file}'" - else - echo "${file}:" - sed 's|^|<<|;s|$|>>|' "${file}" - fi - echo - done - fi - - # receive the *.db.tar.gz's and *.files.tar.gz's - - while read -r repo; do - - ${master_mirror_rsync_command} \ - "${master_mirror_rsync_directory}/i686/${repo}/${repo}.db."* \ - "${master_mirror_rsync_directory}/i686/${repo}/${repo}.files."* \ - "${tmp_dir}/tmp/${repo}/" - - # add and remove the packages locally - - if grep -qxF "${repo}" "${tmp_dir}/tmp/"*".from_repo"; then - - # shellcheck disable=SC2046 - repo-remove -q \ - "${tmp_dir}/tmp/${repo}/${repo}.db.tar.gz" \ - $( - grep -lxF "${repo}" "${tmp_dir}/tmp/"*".from_repo" | \ - sed ' - s|\.from_repo$|.parts_names| - ' | \ - xargs -rn1 cat - ) - fi - - if grep -qxF "${repo}" "${tmp_dir}/tmp/"*".to_repo"; then - grep -lxF "${repo}" "${tmp_dir}/tmp/"*".to_repo" | \ - sed ' - s|\.to_repo$|| - ' | \ - while read -r package; do - while read -r part; do - ${master_mirror_rsync_command} \ - "${master_mirror_rsync_directory}/i686/$(cat "${package}.from_repo")/${part}" \ - "${master_mirror_rsync_directory}/i686/$(cat "${package}.from_repo")/${part}.sig" \ - "${tmp_dir}/tmp/transit/" - repo-add -q \ - "${tmp_dir}/tmp/${repo}/${repo}.db.tar.gz" \ - "${tmp_dir}/tmp/transit/${part}" - rm \ - "${tmp_dir}/tmp/transit/${part}" \ - "${tmp_dir}/tmp/transit/${part}.sig" - done < \ - "${package}.parts" - done - fi - - done < "${tmp_dir}/tmp/repos" - - if ${no_action}; then - find "${tmp_dir}/tmp" -type f - fi - - # move the packages remotely via sftp - - { - while read -r package; do - - if [ -z "${package}" ]; then - continue - fi - - while read -r part; do - if [ -z "${part}" ]; then - continue - fi - printf \ - 'rename "%s" "%s"\n' \ - "i686/$(cat "${tmp_dir}/tmp/${package}.from_repo")/${part}" \ - "i686/$(cat "${tmp_dir}/tmp/${package}.to_repo")/${part}" - done < \ - "${tmp_dir}/tmp/${package}.parts_and_signatures" - - done < \ - "${tmp_dir}/tmp/packages" - echo 'quit' - } | \ - if ${no_action}; then - sed 's|^|sftp: |' - else - ${master_mirror_sftp_command} - fi - - # and push our local *.db.tar.gz via rsync - - while read -r repo; do - - # shellcheck disable=SC2086 - ${dummynator} ${master_mirror_rsync_command} \ - "${tmp_dir}/tmp/${repo}/${repo}.db."* \ - "${tmp_dir}/tmp/${repo}/${repo}.files."* \ - "${master_mirror_rsync_directory}/i686/${repo}/" - - done < \ - "${tmp_dir}/tmp/repos" - - while read -r package; do - - # then we can safely remove old versions - - while read -r part; do - ${dummynator} remove_old_package_versions 'i686' "$(cat "${tmp_dir}/tmp/${package}.to_repo")" "${part}" - done < \ - "${tmp_dir}/tmp/${package}.parts" - - # and update the state files - - from_ending=$( - cat "${tmp_dir}/tmp/${package}.from_ending" - ) - rm_ending=$( - cat "${tmp_dir}/tmp/${package}.rm_ending" - ) - to_ending=$( - cat "${tmp_dir}/tmp/${package}.to_ending" - ) - - if [ -z "${to_ending}" ]; then - ${dummynator} rm \ - "${work_dir}/package-states/${package}.${from_ending}" - else - # remove old state files of $package with ending $rm_ending - find "${work_dir}/package-states" -maxdepth 1 -regextype grep \ - -regex '.*/'"$(str_to_regex "${package%.*.*.*}")"'\(\.[^.]\+\)\{3\}\.'"${rm_ending}" \ - -execdir ${dummynator} rm {} \; - ${dummynator} mv \ - "${work_dir}/package-states/${package}.${from_ending}" \ - "${work_dir}/package-states/${package}.${to_ending}" - fi - - done < \ - "${tmp_dir}/tmp/packages" - - # shellcheck disable=SC2016 - while read -r package; do - while read -r part; do - printf 'UPDATE `binary_packages`' - printf ' JOIN `repositories` ON `binary_packages`.`repository`=`repositories`.`id`' - printf ' JOIN `architectures` ON `binary_packages`.`architecture`=`architectures`.`id`' - printf ' SET `binary_packages`.`repository`=(SELECT `repositories`.`id` FROM `repositories` WHERE `repositories`.`name`=from_base64("%s"))' \ - "$( - base64_encode_each < \ - "${tmp_dir}/tmp/${package}.to_repo" - )" - printf ' WHERE' - printf ' `repositories`.`name`=from_base64("%s")' \ - "$( - base64_encode_each < \ - "${tmp_dir}/tmp/${package}.from_repo" - )" - printf '%s\n' "${part}" | \ - sed ' - s/\.pkg\.tar\.xz$// - s/-\([^-.]\+\)\(-[^-]\+\)$/-\1.0\2/ - s/-\([^-:]\+\)\(\(-[^-]\+\)\{2\}\)$/-0:\1\2/ - s/^\(.\+\)-\([^-:]\+\):\([^-:]\+\)-\([^-.]\+\)\.\([^-.]\+\)-\([^-]\+\)$/\1\n\2\n\3\n\4\n\5\n\6/ - ' | \ - base64_encode_each | \ - tr '\n' ' ' | \ - sed ' - s,\(\S\+\) \(\S\+\) \(\S\+\) \(\S\+\) \(\S\+\) \(\S\+\) $,'"$( - printf ' AND `binary_packages`.`%s`=from_base64("%s")' \ - 'pkgname' '\1' \ - 'epoch' '\2' \ - 'pkgver' '\3' \ - 'pkgrel' '\4' \ - 'sub_pkgrel' '\5' - printf ' AND `architectures`.`name`=from_base64("\\6")' - )"', - ' - printf ';\n' - done < \ - "${tmp_dir}/tmp/${package}.parts" - done < \ - "${tmp_dir}/tmp/packages" | \ - if ${no_action}; then - sed 's|^|mysql: |' - else - ${mysql_command} - fi - - ${dummynator} trigger_mirror_refreshs - - rm -rf --one-file-system "${tmp_dir:?}/tmp" - -} - eval set -- "$( - getopt -o bf:hns:u: \ + getopt -o bf:hnp \ --long block \ - --long from: \ + --long force \ --long help \ --long no-action \ - --long stabilize: \ - --long unstage: \ + --long progressive \ -n "$(basename "$0")" -- "$@" || \ echo usage )" block_flag='-n' no_action=false +progressive=false +force_ids='' while true do @@ -404,19 +61,22 @@ do -b|--block) block_flag='' ;; + -f|--force) + shift + force_ids=$( + printf '%s' "$1" | \ + base64 -w0 + printf '\n%s' "${force_ids}" + ) + ;; -h|--help) usage 0 ;; -n|--no-action) no_action=true ;; - -s|--stabilze) - shift - packages_to_force_stabilize="${packages_to_force_stabilize} $1" - ;; - -u|--unstage) - shift - packages_to_force_unstage="${packages_to_force_unstage} $1" + -p|--progressive) + progressive=true ;; --) shift @@ -435,237 +95,306 @@ if [ $# -ne 0 ]; then usage fi +if ${progressive} && \ + [ -n "${force_ids}" ]; then + >&2 echo 'db-update: conflicting arguments' + usage +fi + if [ -s "${work_dir}/build-master-sanity" ]; then >&2 echo 'Build master is not sane.' exit fi -tmp_dir=$(mktemp -d "${work_dir}/tmp.db-update.XXXXXXXXXX") -trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT - -for package in ${packages_to_force_stabilize}; do - # some sanity checks - if [ ! -f "${work_dir}/package-states/${package}.tested" ] && \ - [ ! -f "${work_dir}/package-states/${package}.testing" ] && \ - [ ! -f "${work_dir}/package-states/${package}.done" ] && \ - ! tr ' ' '.' < \ - "${work_dir}/build-list" | \ - grep -qxF "${package}"; then - >&2 printf 'Package "%s" is not in testing, staging or on the build list!\n' "${package}" - exit 2 - fi -done +# Create tmp_dir, lock and trap. -for package in ${packages_to_force_unstage}; do - # some sanity checks - if [ ! -f "${work_dir}/package-states/${package}.done" ] && \ - ! tr ' ' '.' < \ - "${work_dir}/build-list" | \ - grep -qxF "${package}"; then - >&2 printf 'Package "%s" is not in staging or on the build list!\n' "${package}" - exit 2 - fi -done - -# Create a lock file and a trap. - -exec 9> "${build_list_lock_file}" +exec 9> "${package_database_lock_file}" if ! flock ${block_flag} 9; then - >&2 echo 'come back (shortly) later - I cannot lock build list.' - exit 0 -fi - -exec 8> "${package_database_lock_file}" -if ! flock ${block_flag} 8; then >&2 echo 'come back (shortly) later - I cannot lock package database.' exit 0 fi -exec 7> "${sanity_check_lock_file}" -if ! flock -s ${block_flag} 7; then +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 -clean_up_lock_file() { - rm -f "${package_database_lock_file}" "${build_list_lock_file}" - rm -rf --one-file-system "${tmp_dir}" -} - -trap clean_up_lock_file EXIT - -# sanity check - -for ending in 'done' 'tested'; do - errors=$( - find "${work_dir}/package-states" -name "*.${ending}" -printf '%f\n' | \ - sed 's|\(\.[^.]\+\)\{4\}$||' | \ - sort | \ - uniq -d - ) - if [ -n "${errors}" ]; then - >&2 echo 'Removing duplicates not yet implemented:' - >&2 echo "${errors}" - exit 42 - fi -done - -# packages which are done +tmp_dir=$(mktemp -d "${work_dir}/tmp.db-update.XXXXXXXXXX") +trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT -find "${work_dir}/package-states" -maxdepth 1 -type f -name '*.done' -printf '%f\n' | \ - sed ' - s|\.done$|| - ' | \ - sort -u > \ - "${tmp_dir}/done-packages" +export TMPDIR="${tmp_dir}" -# packages still on the build-list +for source_stability in 'testing' 'staging'; do + find "${tmp_dir}" -mindepth 1 -delete -grep -vxF 'break_loops' "${work_dir}/build-list" | \ - tr ' ' '.' | \ - sort -u > \ - "${tmp_dir}/build-list-packages" + # shellcheck disable=SC2016 + { + if [ -n "${force_ids}" ]; 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 `moveable_binary_packages` (`id`,`to_repository`)' + printf ' VALUES' + # shellcheck disable=SC2086 + printf '(from_base64("%s"),NULL),' \ + ${force_ids} | \ + sed 's/,$/;\n/' + printf 'DELETE `moveable_binary_packages` FROM `moveable_binary_packages`' + printf ' JOIN `binary_packages` ON `binary_packages`.`id`=`moveable_binary_packages`.`id`' + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + printf ' WHERE `repository_stabilities`.`name`!="%s";\n' \ + "${source_stability}" + printf 'UPDATE `moveable_binary_packages`' + printf ' JOIN `binary_packages` ON `binary_packages`.`id`=`moveable_binary_packages`.`id`' + 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 ' SET `moveable_binary_packages`.`to_repository`=`repository_moves`.`to_repository`;\n' + printf 'DELETE FROM `moveable_binary_packages` WHERE `moveable_binary_packages`.`to_repository` IS NULL;\n' + printf 'INSERT IGNORE INTO `replaced_binary_packages` (`id`,`replaced_by`)' + printf ' SELECT `binary_packages`.`id`,`moveable_binary_packages`.`id`' + printf ' FROM `moveable_binary_packages`' + printf ' JOIN `binary_packages` AS `subst_bp` ON `moveable_binary_packages`.`id`=`subst_bp`.`id`' + printf ' JOIN `binary_packages` ON `binary_packages`.`pkgname`=`subst_bp`.`pkgname`' + printf ' AND `binary_packages`.`repository`=`moveable_binary_packages`.`to_repository`;\n' + elif ${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}" + 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' -find "${work_dir}/package-infos" -name '*.groups' \ - -exec grep -qxF 'base' {} \; \ - -printf '%f\n' | \ - sed ' - s|\.groups$|| - ' | \ - sort -u > \ - "${tmp_dir}/base-packages" + 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}" + 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 -{ - # shellcheck disable=SC2086 - printf '%s\n' ${packages_to_force_unstage} - print_list_of_archaic_packages 'build-list' -} > \ - "${tmp_dir}/force-unstage-packages" - ->&2 printf 'calculate what packages should be unstaged ...' -find_biggest_subset_of_packages "${tmp_dir}/done-packages" "${tmp_dir}/build-list-packages" "${tmp_dir}/all-builds" "${tmp_dir}/all-depends" "${tmp_dir}/force-unstage-packages" > \ - "${tmp_dir}/unstage-packages" ->&2 printf ' ok.\n' - -# no base packages on the build list anymore? -if [ -z "$( - join -j 1 \ - "${tmp_dir}/base-packages" \ - "${tmp_dir}/build-list-packages" - )" ]; then - - >&2 echo 'db-update unstage: we pretend, the group "base" does not exist, so we only fetch "direct" dependencies on base-packages' - for s in "${tmp_dir}/all-builds" "${tmp_dir}/all-depends"; do - sed '/ base$/d' "${s}" > \ - "${s}.no-base" - done + 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_run_query | \ + tr '\t' ' ' | \ + grep '^\(repositories\|\(rm\|mv\)\(\.id\)\?\) ' | \ + while read -r what content; do + printf '%s\n' "${content}" >> \ + "${tmp_dir}/${what}" + done - >&2 printf 'calculate what packages should be unstaged ...' - find_biggest_subset_of_packages "${tmp_dir}/done-packages" "${tmp_dir}/build-list-packages" "${tmp_dir}/all-builds.no-base" "${tmp_dir}/all-depends.no-base" "${tmp_dir}/force-unstage-packages" > \ - "${tmp_dir}/unstage-packages" - >&2 printf ' ok.\n' + if [ ! -s "${tmp_dir}/repositories" ]; then + >&2 printf 'Nothing to move from %s.\n' "${source_stability}" + continue + fi -fi + touch \ + "${tmp_dir}/mv" \ + "${tmp_dir}/mv.id" \ + "${tmp_dir}/rm" \ + "${tmp_dir}/rm.id" -{ # shellcheck disable=SC2086 - printf '%s\n' ${packages_to_force_stabilize} - print_list_of_archaic_packages 'build-list' 'staging' 'testing' -} > \ - "${tmp_dir}/force-stabilize-packages" - -# calculate what packages should be stabilized - -{ - cat "${tmp_dir}/done-packages" "${tmp_dir}/build-list-packages" - find "${work_dir}/package-states" -maxdepth 1 -type f -name '*.testing' -printf '%f\n' | \ - sed 's|\.testing$||' -} | \ - sort -u > \ - "${tmp_dir}/keep-packages" - -find "${work_dir}/package-states" -maxdepth 1 -type f -name '*.tested' -printf '%f\n' | \ - sed 's|\.tested$||' > \ - "${tmp_dir}/stabilize-packages" - -# no base packages on the build list or in staging anymore? -if [ -z "$( - cat \ - "${tmp_dir}/build-list-packages" \ - "${tmp_dir}/done-packages" | \ - sort -u | \ - join -j 1 \ - "${tmp_dir}/base-packages" \ - - - )" ]; then - - >&2 echo 'db-update stabilize: we pretend, the group "base" does not exist, so we only fetch "direct" dependencies on base-packages' - >&2 printf 'calculate what packages should be stabilized ...' - find_biggest_subset_of_packages "${tmp_dir}/stabilize-packages" "${tmp_dir}/keep-packages" "${tmp_dir}/all-builds.no-base" "${tmp_dir}/all-depends.no-base" "${tmp_dir}/force-stabilize-packages" | \ - sponge "${tmp_dir}/stabilize-packages" - >&2 printf ' ok.\n' - -else - - >&2 printf 'calculate what packages should be stabilized ...' - find_biggest_subset_of_packages "${tmp_dir}/stabilize-packages" "${tmp_dir}/keep-packages" "${tmp_dir}/all-builds" "${tmp_dir}/all-depends" "${tmp_dir}/force-stabilize-packages" | \ - sponge "${tmp_dir}/stabilize-packages" - >&2 printf ' ok.\n' - -fi + for s in "${tmp_dir}/"*; do + sort -u "${s}" | \ + sponge "${s}" + done -# unlock build list + # 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 -rm -f "${build_list_lock_file}" -flock -u 9 + # 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 -clean_up_lock_file() { - rm -rf --one-file-system "${tmp_dir}" - rm -f "${package_database_lock_file}" -} + # 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 -# testing -> stable + # 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 -while read -r package; do - if [ -z "${package}" ]; then - continue - fi - printf '%s %s %s\n' \ - "${package}" \ - "$(official_or_community "${package}" 'testing')" \ - "$(repository_of_package "${package}")" -done < \ - "${tmp_dir}/stabilize-packages" | \ - sponge "${tmp_dir}/stabilize-packages" - -# staging -> testing - -while read -r package; do - if [ -z "${package}" ]; then + if ${no_action}; then continue fi - printf '%s %s %s\n' \ - "${package}" \ - "$(official_or_community "${package}" 'staging')" \ - "$(official_or_community "${package}" 'testing')" -done < \ - "${tmp_dir}/unstage-packages" | \ - sponge "${tmp_dir}/unstage-packages" - -if [ -s "${tmp_dir}/stabilize-packages" ]; then - >&2 printf 'move packages from *testing/ to the stable repos ...' - move_packages "${tmp_dir}/stabilize-packages" - >&2 printf ' ok.\n' -else - >&2 printf 'Nothing to move from *testing to the stable repos.\n' -fi -if [ -s "${tmp_dir}/unstage-packages" ]; then - >&2 printf 'move packages from *staging to *testing ...' - move_packages "${tmp_dir}/unstage-packages" - >&2 printf ' ok.\n' -else - >&2 printf 'Nothing to move from *staging to *testing.\n' -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 +done -clean_up_lock_file +trigger_mirror_refreshs diff --git a/bin/delete-packages b/bin/delete-packages index 26d323a..4343bc7 100755 --- a/bin/delete-packages +++ b/bin/delete-packages @@ -13,7 +13,7 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" -# TODO: finish this +# TODO: delete other to-be-deleted packages if asked to do so # shellcheck disable=SC2016 usage() { @@ -69,18 +69,15 @@ if [ -s "${work_dir}/build-master-sanity" ]; then exit fi -tmp_dir=$(mktemp -d "${work_dir}/tmp.delete-packages.XXXXXXXXXX") -trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT - # Create a lock file and a trap. if ! ${no_action}; then - exec 9> "${build_list_lock_file}" - if ! flock ${block_flag} 9; then - >&2 echo 'come back (shortly) later - I cannot lock build list.' - exit 0 - fi +# exec 9> "${build_list_lock_file}" +# if ! flock ${block_flag} 9; then +# >&2 echo 'come back (shortly) later - I cannot lock build list.' +# exit 0 +# fi exec 8> "${package_database_lock_file}" if ! flock ${block_flag} 8; then @@ -96,260 +93,158 @@ if ! ${no_action}; then fi -clean_up_lock_file() { - if ! ${no_action}; then - rm -f "${package_database_lock_file}" "${build_list_lock_file}" - fi - rm -rf --one-file-system "${tmp_dir}" -} - -trap clean_up_lock_file EXIT - -cp \ - "${work_dir}/deletion-list" \ - "${work_dir}/build-list" \ - "${tmp_dir}/" - -all_repos="${standalone_package_repositories} ${stable_package_repositories} ${testing_package_repositories} ${staging_package_repositories}" -all_repos="core" - -for repo in ${all_repos}; do - mkdir "${tmp_dir}/${repo}" - ${master_mirror_rsync_command} \ - "${master_mirror_rsync_directory}/i686/${repo}/${repo}.db."* \ - "${master_mirror_rsync_directory}/i686/${repo}/${repo}.files."* \ - "${tmp_dir}/${repo}/" -done - -for repo in ${all_repos}; do - tar -C "${tmp_dir}/${repo}" -xzf "${tmp_dir}/${repo}/${repo}.db.tar.gz" -done +tmp_dir=$(mktemp -d "${work_dir}/tmp.delete-packages.XXXXXXXXXX") +trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT -find "${tmp_dir}" -mindepth 3 -maxdepth 3 -name 'desc' -exec \ +# shellcheck disable=SC2016 +{ + printf 'CREATE TEMPORARY TABLE `to_deletes` (`id` BIGINT, UNIQUE KEY (`id`));\n' + printf 'INSERT IGNORE INTO `to_deletes`' + printf ' SELECT `binary_packages`.`id`' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_dependencies + mysql_join_dependencies_dependency_types + printf ' AND `dependency_types`.`relevant_for_binary_packages`' + mysql_join_binary_packages_repositories + printf ' AND `repositories`.`is_on_master_mirror`' + printf ' WHERE `binary_packages`.`is_to_be_deleted`' + printf ' AND NOT EXISTS (' + printf 'SELECT * FROM `install_target_providers`' + mysql_join_install_target_providers_binary_packages '' 'prov_bp' + mysql_join_binary_packages_repositories 'prov_bp' 'prov_r' + mysql_join_repositories_repository_stabilities 'prov_r' + printf ' WHERE `install_target_providers`.`install_target`=`dependencies`.`depending_on`' + printf ' AND `repository_stabilities`.`name` NOT IN ("forbidden","virtual")' + printf ');\n' + + printf ' SELECT DISTINCT "repo",`repositories`.`name`' + printf ' FROM `to_deletes`' + printf ' JOIN `binary_packages` ON `to_deletes`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_repositories + printf ';\n' + + printf ' SELECT "package",`repositories`.`name`,`binary_packages`.`pkgname`' + printf ' FROM `to_deletes`' + printf ' JOIN `binary_packages` ON `to_deletes`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_repositories + printf ';\n' + + printf ' SELECT "package-file",`repositories`.`name`,' + mysql_package_name_query + printf ' FROM `to_deletes`' + printf ' JOIN `binary_packages` ON `to_deletes`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_repositories + mysql_join_binary_packages_architectures + printf ';\n' + + printf ' SELECT "package-id",`to_deletes`.`id`' + printf ' FROM `to_deletes`' + printf ';\n' +} | \ + mysql_run_query | \ sed ' - /^%DEPENDS%$/{ - s/.*// - :dep_loop - N - /\n$/{ - s@\(^\|\n\)\(.\)@\1depends \2@g - bend - } - bdep_loop + y/\t/ / + /^repo /{ + s/^\S\+ // + w '"${tmp_dir}"'/repositories + d } - /^%PROVIDES%$/{ - s/.*// - :pro_loop - N - /\n$/{ - s@\(^\|\n\)\(.\)@\1provides \2@g - bend - } - bpro_loop + /^package /{ + s/^\S\+ // + w '"${tmp_dir}"'/packages + d } - d - :end - s/[<>=]\S*\($\|\n\)/\1/g - s#\(^\|\n\)\(.\)#\1{} \2#g - ' {} \; \ - -printf '%p provides %p\n' | \ - sed ' - /^$/d - s|^\S\+/\([^/ ]\+\)/\([^/ ]\+\)\(-[^-/ ]\+\)\{2\}/desc |\1 \2 | - s| \S\+/\([^/]\+\)\(-[^/-]\+\)\{2\}/desc$| \1| - ' | \ - awk '{print $3 " " $1 " " $2 " " $4}' | \ - sed -n ' - /^provides /{ - s/^provides // - w '"${tmp_dir}"'/db.provides - b + /^package-file /{ + s/^\S\+ // + s, ,/, + w '"${tmp_dir}"'/package-files + s/$/.sig/ + w '"${tmp_dir}"'/package-files + d } - /^depends /{ - s/^depends // - w '"${tmp_dir}"'/db.depends - b + /^package-id /{ + s/^\S\+ // + w '"${tmp_dir}"'/package-ids + d } ' +if [ ! -s "${tmp_dir}/packages" ]; then + printf 'Nothing to delete.\n' + exit +fi + +mkdir "${tmp_dir}/repos" + +while read -r repo; do + ${master_mirror_rsync_command} \ + "${master_mirror_rsync_directory}/i686/${repo}/${repo}.db.tar.gz" \ + "${master_mirror_rsync_directory}/i686/${repo}/${repo}.files.tar.gz" \ + "${tmp_dir}/repos/" + + tar -Oxzf "${tmp_dir}/repos/${repo}.db.tar.gz" --wildcards '*/desc' | \ + sed -n ' + /^%FILENAME%$/{ + N + s/^\S\+\n\(\S\+-[^-.]\+\)\(-[^-]\+\)/\1.0\2 \1\2/ + T + p + } + ' | \ + while read -r old new; do + printf 's,/%s\\(\\.sig\\)\\?$,/%s\\1,\n' \ + "$(str_to_regex "${old}")" \ + "$(str_to_regex "${new}")" + done >> \ + "${tmp_dir}/sub_pkgrel-removal.sed" + + # shellcheck disable=SC2046 + repo-remove "${tmp_dir}/repos/${repo}.db.tar.gz" \ + $( + grep "^$(str_to_regex "${repo}") " "${tmp_dir}/packages" | \ + cut -d' ' -f2 + ) + if ! ${no_action}; then + ${master_mirror_rsync_command} \ + "${tmp_dir}/repos/${repo}.db.tar.gz" \ + "${tmp_dir}/repos/${repo}.files.tar.gz" \ + "${master_mirror_rsync_directory}/i686/${repo}/" + fi +done < \ + "${tmp_dir}/repositories" + +if [ -s "${tmp_dir}/sub_pkgrel-removal.sed" ]; then + sed -i -f "${tmp_dir}/sub_pkgrel-removal.sed" "${tmp_dir}/package-files" +fi +if ${no_action}; then + printf 'Now, I would remove the packages from the database and delete the following files from the master mirror:\n' + sed ' + s,^, , + ' "${tmp_dir}/package-files" + exit +fi -echo 'OK' >&2 -exit 42 - -# # sanity check -# -# for ending in 'done' 'testing'; do -# if [ "${ending}" = 'testing' ] && \ -# [ -z "${packages_to_stabilize}" ]; then -# # if nothing is to be untested, we don't care about duplicate -# # testing packages (and maybe an unstaging fixes this anyway) -# continue -# fi -# if [ -n "$( -# find "${work_dir}/package-states" -name "*.${ending}" -printf '%f\n' | \ -# sed 's|\(\.[^.]\+\)\{4\}$||' | \ -# sort | \ -# uniq -d -# )" ]; then -# >&2 echo 'Removing duplicates not yet implemented:' -# find "${work_dir}/package-states" -name "*.${ending}" -printf '%f\n' | \ -# sed 's|\(\.[^.]\+\)\{4\}$||' | \ -# sort | \ -# uniq -d -# exit 42 -# fi -# done -# -# # packages which are done -# -# find "${work_dir}/package-states" -maxdepth 1 -type f -name '*.done' -printf '%f\n' | \ -# sed ' -# s|\.done$|| -# ' | \ -# sort -u > \ -# "${tmp_dir}/done-packages" -# -# # packages still on the build-list -# -# tr ' ' '.' < \ -# "${work_dir}/build-list" | \ -# sort -u > \ -# "${tmp_dir}/keep-packages" -# -# find "${work_dir}/package-infos" -name '*.groups' \ -# -exec grep -qx 'base\(-devel\)\?' {} \; \ -# -printf '%f\n' | \ -# sed ' -# s|\.groups$|| -# ' | \ -# sort -u > \ -# "${tmp_dir}/base-packages" -# -# # no base / base-devel packages on the build list? -# if [ -z "$( -# join -j 1 \ -# "${tmp_dir}/base-packages" \ -# "${tmp_dir}/keep-packages" -# )" ]; then -# # unstage all base / base-devel packages from staging -# cat "${tmp_dir}/base-packages" "${tmp_dir}/base-packages" "${tmp_dir}/keep-packages" | \ -# sort | \ -# uniq -u | \ -# sponge "${tmp_dir}/keep-packages" -# fi -# -# # find all dependencies of the unstageable packages -# mv \ -# "${tmp_dir}/keep-packages" \ -# "${tmp_dir}/new-keep-packages" -# touch "${tmp_dir}/keep-packages" -# -# while [ -s "${tmp_dir}/new-keep-packages" ]; do -# -# cat "${tmp_dir}/new-keep-packages" "${tmp_dir}/keep-packages" | \ -# sort -u | \ -# sponge "${tmp_dir}/keep-packages" -# -# sed ' -# s|^|'"${work_dir}"'/package-infos/| -# s|$|.depends| -# ' "${tmp_dir}/keep-packages" | \ -# xargs -r grep -HF '' | \ -# sed ' -# s|^.*/|| -# s|\.depends:| | -# ' | \ -# sort -u | \ -# sort -k2,2 > \ -# "${tmp_dir}/keep-packages.depends" -# -# sed ' -# s|^|'"${work_dir}"'/package-infos/| -# s|$|.builds| -# ' "${tmp_dir}/done-packages" | \ -# xargs -r grep -HF '' | \ -# sed ' -# s|^.*/|| -# s|\.builds:| | -# ' | \ -# sort -u | \ -# sort -k2,2 > \ -# "${tmp_dir}/done-packages.builds" -# -# join -j 2 -o 1.1 \ -# "${tmp_dir}/done-packages.builds" \ -# "${tmp_dir}/keep-packages.depends" | \ -# sort -u > \ -# "${tmp_dir}/new-keep-packages" -# -# # "new" is only what has not been there before -# cat "${tmp_dir}/keep-packages" "${tmp_dir}/keep-packages" "${tmp_dir}/new-keep-packages" | \ -# sort | \ -# uniq -u | \ -# sponge "${tmp_dir}/new-keep-packages" -# -# done -# -# # unlock build list -# -# rm -f "${build_list_lock_file}" -# flock -u 9 -# -# clean_up_lock_file() { -# rm -rf --one-file-system "${tmp_dir}" -# rm -f "${package_database_lock_file}" -# } -# -# # calculate unstageable packages from keep_packages and done_packages -# -# done_packages=$( -# cat \ -# "${tmp_dir}/keep-packages" \ -# "${tmp_dir}/keep-packages" \ -# "${tmp_dir}/done-packages" | \ -# sort | \ -# uniq -u -# ) -# -# # move packages in packages_to_stabilize from *testing/ to the stable repos -# -# # shellcheck disable=SC2046 -# move_packages $( -# -# for package in ${packages_to_stabilize}; do -# -# if [ -z "${package}" ]; then -# continue -# fi -# -# printf '%s/%s/%s\n' \ -# "${package}" \ -# "$(official_or_community "${package}" 'testing')" \ -# "$(repository_of_package "${package}")" -# -# done -# -# ) -# -# # move packages from *staging to *testing -# -# # shellcheck disable=SC2046 -# move_packages $( -# -# for package in ${done_packages}; do -# -# if [ -z "${package}" ]; then -# continue -# fi -# -# printf '%s/%s/%s\n' \ -# "${package}" \ -# "$(official_or_community "${package}" 'staging')" \ -# "$(official_or_community "${package}" 'testing')" -# -# done -# -# ) -# -# clean_up_lock_file +# shellcheck disable=SC2016 +{ + printf 'CREATE TEMPORARY TABLE `to_deletes` (`id` BIGINT, UNIQUE KEY (`id`));\n' + printf 'LOAD DATA LOCAL INFILE "%s" INTO TABLE `to_deletes`;\n' \ + "${tmp_dir}/package-ids" + + printf 'UPDATE `binary_packages` ' + printf ' JOIN `to_deletes` ON `to_deletes`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_repositories + mysql_join_binary_packages_architectures + printf ' SET `repository`=(' + printf 'SELECT `repositories`.`id`' + printf ' FROM `repositories`' + printf ' WHERE `repositories`.`name`="deletion-list"' + printf ');\n' +} | \ + mysql_run_query + +sed ' + s,^,rm "i686/, + s,$,", +' "${tmp_dir}/package-files" | \ + ${master_mirror_sftp_command} diff --git a/bin/get-assignment b/bin/get-assignment index d256248..7ac749f 100755 --- a/bin/get-assignment +++ b/bin/get-assignment @@ -15,49 +15,37 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" -mkdir -p "${work_dir}/package-states" +# TODO: honor manual build order of tool-chain: +# toolchain build order: linux-api-headers->glibc->binutils->gcc->binutils->glibc hand_out_assignment() { - # find out the sub_pkgrel - sub_pkgrel=$( - next_sub_pkgrel "$1" "$2" "$3" "$4" - ) - - # we don't care anymore if an older version of this package was - # "locked" or "broken" - find "${work_dir}/package-states" -maxdepth 1 -regextype grep \ - -regex '.*/'"$(str_to_regex "$1")"'\(\.[^.]\+\)\{3\}\.\(locked\|broken\)' \ - -not -regex '.*/'"$(str_to_regex "$1.$2.$3.$4.")"'[^.]\+' \ - -delete - - # move that build order to the end of the build-list - sed -i ' - /^'"$(str_to_regex "$1 $2 $3 $4")"'$/ { - $ b - d - } - $ a '"$1 $2 $3 $4" \ - "${work_dir}/build-list" - - echo "$1 $2 $3 $4 ${sub_pkgrel}" - { - # shellcheck disable=SC2154 - echo "${slave}" - if [ -r "${work_dir}/package-states/$1.$2.$3.$4.locked" ]; then - cat "${work_dir}/package-states/$1.$2.$3.$4.locked" - fi - } | \ - sort -u | \ - sponge "${work_dir}/package-states/$1.$2.$3.$4.locked" # shellcheck disable=SC2016 { + printf 'SELECT ' + printf '`package_sources`.`%s`,' \ + 'pkgbase' 'git_revision' 'mod_git_revision' + printf '`upstream_repositories`.`name`,`binary_packages`.`sub_pkgrel`' + printf ' FROM `upstream_repositories`' + mysql_join_upstream_repositories_package_sources + mysql_join_package_sources_build_assignments + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="build-list"' + printf ' AND `package_sources`.`%s`=from_base64("%s")' \ + 'pkgbase' "$(printf '%s' "$1" | base64 -w0)" \ + 'git_revision' "$(printf '%s' "$2" | base64 -w0)" \ + 'mod_git_revision' "$(printf '%s' "$3" | base64 -w0)" + printf ' AND `upstream_repositories`.`name`=from_base64("%s")' \ + "$(printf '%s' "$4" | base64 -w0)" + printf ' LIMIT 1;\n' + printf 'UPDATE `build_slaves`' printf ' SET `currently_building` = (' printf ' SELECT `build_assignments`.`id`' printf ' FROM `build_assignments`' - printf ' JOIN `package_sources` ON `build_assignments`.`package_source`=`package_sources`.`id`' - printf ' JOIN `upstream_repositories` ON `package_sources`.`upstream_package_repository`=`upstream_repositories`.`id`' + mysql_join_build_assignments_package_sources + mysql_join_package_sources_upstream_repositories printf ' WHERE' printf ' `package_sources`.`%s` = from_base64("%s") AND' \ 'pkgbase' "$(printf '%s' "$1" | base64 -w0)" \ @@ -66,16 +54,24 @@ hand_out_assignment() { printf ' `upstream_repositories`.`name` = from_base64("%s")' \ "$(printf '%s' "$4" | base64 -w0)" printf ')' - printf ' WHERE `build_slaves`.`name`=from_base64("%s");' \ + # shellcheck disable=SC2154 + printf ' WHERE `build_slaves`.`name`=from_base64("%s");\n' \ "$(printf '%s' "${slave}" | base64 -w0)" - } | \ - ${mysql_command} - # lock every loop this package breaks - find "${work_dir}/build-list.loops" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' \ - -exec grep -qxF "$1" '{}' \; \ - -exec touch '{}.locked' \; + printf 'UPDATE `build_assignments`' + mysql_join_build_assignments_package_sources + mysql_join_package_sources_upstream_repositories + printf ' SET `build_assignments`.`priority`=0' + printf ' WHERE' + printf ' `package_sources`.`%s` = from_base64("%s") AND' \ + 'pkgbase' "$(printf '%s' "$1" | base64 -w0)" \ + 'git_revision' "$(printf '%s' "$2" | base64 -w0)" \ + 'mod_git_revision' "$(printf '%s' "$3" | base64 -w0)" + printf ' `upstream_repositories`.`name` = from_base64("%s");\n' \ + "$(printf '%s' "$4" | base64 -w0)" + } | \ + mysql_run_query | \ + tr '\t' ' ' exit 0 @@ -108,140 +104,106 @@ clean_up() { tmp_dir=$(mktemp -d 'tmp.get-assignment.XXXXXXXXXX' --tmpdir) trap clean_up EXIT -# Check if there are any pending packages at all and if the requester -# has already got an assignment. - -pending_packages=false - -while read -r package git_revision mod_git_revision repository; do +# if we're building something already, hand it out (again) +currently_building=$( + # shellcheck disable=SC2016 + { + printf 'SELECT ' + mysql_query_select_pkgbase_and_revision + mysql_join_build_assignments_build_slaves + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE `build_slaves`.`name`=from_base64("%s")' \ + "$(printf '%s' "${slave}" | base64 -w0)" + printf ' AND `repositories`.`name`="build-list"' + printf ' LIMIT 1;\n' + } | \ + mysql_run_query +) - generate_package_metadata "${package}.${git_revision}.${mod_git_revision}.${repository}" +if [ -n "${currently_building}" ]; then + # shellcheck disable=SC2086 + hand_out_assignment ${currently_building} +fi - if [ -f "${work_dir}/package-states/${package}.${git_revision}.${mod_git_revision}.${repository}.locked" ]; then - # has this slave already got or does he prefer this assignment? - # note, that the dependencies are met, because this package is already locked - if grep -qxF "${slave}" "${work_dir}/package-states/${package}.${git_revision}.${mod_git_revision}.${repository}.locked" || \ - printf ',%s,' "$1" | \ - grep -qF ",${package},"; then - hand_out_assignment "${package}" "${git_revision}" "${mod_git_revision}" "${repository}" - fi - elif [ ! -f "${work_dir}/package-states/${package}.${git_revision}.${mod_git_revision}.${repository}.blocked" ]; then - pending_packages=true - fi +# a package with [all dependencies met or which is part of a loop] +# and which is currently not being built, ordered by: +# 1: we requested it +# 2: its priority +# 3: is not yet built +# 4: was built the longest time ago +next_building=$( + # shellcheck disable=SC2016 + { + printf 'SELECT ' + printf '`package_sources`.`pkgbase`=from_base64("%s") AS `requested`,' \ + "$( + printf '%s' "$1" | \ + base64 -w0 + )" + printf '`build_assignments`.`priority`,' + printf 'COALESCE(' + printf 'MAX(`failed_builds`.`date`),0' + printf ') AS `last_trial`,' + mysql_query_is_part_of_loop '`build_assignments`.`id`' + printf ' AS `part_of_loop`,' + mysql_query_select_pkgbase_and_revision + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' LEFT' + mysql_join_build_assignments_failed_builds + printf ' WHERE `repositories`.`name`="build-list"' + printf ' AND NOT EXISTS (' + printf ' SELECT *' + printf ' FROM `build_slaves`' + printf ' WHERE `build_slaves`.`currently_building`=`build_assignments`.`id`' + printf ') AND (' + printf '`build_assignments`.`is_blocked` IS NULL' + printf ' OR' + printf ' `package_sources`.`pkgbase`=from_base64("%s")' \ + "$( + printf '%s' "$1" | \ + base64 -w0 + )" + printf ') AND (' + mysql_query_is_part_of_loop '`build_assignments`.`id`' + printf ' OR NOT ' + mysql_query_has_pending_dependencies '`build_assignments`.`id`' + printf ')' + printf ' GROUP BY `build_assignments`.`id`' + printf ' ORDER BY `requested` DESC, `priority` DESC, `last_trial`, `part_of_loop`, `build_assignments`.`id`' + printf ' LIMIT 1;\n' + } | \ + mysql_run_query | \ + sed ' + y/\t/ / + s/^.* \(\S\+\( \S\+\)\{3\}\)$/\1/ + ' +) +if [ -n "${next_building}" ]; then + # shellcheck disable=SC2086 + hand_out_assignment ${next_building} +fi -done < "${work_dir}/build-list" +# Check if there are any pending packages at all +count_pending=$( + # shellcheck disable=SC2016 + { + printf 'SELECT count(*)' + printf ' FROM `build_assignments`' + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="build-list"' + printf ' AND `build_assignments`.`is_blocked` IS NULL' + printf ';\n' + } | \ + mysql_run_query +) -if ! ${pending_packages}; then +if [ "${count_pending}" -eq 0 ]; then >&2 echo 'come back after the next run of get-package-updates - currently there are no pending packages' exit 3 +else + >&2 echo 'come back later - there are still packages to be built, but currently none has all its dependencies ready' + exit 2 fi - -# Find first package of build-list whose "dependencies" are all met -# 1st: prefered packages on the build list which have all dependencies met -# 2nd: unbroken packages on the build list which have all dependencies met -# 3rd: unbroken packages breaking a loop -# 4th: broken packages which have all dependencies met or break a loop - -for iteration in 'prefered' 'fresh' 'loops' 'broken'; do - - case "${iteration}" in - 'prefered') - hand_out_blocked=true - hand_out_broken=true - hand_out_loop=false - echo "$1" | \ - tr ',' '\n' | \ - sort -u > \ - "${tmp_dir}/hand-out-only-these-packages" - ;; - 'fresh') - hand_out_blocked=false - hand_out_broken=false - hand_out_loop=false - { - cat "${work_dir}/build-list" - find "${work_dir}/package-states" -name '*.broken' -printf '%f\n' | \ - sed ' - s|\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)\.[^.]\+$| \1 \2 \3| - p - ' - } | \ - sort -k1,1 -k2 | \ - uniq -u | \ - cut -d' ' -f1 | \ - uniq > \ - "${tmp_dir}/hand-out-only-these-packages" - ;; - 'loops') - hand_out_blocked=false - hand_out_broken=false - hand_out_loop=true - find "${work_dir}/build-list.loops" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' \ - -exec cat {} \; | \ - sort -u > \ - "${tmp_dir}/hand-out-only-these-packages" - ;; - 'broken') - hand_out_blocked=false - hand_out_broken=true - hand_out_loop=true - cut -d' ' -f1 < \ - "${work_dir}/build-list" | \ - sort -u > \ - "${tmp_dir}/hand-out-only-these-packages" - ;; - esac - - cat -n "${work_dir}/build-list" | \ - sort -k2,2 | \ - join -1 1 -2 2 -o 2.1,2.2,2.3,2.4,2.5 "${tmp_dir}/hand-out-only-these-packages" - | \ - sort -k1,1 | \ - sed 's|^\s*\S\+\s\+||' > \ - "${tmp_dir}/try-to-hand-out-these-packages" - - while read -r package git_revision mod_git_revision repository; do - - # package locked? (we have taken care of those in the previous loop) - if [ -f "${work_dir}/package-states/${package}.${git_revision}.${mod_git_revision}.${repository}.locked" ]; then - continue - fi - - # package blocked? - if ! ${hand_out_blocked} && - [ -f "${work_dir}/package-states/${package}.${git_revision}.${mod_git_revision}.${repository}.blocked" ]; then - continue - fi - - # package broken? - if ! ${hand_out_broken} && \ - [ -f "${work_dir}/package-states/${package}.${git_revision}.${mod_git_revision}.${repository}.broken" ]; then - continue - fi - - # dependencies met? - if [ -n "$(find_dependencies_on_build_list "${package}" "${git_revision}" "${mod_git_revision}" "${repository}")" ]; then - # do we hand out looped packages? - if ! ${hand_out_loop}; then - continue - fi - # is it a looped package? - if ! find "${work_dir}/build-list.loops" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' \ - -exec cat {} \; | \ - grep -qxF "${package}"; then - continue - fi - fi - - hand_out_assignment "${package}" "${git_revision}" "${mod_git_revision}" "${repository}" - - done < \ - "${tmp_dir}/try-to-hand-out-these-packages" - -done - -# Remove the lock file - ->&2 echo 'come back later - there are still packages to be built, but currently none has all its dependencies ready' -exit 2 diff --git a/bin/get-package-updates b/bin/get-package-updates index 801aadd..cf2ea6c 100755 --- a/bin/get-package-updates +++ b/bin/get-package-updates @@ -1,6 +1,6 @@ #!/bin/sh -# check for packages that need to be built, and build a list in the proper build order +# check for packages that need to be built # Details: # https://github.com/archlinux32/builder/wiki/Build-system#get-package-updates @@ -9,9 +9,12 @@ # TODO: Find out, why sometimes package updates are missed. -# TODO: mark loops in mysql database +# TODO: read information from database -# TODO: test_exclusion does not yet cooperate with the database +# TODO: correctly handle if pkgbase of a split package is renamed, e.g.: +# $a -> ($a,$b) ==> $b -> ($a,$b) + +# TODO: keep database clean in case of abort # shellcheck disable=SC2016 usage() { @@ -86,57 +89,68 @@ if [ -s "${work_dir}/build-master-sanity" ]; then exit fi +# TODO: How should new deletion-list packages be handled? +# - packages deleted upstream should be marked as to-be-deleted if +# existent and otherwise be ignored +# - packages deleted due to black listing should be marked as black +# listed - and thus as to-be-deleted, but they should remain in the +# database after deletion! + # delete_package package # mark $package for deletion delete_package() { - echo "$1" >> \ - "${work_dir}/deletion-list.new" - sed -i "/^$(str_to_regex "${1}") /d" "${work_dir}/build-list.new" - # shellcheck disable=SC2016 - { - printf 'UPDATE `binary_packages`' - printf ' JOIN `%s` ON `%s`.`id`=`binary_packages`.`%s`' \ - 'repositories' 'repositories' 'repository' \ - 'build_assignments' 'build_assignments' 'build_assignment' - printf ' JOIN `%s` ON `%s`.`id`=`%s`.`%s`' \ - 'package_sources' 'package_sources' 'build_assignments' 'package_source' - printf ' SET `binary_packages`.`repository`=(SELECT `repositories`.`id` FROM `repositories` WHERE `repositories`.`name`="deletion-list")' - printf ' WHERE `repositories`.`name`="build-list"' - printf ' AND `package_sources`.`pkgbase`=from_base64("%s");' \ - "$(printf '%s' "$1" | base64 -w0)" - } | \ - ${mysql_command} + # TODO: Once we want to rely on the database for test_exclusion, we + # need to run the command below unconditionally, but with some + # changes, so we can easily revert. + if [ -z "${test_exclusion}" ]; then + # shellcheck disable=SC2016 + { + # packages from the build-list/to-be-decided go straight to the deletion-list + printf 'UPDATE `binary_packages`' + mysql_join_binary_packages_repositories + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_package_sources + printf ' SET `binary_packages`.`repository`=(SELECT `repositories`.`id` FROM `repositories` WHERE `repositories`.`name`="deletion-list")' + printf ' WHERE `repositories`.`name` in ("build-list","to-be-decided")' + printf ' AND `package_sources`.`pkgbase`=from_base64("%s");\n' \ + "$(printf '%s' "$1" | base64 -w0)" + # other packages are marked as `is_to_be_deleted` + printf 'UPDATE `binary_packages`' + mysql_join_binary_packages_repositories + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_package_sources + printf ' SET `binary_packages`.`is_to_be_deleted`=1' + printf ' WHERE `package_sources`.`pkgbase`=from_base64("%s");' \ + "$(printf '%s' "$1" | base64 -w0)" + } | \ + mysql_run_query + fi } -# create tmp_dir and trap -tmp_dir=$(mktemp -d 'tmp.get-package-updates.XXXXXXXXXX' --tmpdir) -trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT - -# Update git repositories (official packages, community packages and the repository of package customizations). +something_new=false for repo in ${repo_names}; do eval repo_path='"${repo_paths__'"${repo}"'}"' + # Update git repositories (official packages, community packages and the repository of package customizations). git -C "${repo_path}" fetch origin master:master -done - -# Read previous git revision numbers from files. - -something_new=false - -for repo in ${repo_names}; do + # read previous git revision numbers from database. + # shellcheck disable=SC2016 eval "old_repo_revisions__${repo}='$( - cat "${work_dir}/${repo}.revision" 2> /dev/null || \ - echo NONE + { + printf 'SELECT `git_repositories`.`head` FROM `git_repositories`' + printf ' WHERE `git_repositories`.`name`=from_base64("%s");\n' \ + "$(printf '%s' "${repo}" | base64 -w0)" + } | \ + mysql_run_query )'" - eval repo_path='"${repo_paths__'"${repo}"'}"' - eval "new_repo_revisions__${repo}='$( - if ${pull}; then + # determine new git revision + if ${pull}; then + eval "new_repo_revisions__${repo}='$( git -C "${repo_path}" rev-parse HEAD - else - cat "${work_dir}/${repo}.revision" - fi | \ - tee "${work_dir}/${repo}.revision.new" - )'" + )'" + else + eval 'new_repo_revisions__'"${repo}"'="${old_repo_revisions__'"${repo}"'}"' + fi if ! eval '[ "${new_repo_revisions__'"${repo}"'}" = "${old_repo_revisions__'"${repo}"'}" ]'; then something_new=true fi @@ -162,6 +176,11 @@ if ! flock -s ${block_flag} 8; then exit fi +trap mysql_cleanup EXIT + +# shellcheck disable=SC2119 +mysql_cleanup + echo 'Check modified packages from the last update, and put them to the build list.' # Check modified packages from the last update, and put them to the build list. @@ -169,13 +188,6 @@ echo 'Check modified packages from the last update, and put them to the build li # If a package is deleted, remove from the rebuild list, and add it to the deletion list. # If a new package is added, then ensure that it's not on the deletion list. -cp \ - "${work_dir}/build-list" \ - "${work_dir}/build-list.new" -cp \ - "${work_dir}/deletion-list" \ - "${work_dir}/deletion-list.new" - for repo in ${repo_names}; do eval repo_path='"${repo_paths__'"${repo}"'}"' eval old_repo_revision='"${old_repo_revisions__'"${repo}"'}"' @@ -191,26 +203,9 @@ for repo in ${repo_names}; do git -C "${repo_path}" diff --no-renames --name-status "${old_repo_revision}" "${new_repo_revision}" fi } | \ - # Packages which are already on the build list should receive a git_revision bump if _any_ file changed. - # Thus, we rename any file "PKGBUILD" to trigger the successive logic. - if [ "${repo}" = 'archlinux32' ]; then - sed "$( - sed ' - s/ .*$// - s|^|\\@^.\\t[^/]\\+/| - s|$|/@ s@/[^/]*$@/PKGBUILD@| - ' "${work_dir}/build-list" - )" - else - sed "$( - sed ' - s/ .*$// - s|^|\\@^.\\t| - s|$|/@ s@/[^/]*$@/PKGBUILD@| - ' "${work_dir}/build-list" - )" - fi | \ # only track changes in PKGBUILDs + # TODO: However, packages which are already on the build list should + # receive a git_revision bump if _any_ file changed. grep '/PKGBUILD$' | \ if [ "${repo}" = "archlinux32" ]; then # modify the directory structure from the modifiaction-repository @@ -245,24 +240,26 @@ done | \ while read -r mode package git_revision repository; do if [ "${mode}" = 'D' ]; then # deleted PKGBUILD - git_revision=$(cat "${work_dir}/archlinux32.revision.new") + # shellcheck disable=SC2154 + git_revision="${new_repo_revisions__archlinux32}" found_package=false for repository in ${repo_names}; do eval 'repo_path="${repo_paths__'"${repository}"'}"' if [ "${repository}" = "archlinux32" ]; then - if git -C "${repo_path}" archive "$(cat "${work_dir}/${repository}.revision.new")" 2> /dev/null | \ + if git -C "${repo_path}" archive "${new_repo_revisions__archlinux32}" 2> /dev/null | \ tar -t 2> /dev/null | \ grep -q "/$(str_to_regex "${package}")/PKGBUILD$"; then found_package=true break; fi else - if git -C "${repo_path}" archive "$(cat "${work_dir}/${repository}.revision.new")" -- "${package}/repos" 2> /dev/null | \ - tar -t --wildcards "${package}/repos/*/PKGBUILD" 2> /dev/null | \ - cut -d/ -f3 | \ - grep -v 'staging\|testing\|-unstable' | \ - grep -vq -- '-i686$'; then - git_revision=$(cat "${work_dir}/${repository}.revision.new") + # shellcheck disable=SC2154 + if eval 'git -C "${repo_path}" archive "${new_repo_revisions__'"${repository}"'}" -- "${package}/repos" 2> /dev/null | ' \ + 'tar -t --wildcards "${package}/repos/*/PKGBUILD" 2> /dev/null | ' \ + 'cut -d/ -f3 | ' \ + 'grep -v '"'"'staging\|testing\|-unstable'"'"' | ' \ + 'grep -vq -- '"'"'-i686$'"'"; then + eval 'git_revision="${new_repo_revisions__'"${repository}"'}"' found_package=true break; fi @@ -280,31 +277,36 @@ done | \ fi fi if [ "${mode}" = 'A' ] || [ "${mode}" = 'M' ]; then - # new or modified PKGBUILD - sed -i "/^$(str_to_regex "${package}") /d" "${work_dir}/build-list.new" - # shellcheck disable=SC2154 - echo "${package} ${git_revision} ${new_repo_revisions__archlinux32} ${repository}" >> \ - "${work_dir}/build-list.new" - sed -i "/^$(str_to_regex "${package}")\$/d" "${work_dir}/deletion-list.new" # shellcheck disable=SC2016 { # delete old build assignment and associated binary packages - # which are not yet build or on the deletion list + # which are not yet built or on the deletion list printf 'DELETE `build_assignments`,`binary_packages`' printf ' FROM `binary_packages`' - printf ' JOIN `%s` ON `%s`.`id`=`%s`.`%s`' \ - 'build_assignments' 'build_assignments' 'binary_packages' 'build_assignment' \ - 'package_sources' 'package_sources' 'build_assignments' 'package_source' \ - 'repositories' 'repositories' 'binary_packages' 'repository' + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_package_sources + mysql_join_binary_packages_repositories printf ' WHERE `package_sources`.`pkgbase`=from_base64("%s")' \ "$( printf '%s' "${package}" | \ base64 -w0 )" printf ' AND (`repositories`.`name`="build-list" OR `repositories`.`name`="deletion-list");\n' + # remove is-to-be-deleted marker from old binary packages + printf 'UPDATE `binary_packages`' + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_package_sources + mysql_join_binary_packages_repositories + printf ' SET `is_to_be_deleted`=0' + printf ' WHERE `package_sources`.`pkgbase`=from_base64("%s");\n' \ + "$( + printf '%s' "${package}" | \ + base64 -w0 + )" } | \ - ${mysql_command} - mysql_generate_package_metadata "${package}" "${git_revision}" "${new_repo_revisions__archlinux32}" "${repository}" + mysql_run_query + # shellcheck disable=SC2154 + mysql_generate_package_metadata 'to-be-decided' "${package}" "${git_revision}" "${new_repo_revisions__archlinux32}" "${repository}" continue fi @@ -312,21 +314,6 @@ done | \ exit 1 done -sort -u "${work_dir}/deletion-list.new" | \ - sponge "${work_dir}/deletion-list.new" - -echo 'Extract dependencies of packages.' - -# First, we extract the dependencies of each package. - -mkdir -p "${work_dir}/package-infos" - -while read -r package git_revision mod_git_revision repository; do - - generate_package_metadata "${package}" "${git_revision}" "${mod_git_revision}" "${repository}" - -done < "${work_dir}/build-list.new" - echo 'apply blacklisting' # ignore blacklisted packages and dependent packages # this is the first time when all the information is available and up to date @@ -334,249 +321,190 @@ echo 'apply blacklisting' black_listed='' black_listed_new=$( { - git -C "${repo_paths__archlinux32}" archive "$(cat "${work_dir}/archlinux32.revision.new")" -- 'blacklist' | \ - tar -Ox 'blacklist' | \ - sed ' - s/\s*#.*$// - /^\s*$/d - ' - if [ -n "${test_exclusion}" ]; then - echo "${test_exclusion}" - fi - find "${work_dir}/package-infos" -maxdepth 1 -name 'lib32-*' -printf '%f\n' | \ - sed 's|\(\.[^.]\+\)\{4\}$||' | \ - uniq + { + git -C "${repo_paths__archlinux32}" archive "${new_repo_revisions__archlinux32}" -- 'blacklist' | \ + tar -Ox 'blacklist' | \ + sed ' + s/\s*#.*$// + /^\s*$/d + ' + if [ -n "${test_exclusion}" ]; then + echo "${test_exclusion}" + fi + } | \ + base64_encode_each + # shellcheck disable=SC2016 + { + printf 'SELECT DISTINCT replace(to_base64(`package_sources`.`pkgbase`),"\\n","")' + printf ' FROM `package_sources`' + printf ' WHERE `package_sources`.`pkgbase` LIKE "lib32-%%"' + } | \ + mysql_run_query } | \ sort -u ) -find "${work_dir}/package-infos/" -maxdepth 1 -name '*.builds' -printf '%f\n' | \ - grep '\(\.[0-9a-f]\{40\}\)\{2\}\.[^.]\+\.builds$' | \ - sed ' - s|^\(.*\)\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)\.[^.]\+$|\2 \3 \4 \1| - ' | \ - sort -u | \ - sort -k4,4 > \ - "${tmp_dir}/packages" - -{ - uniq -f3 -u "${tmp_dir}/packages" | \ - awk '{print $4 " " $4 "." $1 "." $2 "." $3}' - uniq -f3 -D "${tmp_dir}/packages" | \ - uniq -f3 --group=append | \ - while read -r rev mod_rev repo pkg; do - if [ -z "${rev}" ] && \ - [ -z "${mod_rev}" ] && \ - [ -z "${repo}" ] && \ - [ -z "${pkg}" ]; then - # shellcheck disable=SC2031 - printf '%s %s.%s.%s.%s\n' \ - "${opkg}" \ - "${opkg}" \ - "$( - # shellcheck disable=SC2086 - printf '%s\n' ${revs} | \ - sort -u | \ - find_newest_of_git_revisions - )" \ - "$( - # shellcheck disable=SC2086 - printf '%s\n' ${mod_revs} | \ - sort -u | \ - find_newest_of_git_revisions - )" \ - "${orepo}" | \ - grep '\(\.[0-9a-f]\{40\}\)\{2\}\.[^.]\+$' - revs='' - mod_revs='' - continue - fi - revs=$( - printf '%s\n' \ - ${revs} \ - "${rev}" - ) - mod_revs=$( - printf '%s\n' \ - ${mod_revs} \ - "${mod_rev}" - ) - opkg="${pkg}" - orepo="${repo}" - done -} | \ - sort -k1,1 > \ - "${tmp_dir}/newest-revisions" - -sed ' - s|^\S\+ |'"${work_dir}"'/package-infos/| - s|$|.builds| -' "${tmp_dir}/newest-revisions" | \ - xargs -r cat | \ - sort | \ - uniq -c > \ - "${work_dir}/built-packages" - -sed ' - s|^\S\+ |'"${work_dir}"'/package-infos/| - s|$|.build-depends| -' "${tmp_dir}/newest-revisions" | \ - xargs -r grep -HF '' | \ - sed ' - s|\(\.[^.]\+\)\{4\}:\([^:]\+\)$| \2| - s|^.*/|| - ' | \ - sort -k2,2 > \ - "${work_dir}/newest-dependencies" - while [ -n "${black_listed_new}" ]; do black_listed=$( - # shellcheck disable=SC2086 - printf '%s\n' ${black_listed} ${black_listed_new} | \ + printf '%s\n' "${black_listed}" "${black_listed_new}" | \ + grep -vxF '' | \ sort -u ) black_listed_new=$( { - # shellcheck disable=SC2086 - printf '%s\n' ${black_listed} | \ - sort -k1,1 | \ - join -j1 -o 2.2 - "${tmp_dir}/newest-revisions" | \ - sed ' - s|^|'"${work_dir}"'/package-infos/| - s|$|.builds| - ' | \ - xargs -r cat | \ - sort | \ - uniq -c - cat "${work_dir}/built-packages" + printf '%s\n' "${black_listed}" "${black_listed}" + + # shellcheck disable=SC2016 + { + printf 'CREATE TEMPORARY TABLE `bl` (`pkgbase` VARCHAR(64));\n' + printf 'INSERT INTO `bl` (`pkgbase`) VALUES ' + printf '%s\n' "${black_listed}" | \ + sort -u | \ + sed ' + s/^/(from_base64("/ + s/$/")),/ + $ s/,$/;/ + ' + printf 'SELECT replace(to_base64(`a_ps`.`pkgbase`),"\\n","")' + printf ' FROM `package_sources` AS `a_ps`' + mysql_join_package_sources_build_assignments 'a_ps' 'a_ba' + mysql_join_build_assignments_binary_packages 'a_ba' 'a_bp' + mysql_join_binary_packages_dependencies 'a_bp' + printf ' WHERE NOT EXISTS (' + printf ' SELECT *' + printf ' FROM `install_target_providers`' + printf ' WHERE NOT EXISTS (' + printf ' SELECT *' + printf ' FROM `bl`' + printf ' JOIN `package_sources` AS `b_ps` ON `bl`.`pkgbase`=`b_ps`.`pkgbase`' + mysql_join_package_sources_build_assignments 'b_ps' 'b_ba' + mysql_join_build_assignments_binary_packages 'b_ba' 'b_bp' + printf ' WHERE `install_target_providers`.`package`=`b_bp`.`id`' + printf ')' + printf ' AND `install_target_providers`.`install_target`=`dependencies`.`depending_on`' + printf ') AND EXISTS (' + # TODO: This should be corrected at the root: automatic install targets, which are bogus should + # not be added in the first place - but how do we detect that? + printf ' SELECT *' + printf ' FROM `install_target_providers`' + printf ' WHERE `install_target_providers`.`install_target`=`dependencies`.`depending_on`' + printf ');\n' + } | \ + mysql_run_query | \ + sort -u } | \ - sort | \ - uniq -d | \ - awk '{print $2}' | \ - sort -k1,1 | \ - join -1 1 -2 2 -o 2.1 - "${work_dir}/newest-dependencies" | \ - sort -u - ) - black_listed_new=$( - # shellcheck disable=SC2086 - printf '%s\n' ${black_listed} ${black_listed} ${black_listed_new} | \ + grep -vxF '' | \ sort | \ uniq -u ) done -echo "${black_listed}" | \ - while read -r package; do - [ -n "${package}" ] && \ - delete_package "${package}" - done -sort -u "${work_dir}/deletion-list.new" | \ - sponge "${work_dir}/deletion-list.new" - - -if [ -n "${test_exclusion}" ]; then - # we should not actually update the build-list et. al, but solely print this difference: - if diff --color -u "${work_dir}/deletion-list" "${work_dir}/deletion-list.new"; then - printf 'If you put "%s" on the blacklist, no additional packages will end up on the deletion list.\n' "${test_exclusion}" - fi - exit 0 -fi - -# Now we create the partial order. - -while read -r package git_revision mod_git_revision repository; do - # add "$pkgname -> $build-target" to build-order list - sed "s|^|${package} |" "${work_dir}/package-infos/${package}.${git_revision}.${mod_git_revision}.${repository}.builds" - # add "$dependency -> $pkgname" to build-order list - sed "s|\$| ${package}|" "${work_dir}/package-infos/${package}.${git_revision}.${mod_git_revision}.${repository}.build-depends" - # add "base/base-devel -> $pkgname" to build-order list - printf '%s '"${package}"'\n' 'base' 'base-devel' -done \ - < "${work_dir}/build-list.new" \ - > "${work_dir}/build-order" - -if grep -vq '^\S\+ \S\+$' "${work_dir}/build-order"; then - >&2 echo 'Created partial order file "build-order" is invalid.' - exit 2 -fi - -echo 'Now actually sort it.' - -{ - # this part will have the correct build order, but all the infos are missing - tsort "${work_dir}/build-order" 2> "${work_dir}/tsort.error" | \ - nl -ba | \ - awk '{print $1 " not-git also-not-git whatever " $2}' - # this part has all the infos, but possibly the wrong order - awk '{print "0 " $2 " " $3 " " $4 " " $1}' "${work_dir}/build-list.new" -} | \ - sort -k5,5 -k1nr | \ - # now, we have the correct order and the infos, but in adjacent lines - uniq -f4 -D | \ - sed '/^0 /d;N;s|\n| |' | \ - # now in one line, each - sort -k1n,1 | \ - awk '{print $5 " " $7 " " $8 " " $9}' > \ - "${work_dir}/build-list.new.new" - -rm --one-file-system -rf "${work_dir}/build-list.loops.new" -mkdir "${work_dir}/build-list.loops.new" - -if [ -s "${work_dir}/tsort.error" ]; then - >&2 echo 'WARNING: There is a dependency cycle!' - >&2 cat "${work_dir}/tsort.error" - >&2 echo - >&2 echo 'I will continue anyway.' - # save loops in separate files each, so breaking them is easier - awk ' - /^tsort: \S+: input contains a loop:$/{ - n++; - getline - } - { - print $2 >"'"${work_dir}"'/build-list.loops.new/loop_" n - } - ' "${work_dir}/tsort.error" - - # remove lines from loop files which are no packages - find "${work_dir}/build-list.loops.new" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' \ - -printf '%p\n' | \ - while read -r loop; do - { - sort -u "${loop}" - cut -d' ' -f1 "${work_dir}/build-list.new.new" | \ - sort -u - } | \ - sort | \ - uniq -d | \ - sponge "${loop}" - done -else - rm "${work_dir}/tsort.error" -fi +black_listed=$( + printf '%s\n' "${black_listed}" | \ + while read -r line; do + printf '%s' \ + "${line}" | \ + base64 -d + printf '\n' + done +) deletion_list_count=$( - wc -l < \ - "${work_dir}/deletion-list.new" + # shellcheck disable=SC2086 + printf '%s\n' ${black_listed} | \ + wc -l ) if [ "${deletion_list_count}" -gt 1000 ]; then >&2 printf 'There are %s > 1000 packages on the deletion-list. This does not seem right.\n' \ "${deletion_list_count}" + if [ ! -s "${work_dir}/told-irc-about-too-many-deletion-list-packages" ]; then + { + printf '\001ACTION refuses to blacklist %s packages.\001\n' "${deletion_list_count}" + for repo_name in ${repo_names}; do + eval 'old_revision="${old_repo_revisions__'"${repo_name}"'}"' + eval 'new_revision="${new_repo_revisions__'"${repo_name}"'}"' + # shellcheck disable=SC2154 + if [ "${old_revision}" != "${new_revision}" ]; then + printf '%s: %s -> %s\n' \ + "${repo_name}" \ + "${old_revision}" \ + "${new_revision}" + fi + done + } | \ + tee "${work_dir}/told-irc-about-too-many-deletion-list-packages" | \ + irc_say + fi exit 3 fi +rm -f "${work_dir}/told-irc-about-too-many-deletion-list-packages" -# Move the .new-files to the actual files +echo "${black_listed}" | \ + while read -r package; do + if [ -z "${package}" ]; then + continue + fi + delete_package "${package}" + done + + +if [ -n "${test_exclusion}" ]; then + # TODO: reimplement test_exclusion with information from the database + >&2 echo 'sry, not yet done.' + exit 0 +fi + +echo 'Done - mark decisions as final.' -rm -rf --one-file-system "${work_dir}/build-list.loops" +# shellcheck disable=SC2016 { - printf '%s\n' "build-list.loops" "build-list.new" "build-list" "deletion-list" - # shellcheck disable=SC2086 - printf '%s.revision\n' ${repo_names} -} | \ - while read -r file; do - mv "${work_dir}/${file}.new" "${work_dir}/${file}" + # save blacklist into database + printf 'CREATE TEMPORARY TABLE `bl` (`pkgbase` VARCHAR(64), `reason` TEXT);\n' + git -C "${repo_paths__archlinux32}" archive "${new_repo_revisions__archlinux32}" -- 'blacklist' | \ + tar -Ox 'blacklist' | \ + sed -n ' + s/^\(\S\+\)\s*#\s*/\1 / + T + p + ' | \ + while read -r pkgbase reason; do + printf '(from_base64("%s"),from_base64("%s")),\n' \ + "$(printf '%s' "${pkgbase}" | base64 -w0)" \ + "$(printf '%s' "${reason}" | base64 -w0)" + done | \ + sed ' + 1 i INSERT INTO `bl` (`pkgbase`,`reason`) VALUES + $ s/,$/;/ + ' + printf 'UPDATE `build_assignments`' + mysql_join_build_assignments_package_sources + printf ' LEFT JOIN `bl` ON `package_sources`.`pkgbase`=`bl`.`pkgbase`' + printf ' SET `build_assignments`.`is_black_listed`=`bl`.`reason`;\n' + printf 'DROP TEMPORARY TABLE `bl`;\n' + + # update hashes of repositories in mysql database + for repo in ${repo_names}; do + printf 'UPDATE `git_repositories`' + printf ' SET `git_repositories`.`head`=from_base64("%s")' \ + "$(eval 'printf '"'"'%s'"'"' "${new_repo_revisions__'"${repo}"'}" | base64 -w0')" + printf ' WHERE `git_repositories`.`name`=from_base64("%s");\n' \ + "$(printf '%s' "${repo}" | base64 -w0)" done + # move binary_packages from "to-be-decided" to "build-list" + printf 'UPDATE `binary_packages`' + mysql_join_binary_packages_repositories '' 'from_repo' + printf ' SET `repository`=(' + printf 'SELECT `to_repo`.`id`' + printf ' FROM `repositories` AS `to_repo`' + printf ' WHERE `to_repo`.`name`="build-list"' + printf ')' + printf ' WHERE `from_repo`.`name`="to-be-decided";\n' +} | \ + mysql_run_query + +# update loop list in database (beware, the packages are expected to be in "build-list", +# not "to-be-decided", so we need to run this after moving the packages from "to-be-decided" to the "build-list". +mysql_find_build_assignment_loops # Remove the lock file diff --git a/bin/ii-connect b/bin/ii-connect index 54e1eb0..cf332c0 100755 --- a/bin/ii-connect +++ b/bin/ii-connect @@ -8,7 +8,7 @@ # start ii if it is not running if ! pgrep -x ii > /dev/null; then rm -rf --one-file-system "${irc_dir}" - screen -S ii -d -m ii -f buildmaster -n buildmaster + screen -S ii -d -m ii -s irc.freenode.net -f buildmaster -n buildmaster sleep 10 fi @@ -19,12 +19,12 @@ if tail -n1 "${irc_dir}/nickserv/out" 2> /dev/null | \ sponge "${irc_dir}/nickserv/in" fi -# join #archlinux-ports if not yet done +# join #archlinux32 if not yet done if ! grep ' buildmaster\.archlinux32\.org .* buildmaster$' "${irc_dir}/out" | \ tail -n1 | \ - grep -q ' #archlinux-ports '; then + grep -q ' #archlinux32 '; then { - echo '/j #archlinux-ports' + echo '/j #archlinux32' echo '/WHO buildmaster' } | \ sponge "${irc_dir}/in" @@ -39,7 +39,6 @@ fi if [ "$1" = 'watch' ]; then while pgrep -x 'ii' > /dev/null; do find "${irc_dir}" \ - -regextype sed \ -type f \ -name 'out' \ -printf '%p\n' | \ @@ -51,10 +50,10 @@ if [ "$1" = 'watch' ]; then else prefix='' fi - regex='^\S\+ \S\+ <\S\+> '"${prefix}"'why[- ]don'"'"'\?t[- ]you \(build\|stabilize\|unstage\|keep\|stubbornly_keep\) ' + regex='^\(\S\+ \)\?\S\+ <\S\+> '"${prefix}"'why[- ]don'"'"'\?t[- ]you \(build\|stabilize\|unstage\|keep\|stubbornly_keep\) ' if grep -q "${regex}" "${out_file}"; then sed -n ' - s/'"${regex}"'/\1 / + s/'"${regex}"'/\2 / T p ' "${out_file}" | \ diff --git a/bin/interpret-mail b/bin/interpret-mail index d16c07f..83c8999 100755 --- a/bin/interpret-mail +++ b/bin/interpret-mail @@ -5,6 +5,8 @@ # TODO: enable email interface to delete packages +# TODO: read information from database (?) + if [ $# -ne 0 ]; then >&2 echo '' >&2 echo 'usage: interpret-mail' @@ -21,15 +23,18 @@ if [ $# -ne 0 ]; then >&2 echo ' - "block: <state-file> <reason>":' >&2 echo ' Block the given packge for the given reason.' >&2 echo '' + >&2 echo ' - "copy-to-build-support: <pkgname>":' + >&2 echo ' Copy the given binary package into [build-support].' + >&2 echo '' + >&2 echo ' - "schedule: <pkgbase>":' + >&2 echo ' Put the given package on the build list (again).' + >&2 echo '' >&2 echo ' - "stabilize: <package-file>":' >&2 echo ' Mark the given package as tested.' >&2 echo '' >&2 echo ' - "unblock: <state-file>":' >&2 echo ' Unblock the given packge.' >&2 echo '' - >&2 echo ' - "schedule: <pkgname>":' - >&2 echo ' Put the given package on the build list (again).' - >&2 echo '' >&2 echo ' - ALL: all of the above (only valid in' >&2 echo ' "conf/admin-gpg-keys")' >&2 echo '' @@ -156,14 +161,14 @@ sed -n ' /\n$/!ba s/\n$// p -' "${tmp_dir}/plain-content" | +' "${tmp_dir}/plain-content" | \ sed ' :start_loop $!{ N bstart_loop } - s/=\s*\n//g + s/[=\]\s*\n//g s/:\s*\n/: /g s/\n\(\S\+[^: ]\(\s\|\n\|$\)\)/ \1/g ' > \ @@ -185,7 +190,7 @@ sed -n "$( )" "${tmp_dir}/raw-content" if [ -s "${tmp_dir}/block" ]; then - if run_and_log_on_error "${base_dir}/bin/modify-package-state" --block "${tmp_dir}/block"; then + if run_and_log_on_error "${base_dir}/bin/modify-package-state" --wait --block "${tmp_dir}/block"; then log 'Successfully blocked %s packages.\n' "$(wc -l < "${tmp_dir}/block")" else log 'There was an error while blocking the packages - ignoring this message.\n' @@ -196,7 +201,7 @@ if [ -s "${tmp_dir}/stabilize" ]; then sed -i ' /\.pkg\.tar\.xz$/!s/$/.pkg.tar.xz/ ' "${tmp_dir}/stabilize" - if run_and_log_on_error "${base_dir}/bin/modify-package-state" --tested "${tmp_dir}/stabilize"; then + if run_and_log_on_error "${base_dir}/bin/modify-package-state" --wait --tested "${tmp_dir}/stabilize"; then log 'Successfully marked %s packages as tested.\n' "$(wc -l < "${tmp_dir}/stabilize")" else log 'There was an error while marking the packages as tested - ignoring this message.\n' @@ -204,7 +209,7 @@ if [ -s "${tmp_dir}/stabilize" ]; then fi if [ -s "${tmp_dir}/unblock" ]; then - if run_and_log_on_error "${base_dir}/bin/modify-package-state" --unblock "${tmp_dir}/unblock"; then + if run_and_log_on_error "${base_dir}/bin/modify-package-state" --wait --unblock "${tmp_dir}/unblock"; then log 'Successfully unblocked %s packages.\n' "$(wc -l < "${tmp_dir}/unblock")" else log 'There was an error while unblocking the packages - ignoring this message.\n' @@ -213,7 +218,7 @@ fi if [ -s "${tmp_dir}/schedule" ]; then # shellcheck disable=SC2046 - "${base_dir}/bin/seed-build-list" $( + "${base_dir}/bin/seed-build-list" --wait $( tr '[:space:]' '\n' < \ "${tmp_dir}/schedule" | \ grep -vxF '' | \ @@ -224,3 +229,14 @@ if [ -s "${tmp_dir}/schedule" ]; then sponge "${tmp_dir}/schedule" log 'Successfully (re)scheduled %s packages.\n' "$(wc -l < "${tmp_dir}/schedule")" fi + +if [ -s "${tmp_dir}/copy-to-build-support" ]; then + sed -i ' + /\.pkg\.tar\.xz$/!s/$/.pkg.tar.xz/ + ' "${tmp_dir}/copy-to-build-support" + if run_and_log_on_error "${base_dir}/bin/copy-to-build-support" --wait "${tmp_dir}/copy-to-build-support"; then + log 'Successfully copied %s packages to [build-support].\n' "$(wc -l < "${tmp_dir}/copy-to-build-support")" + else + log 'There was an error while copying the packages to [build-support] - ignoring this message.\n' + fi +fi diff --git a/bin/modify-package-state b/bin/modify-package-state index c740d3d..4a2f5ac 100755 --- a/bin/modify-package-state +++ b/bin/modify-package-state @@ -1,5 +1,7 @@ #!/bin/sh +# shellcheck disable=SC2039 + # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" @@ -16,25 +18,28 @@ usage() { >&2 echo ' -n|--no-report: Do not report what packages were modified.' >&2 echo ' -t|--tested: Mark package(s) as tested.' >&2 echo ' -u|--unblock: Unblock package(s).' + >&2 echo ' -w|--wait: Wait for lock if necessary.' >&2 echo '' >&2 echo 'Exactly one of -b|-t|-u is needed for actual operation.' [ -z "$1" ] && exit 1 || exit "$1" } eval set -- "$( - getopt -o bfhntu \ + getopt -o bfhntuw \ --long block \ --long faulty \ --long help \ --long no-report \ --long tested \ --long unblock \ + --long wait \ -n "$(basename "$0")" -- "$@" || \ echo usage )" action='' report=true +wait_for_lock='-n' while true do @@ -73,6 +78,9 @@ do fi action='unblock' ;; + -w|--wait) + wait_for_lock='' + ;; --) shift break @@ -95,26 +103,19 @@ if [ $# -ne 1 ]; then usage fi -clean_up() { - rm -rf --one-file-system "${tmp_dir}" -} -tmp_dir=$(mktemp -d 'tmp.modify-package-state.XXXXXXXXXX' --tmpdir) -trap clean_up EXIT - input_file="$1" if ${report}; then if ! [ -w "${input_file}" ]; then >&2 printf \ - 'Cannot open file "%s" for writing.' \ + 'Cannot open file "%s" for writing.\n' \ "${input_file}" exit 2 fi move_output() { cat "${output_file}" > "${input_file}" - clean_up + rm -f "${output_file}" } - output_file="${tmp_dir}/output-file" - touch "${output_file}" + output_file=$(mktemp 'tmp.modify-package-state.XXXXXXXXXX') trap 'move_output' EXIT else output_file='/dev/null' @@ -122,91 +123,130 @@ fi if ! [ -r "${input_file}" ]; then >&2 printf \ - 'Cannot open file "%s" for reading.' \ + 'Cannot open file "%s" for reading.\n' \ "${input_file}" exit 2 fi exec 9> "${sanity_check_lock_file}" -if ! flock -s -n 9; then +if ! flock -s ${wait_for_lock} 9; then >&2 echo 'Cannot get sanity-check lock.' exit fi -if [ "${action}" = 'tested' ]; then - receive_buglist 'Testing' > \ - "${tmp_dir}/package-bug-titles" +exec 8> "${package_database_lock_file}" +if ! flock ${wait_for_lock} 8; then + >&2 echo 'Cannot get package-database lock.' + exit fi -{ - err=0 - while read -r package reason; do - if echo "${package}" | \ - grep -q '\.pkg\.tar\.xz$'; then - package=$( - find "${work_dir}/package-states" -maxdepth 1 \( -name '*.tested' -o -name '*.testing' \) \ - -exec grep -qxF "${package}" '{}' \; \ - -printf '%f\n' | \ - sed 's|\.[^.]\+$||' | \ - sort -u +while read -r package reason; do + case "${action}" in + 'faulty'|'tested') + # we expect a binary package identifier (pkgname-epoch:pkgver-pkgrel.sub_pkgrel-arch[".pkg.tar.xz"]) + # and we will only operate on packages in "testing" repositories + # shellcheck disable=SC2016 + combiner=$( + printf '`binary_packages`' + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + mysql_join_binary_packages_architectures ) - if [ -z "${package}" ]; then - continue - fi - fi - if [ "${action}" = 'block' ] || \ - [ "${action}" = 'unblock' ]; then - # these packages need to be on the build list - if ! tr ' ' '.' < \ - "${work_dir}/build-list" | \ - grep -qxF "${package}"; then - >&2 printf 'Package "%s" is not on the build-list.\n' "${package}" - err=2 - continue + # shellcheck disable=SC2016,SC2031 + selector=$( + extract_pkgname_epoch_pkgver_pkgrel_sub_pkgrel_arch_from_package_name "${package}" + printf 'WHERE `repository_stabilities`.`name`="testing"' + printf ' AND `binary_packages`.`%s`=from_base64("%s")' \ + 'pkgname' "$(printf '%s' "${pkgname}" | base64 -w0)" \ + 'epoch' "$(printf '%s' "${epoch}" | base64 -w0)" \ + 'pkgver' "$(printf '%s' "${pkgver}" | base64 -w0)" \ + 'pkgrel' "$(printf '%s' "${pkgrel}" | base64 -w0)" \ + 'sub_pkgrel' "$(printf '%s' "${sub_pkgrel}" | base64 -w0)" + printf ' AND `architectures`.`name`=from_base64("%s")' \ + "$(printf '%s' "${arch}" | base64 -w0)" + ) + ;; + 'block'|'unblock') + # we expect a package source identifier (pkgbase.git_revision.mod_git_revision.repository) + # and we will only operate on packages in "unbuilt" repositories + # shellcheck disable=SC2016 + combiner=$( + printf '`build_assignments`' + mysql_join_build_assignments_package_sources + mysql_join_package_sources_upstream_repositories + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + ) + # shellcheck disable=SC2016 + selector=$( + repository="${package##*.}" + pkgbase="${package%.*}" + mod_git_revision="${pkgbase##*.}" + pkgbase="${pkgbase%.*}" + git_revision="${pkgbase##*.}" + pkgbase="${pkgbase%.*}" + printf 'WHERE `repository_stabilities`.`name`="unbuilt"' + printf ' AND `upstream_repositories`.`name`=from_base64("%s")' \ + "$(printf '%s' "${repository}" | base64 -w0)" + printf ' AND `package_sources`.`%s`=from_base64("%s")' \ + 'pkgbase' "$(printf '%s' "${pkgbase}" | base64 -w0)" \ + 'git_revision' "$(printf '%s' "${git_revision}" | base64 -w0)" \ + 'mod_git_revision' "$(printf '%s' "${mod_git_revision}" | base64 -w0)" + ) + ;; + *) + >&2 printf 'Whooops, action "%s" not implemented yet.\n' "${action}" + exit 42 + ;; + esac + case "${action}" in + 'block') + if [ -z "${reason}" ]; then + >&2 printf 'No reason is given for blocking package "%s".\n' "${package}" + exit 2 fi - fi - case "${action}" in - 'block') - if [ -z "${reason}" ]; then - >&2 printf 'No reason is given for blocking package "%s".\n' "${package}" - err=2 - else - echo "${reason}" > \ - "${work_dir}/package-states/${package}.blocked" - printf '%s %s\n' "${package}" "${reason}" - fi - ;; - 'faulty') - if [ -f "${work_dir}/package-states/${package}.tested" ]; then - mv \ - "${work_dir}/package-states/${package}.tested" \ - "${work_dir}/package-states/${package}.testing" - printf '%s\n' "${package}" - fi - ;; - 'tested') - if [ -f "${work_dir}/package-states/${package}.testing" ] && \ - ! grep -qF "[${package%.*.*.*}]" "${tmp_dir}/package-bug-titles"; then - mv \ - "${work_dir}/package-states/${package}.testing" \ - "${work_dir}/package-states/${package}.tested" - printf '%s\n' "${package}" - fi - ;; - 'unblock') - if [ -f "${work_dir}/package-states/${package}.blocked" ]; then - rm "${work_dir}/package-states/${package}.blocked" - printf '%s\n' "${package}" - fi - ;; - *) - >&2 printf 'Whooops, action "%s" not implemented yet.\n' "${action}" - exit 42 - ;; - esac - done > \ - "${output_file}" - - exit ${err} -} < \ - "${input_file}" + tester='1' + # shellcheck disable=SC2016 + modifier=$( + printf '`build_assignments`.`is_blocked`=from_base64("%s")' \ + "$(printf '%s' "${reason}" | base64 -w0)" + ) + ;; + 'unblock') + # shellcheck disable=SC2016 + tester='NOT `build_assignments`.`is_blocked` IS NULL' + # shellcheck disable=SC2016 + modifier='`build_assignments`.`is_blocked`=NULL' + ;; + 'faulty') + # shellcheck disable=SC2016 + tester='`binary_packages`.`has_issues`=0' + # shellcheck disable=SC2016 + modifier='`binary_packages`.`is_tested`=0,`binary_packages`.`has_issues`=1' + ;; + 'tested') + # shellcheck disable=SC2016 + tester='`binary_packages`.`is_tested`=0' + # shellcheck disable=SC2016 + modifier='`binary_packages`.`is_tested`=1' + ;; + *) + >&2 printf 'Whooops, action "%s" not implemented yet.\n' "${action}" + exit 42 + ;; + esac + if printf 'SELECT 1 FROM %s %s AND %s LIMIT 1' "${combiner}" "${selector}" "${tester}" | \ + mysql_run_query | \ + grep -qxF '1'; then + # shellcheck disable=SC2016 + { + printf 'UPDATE %s SET %s %s' "${combiner}" "${modifier}" "${selector}" + printf ';\n' + } | \ + mysql_run_query + printf '%s %s\n' "${action}" "${package}" + fi +done \ + < "${input_file}" \ + > "${output_file}" diff --git a/bin/mysql-functions b/bin/mysql-functions deleted file mode 100755 index ec2bf4c..0000000 --- a/bin/mysql-functions +++ /dev/null @@ -1,647 +0,0 @@ -#!/bin/sh - -# contains functions used to access mysql db - -# shellcheck disable=SC2039 - -if [ -z "${base_dir}" ]; then - # just to make shellcheck happy - . 'conf/default.conf' -fi - -# base64_encode_each encode each line of stdin with base64 - -base64_encode_each() { - local line - - while read -r line; do - printf '%s' \ - "${line}" | \ - base64 -w0 - printf '\n' - done -} - -# mysql_add_package_source $pkgbase $git_revision $mod_git_revision $upstream_package_repository - -# shellcheck disable=SC2016,SC2086 -mysql_add_package_source() { - local names='pkgbase git_revision mod_git_revision upstream_package_repository' - local values; - for _ in ${names}; do - values="${values}$( - printf '%s' "$1" | \ - base64 -w0 - ) " - shift - done - values="${values% }" - - { - printf 'INSERT IGNORE INTO package_sources' - printf ' (' - printf '`%s`, ' ${names} - printf ') SELECT' - printf ' from_base64("%s"), ' ${values% *} - printf ' `upstream_repositories`.`id`' - printf ' FROM `upstream_repositories`' - printf ' WHERE `upstream_repositories`.`name` = from_base64("%s");' \ - "${values##* }" - } | \ - sed 's|, )|)|g' | \ - ${mysql_command} -} - -# mysql_add_binary_package $pkgbase $git_revision $mod_git_revision $upstream_package_repository $pkgname $sub_pkgrel $architecture $repository - -# shellcheck disable=SC2016,SC2031,SC2086,SC2154 -mysql_add_binary_package() { - local names='pkgbase git_revision mod_git_revision upstream_package_repository pkgname sub_pkgrel architecture repository' - local name - for name in ${names}; do - eval 'local '"${name}" - eval "${name}"'=$( - printf "%s" "$1" | - base64 -w0 - )' - shift - done - - { - printf 'INSERT IGNORE INTO binary_packages' - printf ' (' - printf '`%s`, ' 'sub_pkgrel' 'pkgname' 'package_source' 'repository' 'architecture' - printf ') SELECT' - printf ' from_base64("%s"), ' "${sub_pkgrel}" "${pkgname}" - printf ' `%s`.`id`,' 'package_sources' 'repositories' 'architectures' - printf ' FROM' - printf ' `%s` JOIN' 'package_sources' 'repositories' 'architectures' - printf ' `upstream_repositories` ON `package_sources`.`upstream_package_repository` = `upstream_repositories`.`id`' - printf ' WHERE' - printf ' `%s`.`name` = from_base64("%s") AND' \ - 'repositories' "${repository}" \ - 'architectures' "${architecture}" - printf ' `package_sources`.`%s` = from_base64("%s") AND' \ - 'pkgbase' "${pkgbase}" \ - 'git_revision' "${git_revision}" \ - 'mod_git_revision' "${mod_git_revision}" - printf ' `upstream_repositories`.`name` = from_base64("%s")' \ - "${upstream_package_repository}" - } | \ - sed ' - s|, )|)|g - s|, FROM| FROM|g - ' | \ - ${mysql_command} -} - -# mysql_show_binary_package $pkgname $pkgver $pkgrel $sub_pkgrel - -# shellcheck disable=SC2016,SC2031,SC2086,SC2154 -mysql_show_binary_package() { - local names='pkgname pkgver pkgrel sub_pkgrel' - local name - for name in ${names}; do - eval 'local '"${name}" - eval "${name}"'=$( - printf "%s" "$1" | - base64 -w0 - )' - shift - done - - local joint - { - printf 'SELECT' - printf ' `%s`.`%s`,' \ - 'repositories' 'name' \ - 'binary_packages' 'pkgname' \ - 'package_sources' 'pkgver' \ - 'package_sources' 'pkgrel' \ - 'binary_packages' 'sub_pkgrel' \ - 'architectures' 'name' \ - 'package_sources' 'pkgbase' \ - 'package_sources' 'git_revision' \ - 'package_sources' 'mod_git_revision' \ - 'upstream_repositories' 'name' - printf ' FROM `binary_packages`' - for joint in \ - 'architectures:binary_packages:architecture' \ - 'package_sources:binary_packages:package_source' \ - 'repositories:binary_packages:repository' \ - 'upstream_repositories:package_sources:upstream_package_repository'; do - printf ' JOIN `%s` ON `%s`.`id` =' \ - "${joint%%:*}" "${joint%%:*}" - joint="${joint#*:}" - printf ' `%s`.`%s`' \ - "${joint%:*}" "${joint#*:}" - done - printf ' WHERE' - printf ' `%s`.`%s` = from_base64("%s") AND' \ - 'binary_packages' 'pkgname' "${pkgname}" \ - 'binary_packages' 'sub_pkgrel' "${sub_pkgrel}" \ - 'package_sources' 'pkgver' "${pkgver}" \ - 'package_sources' 'pkgrel' "${pkgrel}" - printf ';' - } | \ - sed ' - s|, FROM| FROM|g - s|AND;|;|g - ' | \ - ${mysql_command} --html -} - -# mysql_add_install_target $install_target - -# shellcheck disable=SC2016,2086 -mysql_add_install_target() { - local install_target - install_target=$( - printf "%s" "$1" | \ - base64 -w0 - ) - - { - printf 'INSERT IGNORE INTO `install_targets` (`name`)' - printf ' VALUES (from_base64("%s"))' \ - "${install_target}" - } | \ - ${mysql_command} -} - -# mysql_generate_package_metadata $package $git_revision $mod_git_revision $repository -# or -# mysql_generate_package_metadata $package.$git_revision.$mod_git_revision.$repository -# if sub_pkgrel should be determined automatically and the package is on the build-list -# and -# mysql_generate_package_metadata $sub_pkgrel $package $current_repository $git_revision $mod_git_revision $repository -# or -# mysql_generate_package_metadata $sub_pkgrel $current_repository $package.$git_revision.$mod_git_revision.$repository -# if $sub_pkgrel should be forced and the package is currently in $current_repository -# generate the meta data of a package (dependencies, built packages, ...) in the database - -# shellcheck disable=SC2016 -mysql_generate_package_metadata() { - - ( # new shell is intentional - case "$1" in - ''|*[!0-9]*) - unset forced_sub_pkgrel - current_repository=$( - printf 'build-list' | \ - base64 -w0 - ) - ;; - *) - forced_sub_pkgrel=$( - printf '%s' "$1" | \ - base64 -w0 - ) - shift - current_repository=$( - printf '%s' "$1" | \ - base64 -w0 - ) - shift - ;; - esac - package="$1" - git_revision="$2" - mod_git_revision="$3" - repository="$4" - temp_dir=$(mktemp -d 'tmp.mysql-functions.mysql_generate_package_metadata.XXXXXXXXXX' --tmpdir) - trap 'rm -rf --one-file-system "${temp_dir}"' EXIT - - if [ $# -eq 1 ]; then - # second form - repository="${package##*.}" - package="${package%.*}" - mod_git_revision="${package##*.}" - package="${package%.*}" - git_revision="${package##*.}" - package="${package%.*}" - fi - - printf '.' >&2 - if ! make_source_info "${package}" "${repository}" "${git_revision}" "${mod_git_revision}" "${temp_dir}/SRCINFO"; then - printf '"make_source_info %s %s %s %s %s" failed.\n' "${package}" "${repository}" "${git_revision}" "${mod_git_revision}" "${temp_dir}/SRCINFO" - exit 2 - fi - # remove empty lines and unsupported architectures - sed -i ' - /^[^=]*=\s*$/d - /^\s*arch = /{ - / \(i686\|any\)$/!d - } - ' "${temp_dir}/SRCINFO" - - if [ ! -s "${temp_dir}/SRCINFO" ]; then - >&2 printf '"make_source_info" had empty output - eh, what?\n' - exit 2 - fi - printf '\n\n' >> "${temp_dir}/SRCINFO" - - printf '.' >&2 - pkgbase=$( - grep '^pkgbase = ' "${temp_dir}/SRCINFO" | \ - cut -d' ' -f3 - ) - if [ -z "${pkgbase}" ]; then - >&2 printf '"make_source_info" did not return a "pkgbase" - eh, what?\n' - exit 2 - fi - - # add the package source - mysql_add_package_source "${pkgbase}" "${git_revision}" "${mod_git_revision}" "${repository}" - printf '.' >&2 - - # now we encode everything in base64 - pkgbase=$( - printf '%s' "${pkgbase}" | \ - base64 -w0 - ) - git_revision=$( - printf '%s' "${git_revision}" | \ - base64 -w0 - ) - mod_git_revision=$( - printf '%s' "${mod_git_revision}" | \ - base64 -w0 - ) - repository=$( - printf '%s' "${repository}" | \ - base64 -w0 - ) - - # add the build assignment(s) - { - archs=$( - sed -n ' - s/^\tarch = // - T - p - ' "${temp_dir}/SRCINFO" | \ - grep -vxF 'any' | \ - sort -u - ) - if [ -z "${archs}" ]; then - echo 'any' - else - printf '%s\n' "${archs}" - fi - } | \ - while read -r arch; do - printf 'INSERT IGNORE INTO `build_assignments` (`package_source`,`architecture`,`is_blocked`)' - printf ' SELECT `package_sources`.`id`,`architectures`.`id`,NULL' - printf ' FROM `architectures` JOIN `package_sources`' - printf ' WHERE `architectures`.`name` = from_base64("%s")' \ - "$( - printf '%s' "${arch}" | \ - base64 -w0 - )" - printf ' AND `package_sources`.`%s` = from_base64("%s")' \ - 'pkgbase' "${pkgbase}" \ - 'git_revision' "${git_revision}" \ - 'mod_git_revision' "${mod_git_revision}" - printf ';\n' - done > \ - "${temp_dir}/add-build-assignments-command" - - # TODO: correctly link between binary_packages and build_assignments using any_arch - - # shellcheck disable=SC2034 - # select any specific arch (which will be building the 'any' part of a split package) - any_arch=$( - { - sed -n ' - s/^\tarch = // - T - p - ' "${temp_dir}/SRCINFO" | \ - sort -r | \ - grep -vxFm 1 'any' || \ - echo 'any' - } | \ - base64_encode_each - ) - - grep '^pkgname = ' "${temp_dir}/SRCINFO" | \ - cut -d' ' -f3 | \ - while read -r pkgname; do - pkgname64=$( - printf '%s' "${pkgname}" | \ - base64 -w0 - ) - sed -n ' - /^pkgbase = \|^pkgname = '"$(str_to_regex "${pkgname}")"'$/{ - :a - N - /\n$/{ - p - T - } - ba - } - ' "${temp_dir}/SRCINFO" | \ - sed ' - /^\S/d - s/^\s*// - ' > \ - "${temp_dir}/BINARYINFO.${pkgname64}" - - grep '^arch = ' "${temp_dir}/BINARYINFO.${pkgname64}" | \ - cut -d' ' -f3 | \ - while read -r arch; do - arch64=$( - printf '%s' "${arch}" | \ - base64 -w0 - ) - sed ' - s/^\(\S\+\)_'"${arch}"' = /\1 = / - ' "${temp_dir}/BINARYINFO.${pkgname64}" > \ - "${temp_dir}/ARCHINFO ${pkgname64} ${arch64}" - done - done - find "${temp_dir}" -mindepth 1 -maxdepth 1 -name 'ARCHINFO * *' -printf '%f\n' | \ - while read -r _ pkgname arch; do - pkgver=$( - grep '^pkgver = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ - cut -d' ' -f3 | \ - base64_encode_each - ) - pkgrel=$( - grep '^pkgrel = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ - cut -d' ' -f3 | \ - base64_encode_each - ) - epoch=$( - { - grep '^epoch = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" || \ - echo 'epoch = 0' - } | \ - cut -d' ' -f3 | \ - base64_encode_each - ) - provides=$( - grep '^\(groups\|provides\) = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ - cut -d' ' -f3 | \ - sed 's/[<>=].*$//' | \ - base64_encode_each - ) - builddepends=$( - grep '^\(checkdepends\|makedepends\) = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ - cut -d' ' -f3 | \ - sed 's/[<>=].*$//' | \ - base64_encode_each - ) - rundepends=$( - grep '^depends = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ - cut -d' ' -f3 | \ - sed 's/[<>=].*$//' | \ - base64_encode_each - ) - if [ -n "${forced_sub_pkgrel}" ]; then - sub_pkgrel='from_base64("'"${forced_sub_pkgrel}"'")' - else - sub_pkgrel=$( - printf '(SELECT COALESCE(' - # do not add binary packages which are currently on the build-list - printf '(SELECT `sub_pkgrel` FROM `binary_packages`' - printf ' JOIN `architectures` ON `binary_packages`.`architecture`=`architectures`.`id`' - printf ' JOIN `repositories` ON `binary_packages`.`repository`=`repositories`.`id`' - printf ' WHERE' - printf ' `binary_packages`.`%s`=from_base64("%s") AND' \ - 'epoch' "${epoch}" \ - 'pkgver' "${pkgver}" \ - 'pkgrel' "${pkgrel}" \ - 'pkgname' "${pkgname}" - printf ' `architectures`.`name`=from_base64("%s")' \ - "${arch}" - printf ' AND `repositories`.`name`="build-list"),' - # max(sub_pkgrel)+1 - printf '(SELECT 1+MAX(`binary_packages`.`sub_pkgrel`) FROM `binary_packages`' - printf ' JOIN `architectures` ON `binary_packages`.`architecture`=`architectures`.`id`' - printf ' WHERE' - printf ' `binary_packages`.`%s`=from_base64("%s") AND' \ - 'epoch' "${epoch}" \ - 'pkgver' "${pkgver}" \ - 'pkgrel' "${pkgrel}" \ - 'pkgname' "${pkgname}" - if printf '%s' "${arch}" | base64 -d | grep -qxF 'any'; then - # 'any' gets higher sub_pkgrel than any architecture - printf ' 1' - else - # not-'any' gets higher sub_pkgrel than same or 'any' architecture - printf ' (`architectures`.`name`=from_base64("%s") OR `architectures`.`name`="any")' \ - "${arch}" - fi - printf ')' - printf ',0))' - ) - fi - { - printf 'INSERT IGNORE INTO `binary_packages` (' - printf '`%s`,' \ - 'build_assignment' \ - 'repository' \ - 'architecture' \ - 'epoch' \ - 'pkgver' \ - 'pkgrel' \ - 'pkgname' \ - 'sub_pkgrel' \ - 'has_issues' \ - 'is_tested' - printf ') SELECT ' - printf '`%s`.`id`,' \ - 'build_assignments' \ - 'repositories' \ - 'architectures' - printf 'from_base64("%s"),' \ - "${epoch}" \ - "${pkgver}" \ - "${pkgrel}" \ - "${pkgname}" - printf '%s,0,0 FROM' \ - "${sub_pkgrel}" - printf ' `%s` JOIN' \ - 'repositories' \ - 'architectures' \ - 'build_assignments' \ - 'package_sources' - printf ' ON `build_assignments`.`package_source` = `package_sources`.`id`' - printf ' JOIN `upstream_repositories`' - printf ' ON `package_sources`.`upstream_package_repository` = `upstream_repositories`.`id`' - printf ' WHERE' - printf ' `%s`.`%s` = from_base64("%s") AND' \ - 'repositories' 'name' "${current_repository}" \ - 'architectures' 'name' "${arch}" \ - 'package_sources' 'pkgbase' "${pkgbase}" \ - 'package_sources' 'git_revision' "${git_revision}" \ - 'package_sources' 'mod_git_revision' "${mod_git_revision}" \ - 'upstream_repositories' 'name' "${repository}" - printf ';\n' - } | \ - sed ' - s|,)|)|g - s|JOIN ON|ON|g - s| AND;$|;| - ' >> \ - "${temp_dir}/add-binary-packages-command" - { - printf 'CREATE TEMPORARY TABLE `%s` (`name` VARCHAR(64));\n' \ - 'provides' \ - 'builddepends' \ - 'rundepends' - - printf 'INSERT INTO `provides` VALUES\n' - echo "${provides}" | \ - sort -u | \ - grep -vxF '' | \ - sed ' - s|^| (from_base64("| - s|$|")),| - ' - printf ' (from_base64("%s"));\n' \ - "${pkgname}" - - printf 'INSERT INTO `rundepends` VALUES\n' - echo "${rundepends}" | \ - sort -u | \ - grep -vxF '' | \ - sed ' - s|^| (from_base64("| - s|$|")),| - ' - printf ' ("base");\n' - - printf 'INSERT INTO `builddepends` VALUES\n' - echo "${builddepends}" | \ - sort -u | \ - grep -vxF '' | \ - sed ' - s|^| (from_base64("| - s|$|")),| - ' - printf ' ("base-devel");\n' - - printf 'INSERT IGNORE INTO `install_targets` (`name`)' - printf ' SELECT (`name`) FROM `%s` UNION' \ - 'provides' \ - 'builddepends' \ - 'rundepends' | \ - sed 's| UNION$|;\n|' - - for link in 'provides' 'builddepends' 'rundepends'; do - case "${link}" in - 'provides') - printf 'INSERT IGNORE INTO `install_target_providers` (`package`,`install_target`) SELECT' - printf ' `binary_packages`.`id`,`install_targets`.`id` FROM' - ;; - 'builddepends'|'rundepends') - printf 'INSERT IGNORE INTO `dependencies` (`dependent`,`depending_on`,`dependency_type`) SELECT' - printf ' `binary_packages`.`id`,`install_targets`.`id`,`dependency_types`.`id` FROM' - printf ' `dependency_types` JOIN' - ;; - esac - printf ' `binary_packages`' - printf ' JOIN `architectures`' - printf ' ON `binary_packages`.`architecture` = `architectures`.`id`' - printf ' JOIN `repositories`' - printf ' ON `binary_packages`.`repository` = `repositories`.`id`' - printf ' JOIN `install_targets`' - printf ' JOIN `%s`' "${link}" - printf ' ON `%s`.`name` = `install_targets`.`name`' "${link}" - printf ' WHERE' - if [ "${link}" = 'builddepends' ] || \ - [ "${link}" = 'rundepends' ]; then - printf ' `dependency_types`.`name` = "%s" AND' \ - "${link%depends}" - fi - printf ' `binary_packages`.`%s` = from_base64("%s") AND' \ - 'epoch' "${epoch}" \ - 'pkgver' "${pkgver}" \ - 'pkgrel' "${pkgrel}" \ - 'pkgname' "${pkgname}" - printf ' `binary_packages`.`sub_pkgrel` = %s AND' \ - "${sub_pkgrel}" - printf ' `architectures`.`name` = from_base64("%s") AND' \ - "${arch}" - printf ' `repositories`.`name` = from_base64("%s");\n' \ - "${current_repository}" - done - - printf 'DROP TABLE `%s`;\n' \ - 'provides' \ - 'builddepends' \ - 'rundepends' - } >> \ - "${temp_dir}/add-install-targets-command" - done - printf '.' >&2 - - { - if [ -s "${temp_dir}/add-build-assignments-command" ]; then - cat "${temp_dir}/add-build-assignments-command" - fi - if [ -s "${temp_dir}/add-binary-packages-command" ]; then - cat "${temp_dir}/add-binary-packages-command" - fi - if [ -s "${temp_dir}/add-install-targets-command" ]; then - cat "${temp_dir}/add-install-targets-command" - fi - } | \ - ${mysql_command} - printf '.' >&2 - - ) -} - -# mysql_sanity_check -# do a sanity check on the mysql database - -mysql_sanity_check() { - ( # new shell is intentional - temp_dir=$(mktemp -d 'tmp.mysql-functions.mysql_sanity_check.XXXXXXXXXX' --tmpdir) - trap 'rm -rf --one-file-system "${temp_dir}"' EXIT - - for dir in $(ls_master_mirror 'i686'); do - ls_master_mirror "i686/${dir}" | \ - sed -n ' - s/\.pkg\.tar\.xz$// - T - s/-\([0-9]\+\)-\([^-]\+\)$/-\1.0-\2/ - s/-\([^-:]\+-[^-]\+-[^-]\+\)$/-0:\1/ - s|^|'"${dir}"'/| - p - ' - done | \ - sort > \ - "${temp_dir}/master-mirror-listing" - - # shellcheck disable=SC2016 - { - printf 'SELECT `repositories`.`name`,`pkgname`,`epoch`,`pkgver`,`pkgrel`,`sub_pkgrel`,`architectures`.`name`' - printf ' FROM `binary_packages`' - printf ' JOIN `architectures` ON `binary_packages`.`architecture`=`architectures`.`id`' - printf ' JOIN `repositories` ON `binary_packages`.`repository`=`repositories`.`id`' - printf ' WHERE `repositories`.`is_on_master_mirror`' - } | \ - ${mysql_command} --batch | \ - sed ' - 1d - s,\t,/, - s,\t,-, - s,\t,:, - s,\t,-, - s,\t,., - s,\t,-, - ' | \ - sort > \ - "${temp_dir}/mysql-packages" - - diff -u \ - "${temp_dir}/master-mirror-listing" \ - "${temp_dir}/mysql-packages" - ) -} diff --git a/bin/ping-from-slave b/bin/ping-from-slave new file mode 100755 index 0000000..afaf018 --- /dev/null +++ b/bin/ping-from-slave @@ -0,0 +1,25 @@ +#!/bin/sh + +# should be called periodically on the build-master from the slaves to +# - report any update on the build process +# - show that the build is still running +# - get notified by the build master if the build is not necessary anymore + +# shellcheck source=conf/default.conf +. "${0%/*}/../conf/default.conf" + +# TODO: receive/save some statistics about current build + +if [ "$( + # shellcheck disable=SC2016,SC2154 + { + printf 'SELECT count(*) FROM `build_slaves`' + mysql_join_build_slaves_build_assignments + printf ' WHERE `build_slaves`.`name`=from_base64("%s");\n' \ + "$(printf '%s' "${slave}" | base64 -w0)" + } | \ + mysql_run_query + )" -ne 1 ]; then + >&2 echo 'You do not build anything currently - abort whatever you are doing.' + exit 2 +fi diff --git a/bin/ping-to-master b/bin/ping-to-master new file mode 100755 index 0000000..15c9120 --- /dev/null +++ b/bin/ping-to-master @@ -0,0 +1,39 @@ +#!/bin/sh + +# periodically connects to the buildmaster to call ping-from slave to: +# - report any update on the build process +# - show that the build is still running +# - get notified by the build master if the build is not necessary anymore + +# shellcheck source=conf/default.conf +. "${0%/*}/../conf/default.conf" + +# TODO: abort build if requested to + +parent_pid="$1" +parent_tmp_dir="$2" + +exec 9> "${work_dir}/ping-build-master.lock" + +while kill -0 "${parent_pid}" && \ + [ -f "${parent_tmp_dir}/.ping-build-master" ]; do + + # shellcheck disable=SC2029 + find "${parent_tmp_dir}" \ + -xdev \ + -type f \ + -name '*.build-log' \ + -exec wc -l {} \; | \ + sed 's, .*/, ,' | \ + ssh \ + -i "${master_build_server_identity}" \ + -p "${master_build_server_port}" \ + "${master_build_server_user}@${master_build_server}" \ + 'ping-from-slave' "$(cat "${parent_tmp_dir}/.ping-build-master")" + + # we wait upto 60 seconds to get the lock - if we get it, the parent + # must have released it and we're finished + if flock -w 60 9; then + break + fi +done diff --git a/bin/prioritize-build-list b/bin/prioritize-build-list index 06d4c06..db828ae 100755 --- a/bin/prioritize-build-list +++ b/bin/prioritize-build-list @@ -23,11 +23,24 @@ if ! flock -s -n 8; then exit 1 fi +# shellcheck disable=SC2016 { - sed -n "/^$1/p" "${work_dir}/build-list" - sed "/^$1/d" "${work_dir}/build-list" + printf 'UPDATE `build_assignments`' + mysql_join_build_assignments_package_sources + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' SET `build_assignments`.`priority`=(' + printf 'SELECT COALESCE(MAX(`all_priorities`.`priority`),0)+1' + printf ' FROM (' + printf 'SELECT `others`.`priority`' + printf ' FROM `build_assignments` AS `others`' + printf ') AS `all_priorities`' + printf ')' + printf ' WHERE `package_sources`.`pkgbase` REGEXP from_base64("%s")' \ + "$(printf '%s' "$1" | base64 -w0)" + printf ' AND `repositories`.`name`="build-list";\n' } | \ - sponge "${work_dir}/build-list" + mysql_run_query # Remove the lock file diff --git a/bin/repo-copy b/bin/repo-copy new file mode 100755 index 0000000..c3450fd --- /dev/null +++ b/bin/repo-copy @@ -0,0 +1,92 @@ +#!/bin/sh + +# Rudimentary copy a package from one repository to another. +# Note, that we do _not_ need to have the package itself, since all +# relevant information is already in the original package database. + +# "Rudimentary" means the following restrictions: +# - no arguments are accepted +# - no database signatures are handled +# - only *.db.tar.gz and *.files.tar.gz are recognized as database + +usage() { + >&2 echo 'usage:' + >&2 echo ' repo-copy from-repo.db.tar.gz to-repo.db.tar.gz package1 package2 ...' + >&2 echo + >&2 echo 'Note, that the packages must be given with version, e.g. "linux-4.15.7-1.0".' + exit 2 +} + +if [ $# -le 2 ]; then + usage +fi + +from_repo="$1" +to_repo="$2" +shift +shift + +tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/repo-tools.XXXXXXXXXX") +trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT + +# extract the databases +for repo in 'from' 'to'; do + for archive in 'db' 'files'; do + eval 'repo_db="${'"${repo}"'_repo}"' + if [ "${repo_db}" = "${repo_db%.db.tar.gz}" ]; then + >&2 printf '"%s" has an invalid suffix.\n' "${repo_db}" + usage + fi + if [ "${archive}" = 'files' ]; then + repo_db="${repo_db%.db.tar.gz}.files.tar.gz" + fi + if [ ! -f "${repo_db}" ]; then + >&2 printf 'Cannot open file "%s".\n' "${repo_db}" + usage + fi + mkdir "${tmp_dir}/${repo}.${archive}" + bsdtar -C "${tmp_dir}/${repo}.${archive}" -xf "${repo_db}" + done +done + +# move the packages +for package in "$@"; do + errors=$( + find "${tmp_dir}/to.db" "${tmp_dir}/to.files" -mindepth 1 -maxdepth 1 \ + -printf '%f\n' | \ + sed 's/-[^-]\+-[^-]\+$//' | \ + grep -xF "${package%-*-*}" + ) + if [ -n "${errors}" ]; then + >&2 printf 'The target repository "%s" already contains the following packages - "repo-remove" them first:\n' \ + "${to_repo}" + >&2 printf '%s\n' "${errors}" + exit 2 + fi + for archive in 'db' 'files'; do + if [ ! -d "${tmp_dir}/from.${archive}/${package}" ]; then + >&2 printf 'Repository "%s" does not contain package "%s"\n' \ + "${from_repo}" "${package}" + exit 2 + fi + mv "${tmp_dir}/from.${archive}/${package}" "${tmp_dir}/to.${archive}/" + done +done + +# pack the database +for archive in 'db' 'files'; do + repo_db="${to_repo}" + if [ "${archive}" = 'files' ]; then + repo_db="${repo_db%.db.tar.gz}.files.tar.gz" + fi + bsdtar -C "${tmp_dir}" -czf "${tmp_dir}/${repo_db##*/}" --strip-components=1 "to.${archive}" +done + +# move the database in place +for archive in 'db' 'files'; do + repo_db="${to_repo}" + if [ "${archive}" = 'files' ]; then + repo_db="${repo_db%.db.tar.gz}.files.tar.gz" + fi + mv "${tmp_dir}/${repo_db##*/}" "${repo_db}" +done diff --git a/bin/return-assignment b/bin/return-assignment index cd6d30e..89a1f85 100755 --- a/bin/return-assignment +++ b/bin/return-assignment @@ -47,17 +47,37 @@ trap clean_up_lock_file EXIT if [ "$5" = 'ERROR' ]; then # the build failed on the build slave - if ! grep -qxF "$1 $2 $3 $4" "${work_dir}/build-list" || - ! [ -f "${work_dir}/package-states/$1.$2.$3.$4.locked" ]; then - >&2 echo 'Too late, package already outdated, I ignore this error report.' - exit 2 - fi - - # shellcheck disable=SC2154 - if ! grep -qxF "${slave}" "${work_dir}/package-states/$1.$2.$3.$4.locked"; then - >&2 echo 'You do not build this package - move on.' + # shellcheck disable=SC2016 + infos=$( + { + printf 'SELECT DISTINCT `build_assignments`.`id`,IF(`build_assignments`.`is_broken`,"true","false") FROM `build_slaves`' + mysql_join_build_slaves_build_assignments + mysql_join_build_assignments_package_sources + mysql_join_package_sources_upstream_repositories + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE `build_slaves`.`name`=from_base64("%s")' \ + "$( + # shellcheck disable=SC2154 + printf '%s' "${slave}" | \ + base64 -w0 + )" + printf ' AND `package_sources`.`%s`=from_base64("%s")' \ + 'pkgbase' "$(printf '%s' "$1" | base64 -w0)" \ + 'git_revision' "$(printf '%s' "$2" | base64 -w0)" \ + 'mod_git_revision' "$(printf '%s' "$3" | base64 -w0)" + printf ' AND `upstream_repositories`.`name`=from_base64("%s")' \ + "$(printf '%s' "$4" | base64 -w0)" + printf ' AND `repositories`.`name`="build-list"' + } | \ + mysql_run_query | \ + tr '\t' ' ' + ) + if [ -z "${infos}" ]; then + >&2 echo 'You do not build this package (anymore) - move on.' exit 2 fi + was_broken_before="${infos##* }" # save sent build logs saved_build_logs=$( @@ -69,15 +89,6 @@ if [ "$5" = 'ERROR' ]; then '*.build-log.gz' ) - if [ -f "${work_dir}/package-states/$1.$2.$3.$4.broken" ]; then - was_broken_before=true - else - was_broken_before=false - fi - - # shellcheck disable=SC2154 - echo "${slave}" >> \ - "${work_dir}/package-states/$1.$2.$3.$4.broken" # shellcheck disable=SC2016 { if [ -n "${saved_build_logs}" ]; then @@ -94,8 +105,7 @@ if [ "$5" = 'ERROR' ]; then printf 'SELECT `fail_reasons`.`id`,replace(to_base64(`fail_reasons`.`identifier`),"\\n","")' printf ' FROM `fail_reasons` ORDER BY `fail_reasons`.`severity`' } | \ - ${mysql_command} --raw --batch | \ - sed '1d' + mysql_run_query ) for saved_build_log in ${saved_build_logs}; do printf '%s' "${fail_reason_identifiers}" | \ @@ -139,7 +149,7 @@ if [ "$5" = 'ERROR' ]; then printf 'DROP TABLE `failures`;\n' fi printf 'UPDATE `build_assignments`' - printf ' JOIN `build_slaves` ON `build_slaves`.`currently_building`=`build_assignments`.`id`' + mysql_join_build_assignments_build_slaves printf ' SET `build_assignments`.`is_broken`=1, `build_slaves`.`currently_building`=NULL' printf ' WHERE `build_slaves`.`name`=from_base64("%s");\n' \ "$( @@ -147,36 +157,7 @@ if [ "$5" = 'ERROR' ]; then base64 -w0 )" } | \ - ${mysql_command} - - # shellcheck disable=SC2154 - sed -i ' - /^'"$(str_to_regex "${slave}")"'$/d - ' "${work_dir}/package-states/$1.$2.$3.$4.locked" - if [ ! -s "${work_dir}/package-states/$1.$2.$3.$4.locked" ]; then - rm "${work_dir}/package-states/$1.$2.$3.$4.locked" - - # unlock every loop this package would have broken and which is not - # broken by another locked package - locked_packages=$( - find "${work_dir}/package-states/" -maxdepth 1 -name '*.locked' -printf '%f\n' | \ - sed 's@^\(.\+\)\.\([0-9a-f]\{40\}\.\)\{2\}[^.]\+\.locked$@\1@' - ) - find "${work_dir}/build-list.loops" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' \ - -exec grep -qxF "$1" '{}' \; \ - -not -exec grep -qxF "${locked_packages}" '{}' \; \ - -exec rm '{}.locked' \; - - # move that build order to the end of the build-list - sed -i ' - /^'"$(str_to_regex "$1 $2 $3 $4")"'$/ { - $ b - d - } - $ a '"$1 $2 $3 $4" \ - "${work_dir}/build-list" - fi + mysql_run_query # release lock on build-list - otherwise seed-build-list won't run flock -u 9 @@ -195,7 +176,7 @@ if [ "$5" = 'ERROR' ]; then ' | \ tr ' ' '\n' | \ sed ' - s/^/-p ^haskell-/ + s/^/-p ^(haskell-)?/ s/-[0-9.]\+$/\$/ ' | \ sort -u @@ -209,21 +190,19 @@ if [ "$5" = 'ERROR' ]; then fi ) - if [ -p "${irc_dir}/#archlinux-ports/in" ]; then - { - printf '%s is broken (says %s).' \ - "$1" \ - "${slave}" - if [ -n "${rescheduled_packages}" ]; then - printf -- ' - I rescheduled:' - # shellcheck disable=SC2086 - printf ' %s,' ${rescheduled_packages} | \ - sed 's/,$/./' - fi - printf '\n' - } | \ - sponge "${irc_dir}/#archlinux-ports/in" - fi + { + printf '%s is broken (says %s).' \ + "$1" \ + "${slave}" + if [ -n "${rescheduled_packages}" ]; then + printf -- ' - I rescheduled:' + # shellcheck disable=SC2086 + printf ' %s,' ${rescheduled_packages} | \ + sed 's/,$/./' + fi + printf '\n' + } | \ + irc_say fi exit 0 @@ -245,19 +224,33 @@ clean_up_lock_file() { rm -f "${package_database_lock_file}" } -if ! grep -qxF "$1 $2 $3 $4" "${work_dir}/build-list" || - ! [ -f "${work_dir}/package-states/$1.$2.$3.$4.locked" ] || - ! [ "$5" = "$(next_sub_pkgrel "$1" "$2" "$3" "$4")" ]; then +# shellcheck disable=SC2016 +if ! { + printf 'SELECT count(*)' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_build_slaves + mysql_join_binary_packages_repositories + mysql_join_build_assignments_package_sources + mysql_join_package_sources_upstream_repositories + printf ' WHERE `repositories`.`name`="build-list"' + printf ' AND `build_slaves`.`name`=from_base64("%s")' \ + "$(printf '%s' "${slave}" | base64 -w0)" + printf ' AND `package_sources`.`%s`=from_base64("%s")' \ + 'pkgbase' "$(printf '%s' "$1" | base64 -w0)" \ + 'git_revision' "$(printf '%s' "$2" | base64 -w0)" \ + 'mod_git_revision' "$(printf '%s' "$3" | base64 -w0)" + printf ' AND `upstream_repositories`.`name`=from_base64("%s")' \ + "$(printf '%s' "$4" | base64 -w0)" + printf ' AND `binary_packages`.`sub_pkgrel`=from_base64("%s");\n' \ + "$(printf '%s' "$5" | base64 -w0)" + } | \ + mysql_run_query | \ + grep -qvxF '0'; then >&2 echo 'Sorry, the sent package is outdated.' exit 2 fi -# shellcheck disable=SC2154 -if ! grep -qxF "${slave}" "${work_dir}/package-states/$1.$2.$3.$4.locked"; then - >&2 echo 'Whoops, this package is not built by this slave.' - exit 2 -fi - clean_up_tmp_dir() { cd "${base_dir}" rm -rf --one-file-system "${tmp_dir}" @@ -274,17 +267,41 @@ tar -x \ --no-wildcards-match-slash \ '*.pkg.tar.xz' \ '*.pkg.tar.xz.sig' \ - '*.pkg.tar.xz-namcap.log.gz' - -# check if all packages are signed and all signatures belong to a package + '*.pkg.tar.xz-namcap.log.gz' \ + '*.pkg.tar.xz.so.needs.gz' \ + '*.pkg.tar.xz.so.provides.gz' + +# check if all packages come with: +# - a package file +# - a signature +# - a namcap log +# - a list of needed libraries +# - a list of provided libraries missing_files=$( - find . -maxdepth 1 -name '*.pkg.tar.xz' -o -name '*.pkg.tar.xz.sig' -o -name '*.pkg.tar.xz-namcap.log.gz' | \ + find . -maxdepth 1 \( \ + \( \ + -name '*.pkg.tar.xz' \ + -printf '%f package\n' \ + \) -o \ + \( \ + -name '*.pkg.tar.xz.sig' \ + -printf '%f signature\n' \ + \) -o \ + \( \ + -name '*.pkg.tar.xz-namcap.log.gz' \ + -printf '%f namcap\n' \ + \) -o \ + \( \ + -name '*.pkg.tar.xz.so.needs.gz' \ + -printf '%f needed-libraries\n' \ + \) -o \ + \( \ + -name '*.pkg.tar.xz.so.provides.gz' \ + -printf '%f provided-libraries\n' \ + \) \ + \) | \ sed ' - s@\.sig$@ signature@ - t - s@-namcap\.log\.gz$@ namcap@ - t - s@$@ package@ + s/\(\.pkg\.tar\.xz\)\(\.sig\|-namcap\.log\.gz\|\.so\.\(provides\|needs\)\.gz\) /\1 / ' | \ sort -k1,1 -k2,2 | \ sed ' @@ -315,11 +332,23 @@ missing_files=$( p g } + / needed-libraries /!{ + h + s/^\(\S\+\) .*$/List of libraries needed by "\1" is missing./ + p + g + } + / provided-libraries /!{ + h + s/^\(\S\+\) .*$/List of libraries provided by "\1" is missing./ + p + g + } ' ) if [ -n "${missing_files}" ]; then - >&2 echo 'The following packages lack a signature, namcap log or package file:' + >&2 echo 'The following packages lack a signature, namcap log, package file or list of needed/provided libraries:' >&2 echo "${missing_files}" exit 3 fi @@ -353,16 +382,27 @@ package_errors=$( # shellcheck disable=SC2086 printf '%s\n' ${packages} | \ sed ' - s@\(-[^-]\+\)\{2\}-\([^-]\+\)\.pkg\.tar\.xz$@ \2@ - / any$/{ - s|any$|i686| - } s|^|was_built: | ' - sed ' - s|$| i686| - s|^|expected: | - ' "${work_dir}/package-infos/$1.$2.$3.$4.packages" + # shellcheck disable=SC2016 + { + printf 'SELECT CONCAT(' + printf '"expected: ",' + mysql_package_name_query + printf ')' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_architectures + mysql_join_binary_packages_build_slaves + mysql_join_binary_packages_repositories + printf ' WHERE `build_slaves`.`name`=from_base64("%s")' \ + "$( + printf '%s' "${slave}" | \ + base64 -w0 + )" + printf ' AND `repositories`.`name`="build-list"' + printf ';\n' + } | \ + mysql_run_query } | \ sort -k2 | \ uniq -u -f1 @@ -377,6 +417,62 @@ fi # move namcap.logs find . -maxdepth 1 -name '*.pkg.tar.xz-namcap.log.gz' -execdir mv '{}' "${build_log_directory}/success/" \; +# insert provided libraries into database +# shellcheck disable=SC2016 +{ + for lib_link in 'pl:provides' 'nl:needs'; do + printf 'CREATE TEMPORARY TABLE `%s` (`pkgfile` VARCHAR(64), `lib` VARCHAR(128));\n' \ + "${lib_link%:*}" + find . -maxdepth 1 -name '*.pkg.tar.xz.so.'"${lib_link#*:}"'.gz' -execdir zgrep -HF '' '{}' \; | \ + sed -n ' + s,^\./\(.\+\.pkg\.tar\.xz\)\.so\.'"${lib_link#*:}"'\.gz:\([^:]\+\)$,\1\n\2, + T + p + ' | \ + base64_encode_each | \ + sed ' + N + s/^\(\S\+\)\n\(\S\+\)$/(from_base64("\1"),from_base64("\2")),/ + $s/,$/;/ + 2 s/^/INSERT INTO `'"${lib_link%:*}"'` (`pkgfile`,`lib`) VALUES / + ' + printf 'INSERT IGNORE INTO `install_targets` (`name`)' + printf ' SELECT DISTINCT `%s`.`lib` FROM `%s`;\n' \ + "${lib_link%:*}" "${lib_link%:*}" + if [ "${lib_link%:*}" = 'pl' ]; then + printf 'INSERT IGNORE INTO `install_target_providers` (`package`,`install_target`)' + else + printf 'INSERT IGNORE INTO `dependencies` (`dependent`,`depending_on`,`dependency_type`)' + fi + printf ' SELECT `binary_packages`.`id`,`install_targets`.`id`' + if [ "${lib_link%:*}" = 'nl' ]; then + printf ',`dependency_types`.`id`' + fi + printf ' FROM `install_targets`' + if [ "${lib_link%:*}" = 'nl' ]; then + printf ' JOIN `dependency_types` ON `dependency_types`.`name`="link"' + fi + printf ' JOIN `%s` ON `%s`.`lib`=`install_targets`.`name`' \ + "${lib_link%:*}" "${lib_link%:*}" + printf ' JOIN `binary_packages`' + mysql_join_binary_packages_architectures + mysql_join_binary_packages_build_slaves + mysql_join_binary_packages_repositories + printf ' WHERE `build_slaves`.`name`=from_base64("%s")' \ + "$( + printf '%s' "${slave}" | \ + base64 -w0 + )" + printf ' AND `repositories`.`name`="build-list"' + printf ' AND ' + mysql_package_name_query + printf '=`%s`.`pkgfile`' \ + "${lib_link%:*}" + printf ';\n' + done +} | \ + mysql_run_query + # move packages destination=$(official_or_community "$1.$2.$3.$4" 'staging') @@ -401,58 +497,46 @@ for package in ${packages}; do remove_old_package_versions 'i686' "${destination}" "${package}" done -# remove old state files (these should be only "done" markers, but -# actually we don't care what it is) - as long as it's not "testing" or "tested" -find "${work_dir}/package-states" -maxdepth 1 -regextype grep \ - -not -name '*.testing' \ - -not -name '*.tested' \ - -regex '.*/'"$(str_to_regex "$1")"'\(\.[^.]\+\)\{4\}' \ - -not -regex '.*/'"$(str_to_regex "$1.$2.$3.$4")"'\.[^.]\+' \ - -exec rm '{}' \; - -# remove all loops which are broken by this package -find "${work_dir}/build-list.loops" -maxdepth 1 -regextype grep \ - -regex '.*/loop_[0-9]\+' \ - -exec grep -qxF "$1" '{}' \; \ - -exec rm '{}.locked' \; - -# remove package from build list -sed -i "/^$(str_to_regex "$1 $2 $3 $4")\$/d" "${work_dir}/build-list" - -# remove package lock file -if ! [ "${destination}" = 'build-support' ]; then - # shellcheck disable=SC2086 - printf '%s\n' ${packages} > \ - "${work_dir}/package-states/$1.$2.$3.$4.done" -fi # shellcheck disable=SC2016 { printf 'UPDATE `build_assignments`' - printf ' JOIN `build_slaves` ON `build_slaves`.`currently_building`=`build_assignments`.`id`' - printf ' JOIN `binary_packages` ON `binary_packages`.`build_assignment`=`build_assignments`.`id`' + mysql_join_build_assignments_build_slaves + mysql_join_build_assignments_binary_packages + mysql_join_binary_packages_repositories '' 'old_repo' printf ' SET' printf ' `build_assignments`.`is_broken`=0,' - printf ' `binary_packages`.`repository`=(SELECT `repositories`.`id` FROM `repositories` WHERE `repositories`.`name`=from_base64("%s")),' \ + printf ' `build_assignments`.`priority`=0,' + printf ' `binary_packages`.`repository`=(SELECT `new_repo`.`id` FROM `repositories` AS `new_repo` WHERE `new_repo`.`name`=from_base64("%s")),' \ "$( printf '%s' "${destination}" | \ base64 -w0 )" printf ' `binary_packages`.`has_issues`=0,' printf ' `binary_packages`.`is_tested`=0' - printf ' WHERE `build_slaves`.`name`=from_base64("%s");\n' \ + printf ' WHERE `build_slaves`.`name`=from_base64("%s")' \ "$( printf '%s' "${slave}" | \ base64 -w0 )" - printf 'UPDATE `build_slaves` SET' - printf ' `build_slaves`.`currently_building`=NULL' - printf ' WHERE `build_slaves`.`name`=from_base64("%s");\n' \ + printf ' AND `old_repo`.`name`="build-list";\n' + printf 'UPDATE `build_slaves` AS `to_update`' + printf ' JOIN `build_slaves` AS `current_slave`' + printf ' ON `to_update`.`currently_building`=`current_slave`.`currently_building`' + printf ' SET `to_update`.`currently_building`=NULL' + printf ' WHERE `current_slave`.`name`=from_base64("%s");\n' \ "$( printf '%s' "${slave}" | \ base64 -w0 )" + printf 'CREATE TEMPORARY TABLE `loops_to_delete` (`loop` MEDIUMINT);\n' + printf 'INSERT INTO `loops_to_delete`' + printf ' SELECT `build_dependency_loops`.`loop` FROM `build_dependency_loops`' + mysql_join_build_dependency_loops_binary_packages + mysql_join_binary_packages_repositories + printf ' WHERE NOT `repositories`.`name` = "build-list";\n' + printf 'DELETE FROM `build_dependency_loops` WHERE EXISTS (' + printf 'SELECT * FROM `loops_to_delete` WHERE `loops_to_delete`.`loop`=`build_dependency_loops`.`loop`' + printf ');\n' + printf 'DROP TABLE `loops_to_delete`;\n' } | \ - ${mysql_command} -rm -f \ - "${work_dir}/package-states/$1.$2.$3.$4.locked" \ - "${work_dir}/package-states/$1.$2.$3.$4.broken" + mysql_run_query diff --git a/bin/sanity-check b/bin/sanity-check index 8736d47..7d1b2f0 100755 --- a/bin/sanity-check +++ b/bin/sanity-check @@ -20,7 +20,7 @@ usage() { i_am_insane() { if [ ! -s "${work_dir}/build-master-sanity" ]; then printf '\001ACTION goes insane.\001\n' | \ - sponge "${irc_dir}/#archlinux-ports/in" + irc_say fi echo 'build master is insane' > \ "${work_dir}/build-master-sanity" @@ -103,7 +103,7 @@ touch "${tmp_dir}/messages" trap 'finish' EXIT if [ $# -eq 0 ]; then - set -- git-repositories build-list repos package-database state-files + set -- git-repositories build-list mysql repos package-database track-state fi while [ $# -gt 0 ]; do @@ -119,16 +119,28 @@ while [ $# -gt 0 ]; do for repo in ${repo_names}; do eval 'repo_path="${repo_paths__'"${repo}"'}"' repo_revision=$( - cat "${work_dir}/${repo}.revision" + # shellcheck disable=SC2016 + { + printf 'SELECT `git_repositories`.`head` FROM `git_repositories`' + printf ' WHERE `git_repositories`.`name`=from_base64("%s");\n' \ + "$(printf '%s' "${repo}" | base64 -w0)" + } | \ + mysql_run_query ) - if ! git -C "${repo_path}" archive "${repo_revision}" -- | \ - tar -t > /dev/null; then + if ! obj_type=$(git -C "${repo_path}" cat-file -t "${repo_revision}" 2>/dev/null); then if [ ${silence} -le 1 ]; then - printf '\nThe repository %s (%s) cannot archive the current revision %s.\n' \ + printf '\nThe repository %s (%s) does not know the current revision %s.\n' \ "${repo}" "${repo_path}" "${repo_revision}" >> \ "${tmp_dir}/messages" fi i_am_insane + elif [ "${obj_type}" != 'commit' ]; then + if [ ${silence} -le 1 ]; then + printf '\nThe repository %s (%s) knows the current revision %s, but it is not a commit, but a %s.\n' \ + "${repo}" "${repo_path}" "${repo_revision}" "${obj_type}" >> \ + "${tmp_dir}/messages" + fi + i_am_insane fi done @@ -145,20 +157,19 @@ while [ $# -gt 0 ]; do "${tmp_dir}/messages" errors=$( - grep -vn '^\S\+ [0-9a-f]\{40\} [0-9a-f]\{40\} \S\+$' "${work_dir}/build-list" - ) || true - if [ -n "${errors}" ]; then - if [ ${silence} -le 1 ]; then - printf '\nThe following build orders are wrongly formatted:\n%s\n' \ - "${errors}" >> \ - "${tmp_dir}/messages" - fi - i_am_insane - fi - - errors=$( - cut -d' ' -f1 < \ - "${work_dir}/build-list" | \ + # shellcheck disable=SC2016 + { + printf 'SELECT `architectures`.`name`,`package_sources`.`pkgbase` FROM `package_sources`' + mysql_join_package_sources_build_assignments + mysql_join_build_assignments_architectures + printf ' WHERE EXISTS(' + printf 'SELECT * FROM `binary_packages`' + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="build-list"' + printf ' AND `binary_packages`.`build_assignment`=`build_assignments`.`id`' + printf ');\n' + } | \ + mysql_run_query | \ sort | \ uniq -d ) @@ -172,13 +183,16 @@ while [ $# -gt 0 ]; do fi errors=$( + # shellcheck disable=SC2016 { - cut -d' ' -f1 < \ - "${work_dir}/build-list" - cat "${work_dir}/deletion-list" + printf 'SELECT `a`.`pkgname` FROM `binary_packages` AS `a`' + mysql_join_binary_packages_repositories 'a' 'a_r' + printf ' AND `a_r`.`name`="build-list"' + printf ' JOIN `binary_packages` AS `b` ON `a`.`pkgname`=`b`.`pkgname`' + mysql_join_binary_packages_repositories 'b' 'b_r' + printf ' AND `b_r`.`name`="deletion-list";\n' } | \ - sort | \ - uniq -d + mysql_run_query ) if [ -n "${errors}" ]; then if [ ${silence} -le 1 ]; then @@ -268,17 +282,18 @@ while [ $# -gt 0 ]; do errors=$( { - tar -tzf "${tmp_dir}/${repo}.db.tar.gz" | \ - grep '/$' | \ - sed ' - s|/$|| - s|^|in_database | + tar -Oxzf "${tmp_dir}/${repo}.db.tar.gz" --wildcards '*/desc' 2>/dev/null | \ + sed -n ' + /^%FILENAME%$/ { + N + s/^.*\n/in_database / + p + } ' echo "${packages}" | \ - grep '\S' | \ sed ' - s|-[^-]\+$|| - s|^|in_repository | + /\.pkg\.tar\.xz$/ !d + s/^/in_repository / ' | \ sort -u } | \ @@ -334,51 +349,80 @@ while [ $# -gt 0 ]; do ;; - state-files) - - for status in 'staging' 'testing'; do + track-state) - [ ${silence} -gt 0 ] || \ - printf 'checking state-files of "%s" ...' "${status}" >> \ - "${tmp_dir}/messages" + [ ${silence} -gt 0 ] || \ + printf 'checking if all packages are tracked correctly ...' >> \ + "${tmp_dir}/messages" - errors=$( + errors=$( + { + # shellcheck disable=SC2016 { - if [ "${status}" = 'staging' ]; then - find "${work_dir}/package-states" -name '*.done' \ - -exec sed 's|^|package-state-file |' '{}' \; - else - find "${work_dir}/package-states" \( -name '*.testing' -o -name '*.tested' \) \ - -exec sed 's|^|package-state-file |' '{}' \; - fi - ls_master_mirror 'i686' | \ - grep "${status}\$" | \ - while read -r repo; do - ls_master_mirror "i686/${repo}" - done | \ - grep '\.pkg\.tar\.xz$' | \ - sed 's|^|package-file |' + printf 'SELECT "mysql",CONCAT(`repositories`.`name`,"/",' + mysql_package_name_query + printf ') FROM `binary_packages`' + mysql_join_binary_packages_repositories + printf ' AND `repositories`.`is_on_master_mirror`' + mysql_join_binary_packages_architectures } | \ - sort -k2 | \ - uniq -cf1 | \ - grep -v '^\s*2\s' | \ - awk '{print $2 " " $3}' - ) - if [ -n "${errors}" ]; then - if [ ${silence} -le 1 ]; then - printf '\nThe following %s packages do not have state files or vice versa:\n%s\n' \ - "${status}" \ - "${errors}" >> \ - "${tmp_dir}/messages" - fi - i_am_insane + mysql_run_query | \ + tr '\t' ' ' + ls_master_mirror 'i686' | \ + while read -r repo; do + ls_master_mirror "i686/${repo}" | \ + sed ' + /\.pkg\.tar\.xz$/!d + s,^,package-file '"${repo}"'/, + ' + done + } | \ + sed 's/\(-[0-9]\+\)\.0\(-[^- ]\+$\)/\1\2/' | \ + sort -k2 | \ + uniq -uf1 + ) + if [ -n "${errors}" ]; then + if [ ${silence} -le 1 ]; then + printf '\nThe following packages from the master mirror are not tracked in the database or vice versa:\n%s\n' \ + "${errors}" >> \ + "${tmp_dir}/messages" fi + i_am_insane + fi - [ ${silence} -gt 0 ] || \ - echo ' passed.' >> \ + [ ${silence} -gt 0 ] || \ + echo ' passed.' >> \ + "${tmp_dir}/messages" + + ;; + + mysql) + + [ ${silence} -gt 0 ] || \ + printf 'checking mysql-sanity-check-file ...' >> \ + "${tmp_dir}/messages" + + if [ -s "${webserver_directory}/mysql-sanity.html" ]; then + if [ ${silence} -le 1 ]; then + printf '\nThere is something wrong with the database:\n' + cat "${webserver_directory}/mysql-sanity.html" + fi >> \ "${tmp_dir}/messages" + i_am_insane + fi - done + # hopefully, this gets rid of false positives :-) + sleep 1 + + if find "${work_dir}" -mindepth 1 -maxdepth 1 -name 'tmp.mysql-functions.query.*' | \ + grep '\S' >> \ + "${tmp_dir}/messages"; then + i_am_insane + fi + + [ ${silence} -gt 0 ] || \ + echo ' passed.' >> \ + "${tmp_dir}/messages" ;; @@ -399,5 +443,5 @@ done if [ -f "${work_dir}/build-master-sanity" ]; then rm "${work_dir}/build-master-sanity" printf '\001ACTION resumes sanity.\001\n' | \ - sponge "${irc_dir}/#archlinux-ports/in" + irc_say fi diff --git a/bin/seed-build-list b/bin/seed-build-list index acf25c3..ae08d57 100755 --- a/bin/seed-build-list +++ b/bin/seed-build-list @@ -5,8 +5,6 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" -# TODO: put new packages also in mysql database - # shellcheck disable=SC2016 usage() { >&2 echo '' @@ -16,6 +14,9 @@ usage() { >&2 echo ' - a list of packages which need to be rebuilt' >&2 echo '' >&2 echo 'possible options:' + >&2 echo ' -a|--auto:' + >&2 echo ' Automatically reschedule packages which have run-time' + >&2 echo ' dependencies that are not available anywhere.' >&2 echo ' -h|--help:' >&2 echo ' Show this help and exit.' >&2 echo ' -i|--ignore $package:' @@ -26,6 +27,8 @@ usage() { >&2 echo ' Do not actually update build-list, just print it.' >&2 echo ' -p|--package $pkg_regex:' >&2 echo ' Update packages matching $pkg_regex.' + >&2 echo ' -w|--wait:' + >&2 echo ' Wait for lock if necessary.' [ -z "$1" ] && exit 1 || exit "$1" } @@ -34,26 +37,32 @@ tmp_dir=$(mktemp -d 'tmp.seed-build-list.XXXXXXXXXX' --tmpdir) trap "rm -rf --one-file-system '${tmp_dir:?}'" EXIT eval set -- "$( - getopt -o hi:m:np: \ + getopt -o ahi:m:np:w \ + --long auto \ --long help \ --long ignore: \ --long mirror: \ --long no-action \ --long package: \ + --long wait \ -n "$(basename "$0")" -- "$@" || \ echo usage )" touch "${tmp_dir}/mirrors" -touch "${tmp_dir}/delta-packages" touch "${tmp_dir}/package-regexes" touch "${tmp_dir}/ignore-packages" +auto=false update=true +wait_for_lock='-n' while true do case "$1" in + -a|--auto) + auto=true + ;; -h|--help) usage 0 ;; @@ -75,6 +84,9 @@ do echo "$1" >> \ "${tmp_dir}/package-regexes" ;; + -w|--wait) + wait_for_lock='' + ;; --) shift break @@ -92,207 +104,280 @@ if [ $# -ne 0 ]; then fi if [ ! -s "${tmp_dir}/mirrors" ] && \ - [ ! -s "${tmp_dir}/package-regexes" ]; then + [ ! -s "${tmp_dir}/package-regexes" ] && \ + ! ${auto}; then # nothing to do exit 0 fi -repos="${stable_package_repositories}" - -find "${work_dir}/package-infos" -maxdepth 1 -name '*.packages' -printf '%f\n' | \ - sed 's|\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)\.[^.]\+$| \1 \2 \3|' | \ - sort -k1,1 > \ - "${tmp_dir}/known-packages" - -mod_repo_rev=$(cat "${work_dir}/archlinux32.revision") -{ - for repo in ${repo_names}; do - if [ "${repo_names}" = 'archlinux32' ]; then - continue - fi - eval 'repo_path="${repo_paths__'"${repo}"'}"' - repo_rev=$(cat "${work_dir}/${repo}.revision") - git -C "${repo_path}" archive "$(cat "${work_dir}/${repo}.revision")" | \ - tar -t | \ - grep '^[^/]\+/repos/[^/]\+/PKGBUILD$' | \ - grep -v -- '-i686/PKGBUILD$' | \ - grep -v -- '[-/]\(staging\|testing\|unstable\)-[^/]\+/PKGBUILD$' | \ - sed ' - s|^\([^/]\+\)/repos/\([^/]\+\)-[^/-]\+/PKGBUILD$|'"${repo_rev}"' \1 \2| - ' - done | \ - sort -u | \ - sort -k1,1 - awk '{print "nothing " $1 " " $4}' "${tmp_dir}/known-packages" | \ - sort -u | \ - sed 'p' -} | \ - sort -k2,3 | \ - uniq -uf1 | \ - while read -r repo_rev pkg prepo; do - generate_package_metadata "${pkg}" "${repo_rev}" "${mod_repo_rev}" "${prepo}" - done - -find "${work_dir}/package-infos" -maxdepth 1 -name '*.packages' -exec grep -HF '' "{}" \; | \ - sed ' - s|^.*/|| - s|\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)\.[^.]\+:| \1 \2 \3 | - ' | \ - sort -k5,5 > \ - "${tmp_dir}/known-packages" +# get locks +if ${update}; then + exec 9> "${sanity_check_lock_file}" + if ! flock -s ${wait_for_lock} 9; then + >&2 echo 'Cannot get sanity-check lock.' + exit 1 + fi -# generate delta-packages from package-regexes -while read -r pkg_regex; do - if [ -z "${pkg_regex}" ]; then - continue + exec 8> "${build_list_lock_file}" + if ! flock ${wait_for_lock} 8; then + >&2 echo 'Cannot get build-list lock.' + exit 1 fi - awk '{print $5}' "${tmp_dir}/known-packages" | \ - grep "${pkg_regex}" || \ - true -done < \ - "${tmp_dir}/package-regexes" >> \ - "${tmp_dir}/delta-packages" +fi -# genereate delta_packages from mirror delta +repos="${stable_package_repositories}" -while read -r mirror; do - if [ -z "${mirror}" ]; then - continue - fi +# genereate must-haves query from mirror delta +if [ -s "${tmp_dir}/mirrors" ]; then { # theirs - for repo in ${repos}; do - curl -sS "${mirror}/${repo}/os/x86_64/${repo}.db.tar.gz" | \ - tar -tz - done | \ - grep '/$' | \ - sed ' - s|/$|| - s|^\(.*\)-\([^-]\+-[^-]\+\)|theirs \1-\2 \2 \1| - ' - + while read -r mirror; do + if [ -z "${mirror}" ]; then + continue + fi + for repo in ${repos}; do + curl -sS "${mirror}/${repo}/os/x86_64/${repo}.db.tar.gz" | \ + tar -Oxz --wildcards '*/desc' | \ + sed ' + /^%FILENAME%$/!d + N + s/^.*\n// + s/-x86_64\(\.pkg\.tar\.xz\)$/-i686\1/ + s/^\(.*\)-\([^-]\+-[^-]\+\)-\([^-]\+\)/theirs \2 \3 \1/ + ' + done + done < \ + "${tmp_dir}/mirrors" # ours - for repo in $(ls_master_mirror 'i686'); do - ls_master_mirror "i686/${repo}" | \ - grep '\.pkg\.tar\.xz$' | \ - sed 's|-[^-]\+$||' - done | \ + # shellcheck disable=SC2016 + { + printf 'SELECT ' + mysql_package_name_query + printf ' FROM `binary_packages`' + mysql_join_binary_packages_architectures + } | \ + mysql_run_query | \ sed ' - s|^\(.*\)-\([^-]\+-[^-]\+\)|ours \1-\2 \2 \1| + s/^\(.*\)-\([^-]\+-[^-]\+\)-\([^-]\+\)/ours \2 \3 \1/ ' } | \ - expand_version 3 | \ - sort -k4,4 -k3Vr,3 -k1,1 | \ - shrink_version 3 | \ - uniq -f3 | \ - grep '^theirs ' | \ - awk '{print $4}' | \ - sort -k1,1 >> \ - "${tmp_dir}/delta-packages" -done < \ - "${tmp_dir}/mirrors" - -sort -k1,1 -u "${tmp_dir}/delta-packages" | \ - sponge "${tmp_dir}/delta-packages" - -cat \ - "${tmp_dir}/delta-packages" \ - "${tmp_dir}/ignore-packages" \ - "${tmp_dir}/ignore-packages" | \ - sort | \ - uniq -u | \ - sponge "${tmp_dir}/delta-packages" - -join -1 1 -2 5 -o 2.1,2.2,2.3,2.4,2.5 "${tmp_dir}/delta-packages" "${tmp_dir}/known-packages" >> \ - "${tmp_dir}/append-packages" + expand_version 2 | \ + sort -k3,4 -k2Vr,2 -k1,1 | \ + shrink_version 2 | \ + uniq -f2 | \ + sed -n ' + s/^theirs \(\S\+ \)\{2\}// + T + p + ' | \ + sort -u > \ + "${tmp_dir}/must-haves" +fi +# shellcheck disable=SC2016 { - awk '{print $5}' "${tmp_dir}/append-packages" - cat "${tmp_dir}/delta-packages" + printf 'CREATE TEMPORARY TABLE `ignore_packages` (`pkgname` VARCHAR(64));\n' + if [ -s "${tmp_dir}/ignore-packages" ]; then + grep -vxF '' "${tmp_dir}/ignore-packages" | \ + base64_encode_each | \ + sed ' + s/^/(from_base64("/ + s/$/")),/ + $s/,$/;/ + 1 s/^/INSERT INTO `ignore_packages` (`pkgname`) VALUES \n/ + ' + fi + # packages on the build-list or deletion-list should be ignored + printf 'INSERT IGNORE INTO `ignore_packages` (`pkgname`)' + printf ' SELECT DISTINCT `ignore_bin`.`pkgname`' + printf ' FROM `binary_packages` AS `ignore_bin`' + mysql_join_binary_packages_repositories 'ignore_bin' + printf ' WHERE `repositories`.`name` IN ("build-list","deletion-list")' + # packages with no not-to-be-deleted and at least on to-be-deleted version should be ignored + printf ' OR (' + printf '`ignore_bin`.`is_to_be_deleted`' + printf ' AND NOT EXISTS (' + printf 'SELECT * FROM `binary_packages` AS `other_bin`' + printf ' WHERE NOT `other_bin`.`is_to_be_deleted`' + printf ' AND `other_bin`.`pkgname`=`ignore_bin`.`pkgname`' + printf '));\n' } | \ - sort | \ - uniq -u | \ - sponge "${tmp_dir}/delta-packages" + sponge "${tmp_dir}/ignore-packages" -if [ -s "${tmp_dir}/delta-packages" ]; then - echo 'There are still packages newer for x86_64 which I cannot identify :-/' - cat "${tmp_dir}/delta-packages" - exit 2 -fi - -rm "${tmp_dir}/delta-packages" - -# convert append-packages to build-list.new (add git hashes) +# shellcheck disable=SC2016 +{ + printf 'CREATE TEMPORARY TABLE `must_haves` (`pkgname` VARCHAR(64));\n' + if [ -s "${tmp_dir}/must-haves" ]; then + grep -vxF '' "${tmp_dir}/must-haves" | \ + base64_encode_each | \ + sed ' + s/^/(from_base64("/ + s/$/")),/ + $s/,$/;/ + 1 s/^/INSERT INTO `must_haves` (`pkgname`) VALUES \n/ + ' + fi +} | \ + sponge "${tmp_dir}/must-haves" -cut -d' ' -f1,4 < \ - "${tmp_dir}/append-packages" | \ - sort -u | \ - while read -r pkg repo; do - git_repo=$(find_git_repository_to_package_repository "${repo}") - printf '%s %s %s %s\n' \ - "${pkg}" \ - "$(cat "${work_dir}/${git_repo}.revision")" \ - "$(cat "${work_dir}/archlinux32.revision")" \ - "${repo}" +# fetch unknown must-haves from upstream +# shellcheck disable=SC2016 +printf 'CREATE TEMPORARY TABLE `pkgbases` (`pkgbase` VARCHAR(64), `repository` VARCHAR(64));\n' > \ + "${tmp_dir}/pkgbases" +# shellcheck disable=SC2016 +{ + cat "${tmp_dir}/must-haves" "${tmp_dir}/ignore-packages" + printf 'SELECT `must_haves`.`pkgname` FROM `must_haves`' + printf ' WHERE NOT EXISTS (' + printf 'SELECT * FROM `binary_packages`' + printf ' WHERE `binary_packages`.`pkgname`=`must_haves`.`pkgname`' + printf ') AND NOT EXISTS (' + printf 'SELECT * FROM `ignore_packages`' + printf ' WHERE `ignore_packages`.`pkgname`=`must_haves`.`pkgname`' + printf ') AND NOT `must_haves`.`pkgname` LIKE "lib32-%%";\n' +} | \ + mysql_run_query | \ + while read -r pkgname; do + content=$( + curl -Ss 'https://www.archlinux.org/packages/search/json/?name='"${pkgname}" | \ + tr ',' '\n' + ) + repo=$( + printf '%s\n' "${content}" | \ + sed -n ' + s/^\s*"repo"\s*:\s*"// + T + s/".*$// + T + p + ' + ) + pkgbase=$( + printf '%s\n' "${content}" | \ + sed -n ' + s/^\s*"pkgbase"\s*:\s*"// + T + s/".*$// + T + p + ' + ) + if [ -z "${pkgbase}" ] || [ -z "${repo}" ]; then + >&2 printf 'Could not find "%s" which is newer on x86_64!?\n' "${pkgname}" + exit 2 + fi + printf '(from_base64("%s"),from_base64("%s")),\n' \ + "$(printf '%s' "${pkgbase}" | base64 -w0)" \ + "$(printf '%s' "${repo}" | base64 -w0)" done | \ - sort -u > \ - "${tmp_dir}/build-list.append" - -# Create a lock file for build list. - -if ${update}; then - # always block if locked - exec 9> "${build_list_lock_file}" - flock 9 - - exec 8> "${sanity_check_lock_file}" - flock -s 8 -fi + sort -u | \ + sed ' + 1 s/^/INSERT IGNORE INTO `pkgbases` (`pkgbase`,`repository`) VALUES \n/ + $s/,$/;/ + ' >> \ + "${tmp_dir}/pkgbases" +# shellcheck disable=SC2016 { - awk '{print $2 " " $3 " " $4 " " $1}' "${tmp_dir}/build-list.append" - - # ignore packages on the build-list - awk '{print $2 " " $3 " " $4 " " $1}' < \ - "${work_dir}/build-list" | \ - sed 'p' - - # ignore packages on the deletion-list - awk '{print "X X X " $1}' "${work_dir}/deletion-list" | \ - sed 'p' - - # ignore packages on the blacklist - git -C "${repo_paths__archlinux32}" archive "$(cat "${work_dir}/archlinux32.revision")" -- blacklist | \ - tar -Ox blacklist | \ - sed ' - s/\s*#.*$// - /^\s*$/d - ' | \ - awk '{print "X X X " $1}' | \ - sed 'p' - - # ignore explicitely ignored packages - awk '{print "X X X " $1}' "${tmp_dir}/ignore-packages" | \ - sed 'p' + cat "${tmp_dir}/must-haves" "${tmp_dir}/ignore-packages" "${tmp_dir}/pkgbases" + if ${auto}; then + printf 'CREATE TEMPORARY TABLE `bin_ids` (`id` BIGINT, UNIQUE KEY (`id`));\n' + printf 'INSERT IGNORE INTO `bin_ids`' + printf ' SELECT `binary_packages`.`id` FROM `binary_packages`' + mysql_join_binary_packages_repositories + printf ' AND `repositories`.`is_on_master_mirror`' + printf ' AND `repositories`.`name`!="build-support"' + mysql_join_binary_packages_dependencies + mysql_join_dependencies_dependency_types + printf ' AND `dependency_types`.`relevant_for_binary_packages`' + # nothing "less stable" has the same name + printf ' WHERE NOT EXISTS (' + printf 'SELECT * FROM `binary_packages` AS `subst_bp`' + mysql_join_binary_packages_repositories 'subst_bp' 'subst_r' + printf ' AND `subst_r`.`name`!="build-support"' + printf ' JOIN `repository_stability_relations` ON `repository_stability_relations`.`less_stable`=`subst_r`.`stability`' + printf ' WHERE `repository_stability_relations`.`more_stable`=`repositories`.`stability`' + printf ' AND `subst_bp`.`id`!=`binary_packages`.`id`' + printf ' AND `subst_bp`.`pkgname`=`binary_packages`.`pkgname`' + printf ') AND NOT EXISTS (' + printf 'SELECT * FROM `install_target_providers`' + mysql_join_install_target_providers_binary_packages '' 'subst_bp' + mysql_join_binary_packages_repositories 'subst_bp' 'subst_r' + printf ' AND `subst_r`.`name` NOT IN ("build-support","deletion-list","to-be-decided")' + printf ' WHERE `install_target_providers`.`install_target`=`dependencies`.`depending_on`' + printf ')' + printf ';\n' + fi + printf 'SELECT ' + printf '`pkgbases`.`pkgbase`,' + printf '`git_repositories`.`head`,' + printf '(' + printf 'SELECT `al32`.`head` FROM `git_repositories` AS `al32`' + printf ' WHERE `al32`.`name`="archlinux32"' + printf '),' + printf '`pkgbases`.`repository`' + printf ' FROM `pkgbases`' + printf ' JOIN `upstream_repositories` ON `upstream_repositories`.`name`=`pkgbases`.`repository`' + mysql_join_upstream_repositories_git_repositories + printf ' WHERE NOT EXISTS (' + printf 'SELECT * FROM `ignore_packages`' + printf ' JOIN `binary_packages` AS `i_bp` ON `ignore_packages`.`pkgname`=`i_bp`.`pkgname`' + mysql_join_binary_packages_build_assignments 'i_bp' 'i_ba' + mysql_join_build_assignments_package_sources 'i_ba' 'i_ps' + printf ' WHERE `i_ps`.`pkgbase`=`pkgbases`.`pkgbase`' + printf ');\n' + printf ';\n' + printf 'SELECT ' + printf '`package_sources`.`pkgbase`,' + printf '`git_repositories`.`head`,' + printf '(' + printf 'SELECT `al32`.`head` FROM `git_repositories` AS `al32`' + printf ' WHERE `al32`.`name`="archlinux32"' + printf '),' + printf '`upstream_repositories`.`name`' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_package_sources + mysql_join_package_sources_upstream_repositories + mysql_join_upstream_repositories_git_repositories + printf ' WHERE (' + if [ -s "${tmp_dir}/package-regexes" ]; then + grep -vxF '' "${tmp_dir}/package-regexes" | \ + base64_encode_each | \ + sed ' + s/^/`binary_packages`.`pkgname` REGEXP from_base64("/ + s/$/") OR / + ' | \ + tr -d '\n' + fi + if ${auto}; then + printf 'EXISTS (' + printf 'SELECT * FROM `bin_ids`' + printf ' WHERE `bin_ids`.`id`=`binary_packages`.`id`' + printf ')' + printf ' OR ' + fi + printf 'EXISTS (' + printf 'SELECT * FROM `must_haves`' + printf ' WHERE `must_haves`.`pkgname`=`binary_packages`.`pkgname`' + printf ')) AND NOT EXISTS (' + printf 'SELECT * FROM `ignore_packages`' + printf ' WHERE `ignore_packages`.`pkgname`=`binary_packages`.`pkgname`' + printf ');\n' } | \ - sort -k4,4 | \ - uniq -uf3 > \ - "${tmp_dir}/build-list.new" - -if ${update}; then - awk '{print $4 " " $1 " " $2 " " $3}' \ - "${tmp_dir}/build-list.new" | \ - tee -a "${work_dir}/build-list" - - while read -r git_revision mod_git_revision repository pkgbase; do - mysql_generate_package_metadata "${pkgbase}" "${git_revision}" "${mod_git_revision}" "${repository}" - done < \ - "${tmp_dir}/build-list.new" - - # Remove the lock file - - rm -f "${build_list_lock_file}" -else - awk '{print $4 " " $1 " " $2 " " $3}' \ - "${tmp_dir}/build-list.new" -fi + mysql_run_query | \ + sort -u | \ + tr '\t' ' ' | \ + if ${update}; then + while read -r pkgbase git_rev mod_git_rev repo; do + printf '%s ' "${pkgbase}" "${git_rev}" "${mod_git_rev}" "${repo}" >&2 + printf '%s ' "${pkgbase}" "${git_rev}" "${mod_git_rev}" "${repo}" | \ + sed 's/ $/\n/' + mysql_generate_package_metadata 'build-list' "${pkgbase}" "${git_rev}" "${mod_git_rev}" "${repo}" + printf '\n' >&2 + done + + rm -f "${build_list_lock_file}" + else + cat + fi diff --git a/bin/show-dependencies b/bin/show-dependencies index efdf745..492f11b 100755 --- a/bin/show-dependencies +++ b/bin/show-dependencies @@ -3,85 +3,19 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" +# TODO: can this be faster? + # Create a lock file. if [ $# -eq 0 ]; then broken=$( - find "${work_dir}/package-states" -maxdepth 1 -name '*.broken' -printf '%f\n' | \ - sed 's|\(\.[^.]\+\)\{4\}$||' | \ - sort -u - ) - new_sum='x' - sum='' - tmp_dir=$(mktemp -d 'tmp.show-dependencies.0.XXXXXXXXXX' --tmpdir) - trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT - find "${work_dir}/package-infos" -maxdepth 1 -name '*.builds' -exec \ - grep -HF '' {} \; | \ - sed ' - s|^.*/|| - s|\(\.[^.]\+\)\{4\}:| | - ' | \ - sort -k2,2 > \ - "${tmp_dir}/builds" - while ! [ "${new_sum}" = "${sum}" ]; do - sum="${new_sum}" - # shellcheck disable=SC2086 - printf '%s\n' ${broken} | \ - sort > \ - "${tmp_dir}/broken" - broken=$( - { - find "${work_dir}/package-infos" -maxdepth 1 -name '*.build-depends' | \ - sed 's|^.*/\(.*\)\(\.[^.]\+\)\{4\}$|\1 \0|' | \ - sort -k1,1 | \ - join -1 1 -2 1 -o 1.2 - "${tmp_dir}/broken" | \ - xargs -r cat - } | \ - sort -u | \ - join -1 1 -2 2 -o 2.1 - "${tmp_dir}/builds" - ) - broken=$( - { - { - # shellcheck disable=SC2086 - printf '%s\n' ${broken} - cat "${tmp_dir}/broken" - } | \ - sort -u - cut -d' ' -f1 < \ - "${work_dir}/build-list" | \ - sort -u - } | \ - sort | \ - uniq -d - ) - new_sum=$( - # shellcheck disable=SC2086 - printf '%s\n' ${broken} | \ - sha512sum - ) - done - - rm -rf --one-file-system "${tmp_dir}" - trap - EXIT - broken=$( - { - # shellcheck disable=SC2086 - printf '%s\n' ${broken} 'ALL' - { - find "${webserver_directory}/graphs" -maxdepth 1 -name '*.png' -printf '%f\n' | \ - sed 's|\.png$||' - { - cut -d' ' -f1 < \ - "${work_dir}/build-list" - cat "${work_dir}/deletion-list" - } | \ - sort -u - } | \ - sort | \ - uniq -d - } | \ + printf 'CALL show_broken_packages_and_dependencies;\n' | \ + mysql_run_query | \ + sed ' + s/\s.*$// + ' | \ sort -u + printf 'ALL\n' ) # shellcheck disable=SC2086 "$0" ${broken} @@ -119,360 +53,248 @@ fi tmp_dir=$(mktemp -d 'tmp.show-dependencies.1.XXXXXXXXXX' --tmpdir) trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT -sort -u "${work_dir}/build-order" | \ - grep -v '^\(\S\+\) \1$' | \ - grep '^\S\+ \S\+$' > \ - "${tmp_dir}/original-build-order" - -sort -k1,1 "${work_dir}/build-list" > \ - "${tmp_dir}/build-list.sorted-by-package" - -{ - sort -k1,1 "${tmp_dir}/original-build-order" | \ - join -1 1 -2 1 -o 1.1,1.2 - "${tmp_dir}/build-list.sorted-by-package" - sort -k2,2 "${tmp_dir}/original-build-order" | \ - join -1 2 -2 1 -o 1.1,1.2 - "${tmp_dir}/build-list.sorted-by-package" -} | \ - sort -u | \ - sponge "${tmp_dir}/original-build-order" - -find "${work_dir}/package-infos" -maxdepth 1 -printf '%f\n' | \ - sed 's|\(\.[^.]\+\)\{4\}$||' | \ - sort -u > \ - "${tmp_dir}/known-packages" - -find "${work_dir}/package-infos" -maxdepth 1 -name '*.groups' -printf '%f %p\n' | \ - while read -r name pf; do - xargs -r printf "${name%.*.*.*.*}"' %s\n' < \ - "${pf}" - done > \ - "${tmp_dir}/known-groups" - -find "${work_dir}/package-infos" -maxdepth 1 -name '*.packages' -printf '%f %p\n' | \ - while read -r name pf; do - grep -vxF "${name%.*.*.*.*}" "${pf}" | \ - xargs -r printf "${name%.*.*.*.*}"' %s\n' - done > \ - "${tmp_dir}/known-split-packages" - -find "${work_dir}/package-infos" -maxdepth 1 -name '*.run-depends' -printf '%f %p\n' | \ - while read -r name pf; do - xargs -r printf '%s '"${name%.*.*.*.*}"'\n' < \ - "${pf}" - done > \ - "${tmp_dir}/known-run-depends" - for target_package in "$@"; do output="${webserver_directory}/graphs/${target_package}.png" - if [ "${target_package}" = 'ALL' ]; then - - # groups and split packages built by jobs on the build list - cat "${tmp_dir}/known-groups" "${tmp_dir}/known-split-packages" | \ - sort -k1,1 | \ - join -1 1 -2 1 -o 1.2 - "${tmp_dir}/build-list.sorted-by-package" | \ - sort -u > \ - "${tmp_dir}/relevant-stuff" - - { - # groups and split packages built by jobs on the build list - which are also dependencies of packages on the build-list - sort -k1,1 "${tmp_dir}/original-build-order" | \ - join -1 1 -2 1 -o 2.1 - "${tmp_dir}/relevant-stuff" - # build list jobs themself - cut -d' ' -f1 < "${work_dir}/build-list" - } | \ - sort -u | \ - sponge "${tmp_dir}/relevant-stuff" - - { - sort -k1,1 "${tmp_dir}/original-build-order" | \ - join -1 1 -2 1 -o 1.1,1.2 - "${tmp_dir}/relevant-stuff" | \ - sort -k2,2 | \ - join -1 2 -2 1 -o 1.1,1.2 - "${tmp_dir}/relevant-stuff" - sed 's/.*/\0 \0/' "${tmp_dir}/relevant-stuff" - } > \ - "${tmp_dir}/build-order" - - rm "${tmp_dir}/relevant-stuff" - - find "${work_dir}/package-states" -name '*.done' -printf '%f\n' | \ - sed 's|\(\.[^.]\+\)\{4\}$||' | \ - sort -u | \ - tee "${tmp_dir}/done-packages" | \ - sed 's|^|0 staging-package |' > \ - "${tmp_dir}/knots" - - { - cat \ - "${tmp_dir}/known-groups" \ - "${tmp_dir}/known-run-depends" \ - "${tmp_dir}/known-split-packages" - awk '{print $1 " " $1}' < \ - "${tmp_dir}/known-packages" - } | \ - sort -k1,1 > \ - "${tmp_dir}/known-connections.sorted.1" - - sort -k2,2 \ - "${tmp_dir}/known-connections.sorted.1" > \ - "${tmp_dir}/known-connections.sorted.2" - - { - # staging -> something -> build-list - join -1 1 -2 1 -o 1.1,1.2 \ - "${tmp_dir}/known-connections.sorted.1" \ - "${tmp_dir}/done-packages" | \ - sort -k2,2 | \ - join -1 2 -2 1 -o 1.1,1.2,2.1,2.2 \ - - \ - "${tmp_dir}/known-connections.sorted.1" | \ - sort -k4,4 | \ - join -1 4 -2 1 -o 1.1,1.2,1.3,1.4 \ - - \ - "${tmp_dir}/build-list.sorted-by-package" | \ - sed ' - s|^\(\S\+ \S\+\) |\1\n| - ' - # staging -> something -> staging - join -1 1 -2 1 -o 1.1,1.2 \ - "${tmp_dir}/known-connections.sorted.1" \ - "${tmp_dir}/done-packages" | \ - sort -k2,2 | \ - join -1 2 -2 1 -o 1.1,1.2,2.1,2.2 \ - - \ - "${tmp_dir}/known-connections.sorted.1" | \ - sort -k4,4 | \ - join -1 4 -2 1 -o 1.1,1.2,1.3,1.4 \ - - \ - "${tmp_dir}/done-packages" | \ - sed ' - s|^\(\S\+ \S\+\) |\1\n| - ' - # build-list -> something -> staging - join -1 1 -2 1 -o 1.1,1.2 \ - "${tmp_dir}/known-connections.sorted.1" \ - "${tmp_dir}/build-list.sorted-by-package" | \ - sort -k2,2 | \ - join -1 2 -2 1 -o 1.1,1.2,2.1,2.2 \ - - \ - "${tmp_dir}/known-connections.sorted.1" | \ - sort -k4,4 | \ - join -1 4 -2 1 -o 1.1,1.2,1.3,1.4 \ - - \ - "${tmp_dir}/done-packages" | \ - sed ' - s|^\(\S\+ \S\+\) |\1\n| - ' - cat "${tmp_dir}/build-order" - } | \ - sort -u | \ - sponge "${tmp_dir}/build-order" - - rm \ - "${tmp_dir}/done-packages" \ - "${tmp_dir}/known-connections.sorted.1" \ - "${tmp_dir}/known-connections.sorted.2" - - else - - grep " $(str_to_regex "${target_package}")\$" "${tmp_dir}/original-build-order" | \ - sort -u > \ - "${tmp_dir}/build-order" - printf '0 target-package %s\n' "${target_package}" > \ - "${tmp_dir}/knots" - - last_sum='' - current_sum=$(sha512sum "${tmp_dir}/build-order") - while ! [ "${last_sum}" = "${current_sum}" ]; do - - last_sum="${current_sum}" - - awk '{print $1}' "${tmp_dir}/build-order" | \ - sort -u > \ - "${tmp_dir}/new" - { - cat \ - "${tmp_dir}/original-build-order" \ - "${tmp_dir}/known-split-packages" - sed '/ base\(-devel\)\?$/d' "${tmp_dir}/known-groups" - } | \ - sort -k2,2 | \ - join -1 2 -2 1 -o 1.1,1.2 - "${tmp_dir}/new" | \ - sponge -a "${tmp_dir}/build-order" - - rm "${tmp_dir}/new" - - sort -u "${tmp_dir}/build-order" | \ - sponge "${tmp_dir}/build-order" - - current_sum=$(sha512sum "${tmp_dir}/build-order") - - done - - fi + # shellcheck disable=SC2016 + { + printf 'CREATE TEMPORARY TABLE `relevant_binary_packages` (`id` BIGINT, UNIQUE KEY (`id`));\n' + printf 'CREATE TEMPORARY TABLE `relevant_binary_packages_copy` (`id` BIGINT, UNIQUE KEY (`id`));\n' + printf 'CREATE TEMPORARY TABLE `relevant_install_targets` (`id` BIGINT, UNIQUE KEY (`id`));\n' -# grep -v '^\(\S\+\) \1$' "${tmp_dir}/build-order" | \ -# sponge "${tmp_dir}/build-order" + if [ "${target_package}" = 'ALL' ]; then - # shellcheck disable=SC2129 - tr ' ' '\n' < \ - "${tmp_dir}/build-order" | \ - sort -u | \ - join -j 1 - "${tmp_dir}/build-list.sorted-by-package" | \ - while read -r pkg rev mod_rev repo; do - if [ -f "${work_dir}/package-states/${pkg}.${rev}.${mod_rev}.${repo}.broken" ]; then - printf '1 broken-build-list-package %s\n' "${pkg}" - elif [ -f "${work_dir}/package-states/${pkg}.${rev}.${mod_rev}.${repo}.blocked" ]; then - printf '2 blocked-build-list-package %s\n' "${pkg}" - else - printf '3 build-list-package %s\n' "${pkg}" - fi - done >> \ - "${tmp_dir}/knots" + printf 'INSERT IGNORE INTO `relevant_binary_packages` (`id`)' + printf ' SELECT DISTINCT `binary_packages`.`id`' + printf ' FROM `repositories`' + mysql_join_repositories_repository_stabilities + printf ' AND `repository_stabilities`.`name` IN ("unbuilt","staging")' + mysql_join_repositories_binary_packages + printf ';\n' - { - sort -u "${work_dir}/deletion-list" - tr ' ' '\n' < \ - "${tmp_dir}/build-order" | \ - sort -u - } | \ - sort | \ - uniq -d | \ - xargs -r printf '4 deletion-list-package %s\n' >> \ - "${tmp_dir}/knots" + else - { - awk '{print $2}' "${tmp_dir}/known-split-packages" | \ - sort -u - tr ' ' '\n' < \ - "${tmp_dir}/build-order" | \ - sort -u - } | \ - sort | \ - uniq -d | \ - xargs -r printf '5 split-package %s\n' >> \ - "${tmp_dir}/knots" + printf 'CALL calculate_dependencies_of_package_upto_first_built_one(from_base64("%s"));\n' \ + "$( + printf '%s' "${target_package}" | \ + base64 -w0 + )" - { - cat "${tmp_dir}/known-packages" - tr ' ' '\n' < \ - "${tmp_dir}/build-order" | \ - sort -u - } | \ - sort | \ - uniq -d | \ - xargs -r printf '6 package %s\n' >> \ - "${tmp_dir}/knots" + fi - { - awk '{print $2}' "${tmp_dir}/known-groups" | \ - sort -u - tr ' ' '\n' < \ - "${tmp_dir}/build-order" | \ - sort -u + printf 'INSERT IGNORE INTO `relevant_binary_packages_copy` (`id`)' + printf ' SELECT `relevant_binary_packages`.`id` FROM `relevant_binary_packages`' + printf ';\n' + printf 'INSERT IGNORE INTO `relevant_install_targets` (`id`)' + printf ' SELECT DISTINCT `install_target_providers`.`install_target`' + printf ' FROM `relevant_binary_packages`' + mysql_join_binary_packages_install_target_providers 'relevant_binary_packages' + mysql_join_install_target_providers_dependencies + mysql_join_dependencies_dependency_types + printf ' AND `dependency_types`.`relevant_for_building`' + printf ' JOIN `relevant_binary_packages_copy` ON `relevant_binary_packages_copy`.`id`=`dependencies`.`dependent`' + printf ';\n' + + # we return lines with either: + # "knot" $type $identifier $label + # or + # "edge" $type $from_knot $to_knot + printf 'SELECT DISTINCT' + printf ' "knot",' + printf 'IF(`build_assignments`.`is_%s`,"%s-build-list-pkgbase",' \ + 'blocked' 'broken' \ + 'broken' 'broken' + printf '"build-list-pkgbase"' + printf ')),' + printf 'CONCAT("pkgbase:",`package_sources`.`id`),' + printf '`package_sources`.`pkgbase`' + printf ' FROM `relevant_binary_packages`' + printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + printf ' AND `repository_stabilities`.`name`="unbuilt"' + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_package_sources + printf ';\n' + + printf 'SELECT DISTINCT' + printf ' "knot",' + printf 'CONCAT("pkgname-",`repository_stabilities`.`name`),' + printf 'CONCAT("pkgname:",`binary_packages`.`id`),' + printf '`binary_packages`.`pkgname`' + printf ' FROM `relevant_binary_packages`' + printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + printf ';\n' + + printf 'SELECT DISTINCT' + printf ' "knot","install-target",' + printf 'CONCAT("install-target:",`install_targets`.`id`),' + printf '`install_targets`.`name`' + printf ' FROM `relevant_install_targets`' + printf ' JOIN `install_targets` ON `install_targets`.`id`=`relevant_install_targets`.`id`' + printf ';\n' + + printf 'SELECT DISTINCT' + printf ' "edge","builds"' + printf ',CONCAT("%s:",`%s`.`id`)' \ + 'pkgbase' 'package_sources' \ + 'pkgname' 'binary_packages' + printf ' FROM `relevant_binary_packages`' + printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_repositories + mysql_join_repositories_repository_stabilities + printf ' AND `repository_stabilities`.`name`="unbuilt"' + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_package_sources + printf ';\n' + + printf 'SELECT DISTINCT' + printf ' "edge","provides"' + printf ',CONCAT("%s:",`%s`.`id`)' \ + 'pkgname' 'binary_packages' \ + 'install-target' 'install_targets' + printf ' FROM `relevant_binary_packages`' + printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_install_target_providers + printf ' JOIN `relevant_install_targets` ON `relevant_install_targets`.`id`=`install_target_providers`.`install_target`' + mysql_join_install_target_providers_install_targets + printf ';\n' + + printf 'SELECT DISTINCT' + printf ' "edge",CONCAT("depends:",`dependency_types`.`name`)' + printf ',CONCAT("%s:",`%s`.`id`)' \ + 'install-target' 'install_targets' \ + 'pkgname' 'binary_packages' + printf ' FROM `relevant_binary_packages`' + printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`' + mysql_join_binary_packages_dependencies + printf ' JOIN `relevant_install_targets` ON `relevant_install_targets`.`id`=`dependencies`.`depending_on`' + mysql_join_dependencies_dependency_types + printf ' AND `dependency_types`.`relevant_for_building`' + mysql_join_dependencies_install_targets + printf ';\n' } | \ - sort | \ - uniq -d | \ - xargs -r printf '7 group %s\n' >> \ - "${tmp_dir}/knots" - - tr ' ' '\n' < \ - "${tmp_dir}/build-order" | \ - sort -u | \ - xargs -r printf '100 unknown %s\n' >> \ + mysql_run_query | \ + sed ' + y/\t/ / + /^knot /{ + s/^\S\+ // + w '"${tmp_dir}"'/knots + d + } + /^edge /{ + s/^\S\+ // + w '"${tmp_dir}"'/edges + d + } + ' >&2 + + mkdir "${tmp_dir}/find-identical" + + awk '{ + print $1 >> "'"${tmp_dir}"'/find-identical/characteristics-"$2 + }' < \ "${tmp_dir}/knots" + awk '{ + print $1 " X " $3 >> "'"${tmp_dir}"'/find-identical/characteristics-"$2 + print $1 " " $2 " X" >> "'"${tmp_dir}"'/find-identical/characteristics-"$3 + }' < \ + "${tmp_dir}/edges" + + modifier=$( + find "${tmp_dir}/find-identical" -maxdepth 1 \ + -type f \ + -name 'characteristics-*' | \ + while read -r file; do + printf '%s ' "${file##*/}" + sort -u "${file}" | \ + sha512sum | \ + awk '{print $1}' + done | \ + sort -k2,2 | \ + uniq -Df1 | \ + sed 's,^characteristics-,,' | \ + sed ' + :a + $!N + s/^\(\S\+\) \(\S\+\)\n\(\S\+ \)\2$/\1+\3\2/ + ta + P + D + ' | \ + cut -d' ' -f1 | \ + sed ' + s/^/s,\\(/ + s/+\([^+[:space:]]\+\)$/\\)\\(\\s\\|$\\),\1\\2,g;/ + s/+/\\|/g + ' + ) + rm -rf --one-file-system "${tmp_dir}/find-identical" - sort -k3,3 -k1n,1 "${tmp_dir}/knots" | \ - uniq -f2 | \ - awk '{print $3 " " $1 " " $2}' | \ - sort -k2,3 | \ + sed "${modifier}" "${tmp_dir}/knots" | \ + sort -k2,2 -k1,1 | \ + sed ' + :a + $!N + s/^\(\S\+ \S\+ \)\(\S\+\)\n\1\(\S\+\)$/\1\2<nl>\3/ + ta + P + D + ' | \ sponge "${tmp_dir}/knots" - mkdir "${tmp_dir}/neighbours" - cat "${tmp_dir}/knots" "${tmp_dir}/build-order" | \ - awk '{ - print $1 " " $2 > "'"${tmp_dir}/neighbours/"'"$1; - print $1 " " $2 > "'"${tmp_dir}/neighbours/"'"$2; - }' - - find "${tmp_dir}/neighbours" -maxdepth 1 -type f | \ - while read -r file; do - sed ' - s@\(^\| \)'"$(str_to_regex "${file##*/}")"'\( \|$\)@\1<knot>\2@ - s@\(^\| \)'"$(str_to_regex "${file##*/}")"'\( \|$\)@\1<knot>\2@ - ' "${file}" | \ - sort -u | \ - sponge "${file}" - done - - find "${tmp_dir}/neighbours" -maxdepth 1 -type f -exec \ - sha512sum {} \; | \ - sed 's|^\(\S\+\) .*/\([^/]\+\)$|\2 \1|' | \ - sort -k2,2 | \ - uniq -f1 -D | \ - awk '{print $2 " " $1}' > \ - "${tmp_dir}/sums" - - rm -rf --one-file-system "${tmp_dir}/neighbours" - - sed ' - :a - $!N - s|^\(\S\+\) \([^\n]\+\)\n\1 |\1 \2<nl>| - ta - P - D - ' "${tmp_dir}/sums" | \ - join -1 1 -2 1 -o 2.2,1.2 - "${tmp_dir}/sums" | \ - while read -r original replacement; do - sed -i ' - s@\( \|^\)'"$(str_to_regex "${original}")"'\( \|$\)@\1'"${replacement}"'\2@ - s@\( \|^\)'"$(str_to_regex "${original}")"'\( \|$\)@\1'"${replacement}"'\2@ - ' "${tmp_dir}/build-order" "${tmp_dir}/knots" - done - - grep -v '^\(\S\+\) \1$' "${tmp_dir}/build-order" | \ + sed "${modifier}" "${tmp_dir}/edges" | \ sort -u | \ - sponge "${tmp_dir}/build-order" - sort -u "${tmp_dir}/knots" | \ - sponge "${tmp_dir}/knots" + sponge "${tmp_dir}/edges" { printf '%s\n' \ 'digraph dependencies {' \ ' fontname=dejavu;' - cut -d' ' -f1,3 < \ - "${tmp_dir}/knots" | \ - while read -r who what; do - if [ "${what}" = 'broken-build-list-package' ]; then + # knots: $type $identifier $label + # edges: $type $from_knot $to_knot + + while read -r type id label; do + case "${type}" in + 'broken-build-list-pkgbase') color='#ff0000' - elif [ "${what}" = 'blocked-build-list-package' ]; then + ;; + 'blocked-build-list-pkgbase') color='#d00000' - elif [ "${what}" = 'build-list-package' ]; then + ;; + 'build-list-pkgbase') color='#800000' - elif [ "${what}" = 'deletion-list-package' ]; then - color='#808080' - elif [ "${what}" = 'group' ]; then - color='#0000ff' - elif [ "${what}" = 'package' ]; then - color='#000000' - elif [ "${what}" = 'split-package' ]; then - color='#8080ff' - elif [ "${what}" = 'staging-package' ]; then + ;; + 'pkgname-unbuilt') + color='#800000' + ;; + 'pkgname-staging') color='#008000' - elif [ "${what}" = 'target-package' ]; then - color='#00ff00' - else + ;; + 'pkgname-stable'|'pkgname-testing'|'pkgname-standalone') + color='#000000' + ;; + 'install-target') + color='#000080' + ;; + *) color='#ff80ff' - fi - printf ' "%s" [fontcolor="%s"];\n' "${who}" "${color}" - done + label="${label} (${type})" + ;; + esac + printf ' "%s" [label="%s", fontcolor="%s"];\n' "${id}" "${label}" "${color}" + done < \ + "${tmp_dir}/knots" - sed 's|\\|\\\\|g' "${tmp_dir}/build-order" | \ - xargs -rn2 printf ' "%s" -> "%s";\n' | \ - sort -u + while read -r type from_knot to_knot; do + printf '"%s" -> "%s";\n' \ + "${from_knot}" "${to_knot}" + done < \ + "${tmp_dir}/edges" printf '%s\n' \ '}' @@ -486,16 +308,12 @@ for target_package in "$@"; do ' "${tmp_dir}/input" line_count=$(wc -l < "${tmp_dir}/input") - if [ "${line_count}" -gt 500 ] && [ "${target_package}" != 'ALL' ]; then - sed -i '/"base\(-devel\)\?"/d' "${tmp_dir}/input" - line_count=$(wc -l < "${tmp_dir}/input") - if [ "${line_count}" -gt 700 ]; then - rm -f "${output}" - >&2 printf 'Skipping graph for "%s" - would be too big (%d).\n' \ - "${target_package}" \ - "${line_count}" - continue - fi + if [ "${target_package}" != 'ALL' ] && [ "${line_count}" -gt 1000 ]; then + rm -f "${output}" + >&2 printf 'Skipping graph for "%s" - would be too big (%d).\n' \ + "${target_package}" \ + "${line_count}" + continue fi printf 'small enough (%s): %d\n' \ "${target_package}" \ diff --git a/bin/slave-build-connect b/bin/slave-build-connect index 2ca72a3..f8ee9ad 100755 --- a/bin/slave-build-connect +++ b/bin/slave-build-connect @@ -3,11 +3,12 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" -if [ "${SSH_ORIGINAL_COMMAND%% *}" = "get-assignment" ] || \ - [ "${SSH_ORIGINAL_COMMAND%% *}" = "return-assignment" ]; then +if [ "x${SSH_ORIGINAL_COMMAND%% *}" = 'xget-assignment' ] || \ + [ "x${SSH_ORIGINAL_COMMAND%% *}" = 'xreturn-assignment' ] || \ + [ "x${SSH_ORIGINAL_COMMAND%% *}" = 'xping-from-slave' ]; then # small check to prevent some shell-injections - if echo "${SSH_ORIGINAL_COMMAND}" | \ + if printf '%s\n' "${SSH_ORIGINAL_COMMAND}" | \ grep -q '[^-a-zA-Z0-9.+_ ]'; then >&2 printf 'Invalid command: "%s".\n' "${SSH_ORIGINAL_COMMAND}" @@ -28,7 +29,7 @@ if [ "${SSH_ORIGINAL_COMMAND%% *}" = "get-assignment" ] || \ base64 -w0 )" } | \ - ${mysql_command} + mysql_run_query slave="$1" /bin/sh -c "${base_dir}/bin/${SSH_ORIGINAL_COMMAND}" diff --git a/bin/strict-bashism-check b/bin/strict-bashism-check index 3bb3884..cbf7e5b 100755 --- a/bin/strict-bashism-check +++ b/bin/strict-bashism-check @@ -49,37 +49,54 @@ trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT git archive "${tree}" | \ tar -C "${tmp_dir}" -x +if ! cd "${tmp_dir}"; then + echo 'Cannot cd.' + exit 1 +fi + errors=$( - cd "${tmp_dir}" || \ - echo 'Cannot cd.' - find "bin" "conf" -type f -not -executable -not -name '.gitignore' + find bin conf lib -type f -not -executable -not -name '.gitignore' ) if [ -n "${errors}" ]; then - >&2 echo 'Non-executable files found in bin/ or conf/:' + >&2 echo 'Non-executable files found in bin/, conf/ or lib/:' >&2 echo "${errors}" exit 1 fi errors=$( -# shellcheck disable=SC2016 - find bin conf -type f -executable -exec grep -H '="\$(' {} \; + grep -r '\(\s\|^\)mysql buildmaster\($\|\s\)' bin lib conf ) if [ -n "${errors}" ]; then - >&2 echo 'Unnecessary quotes found:' + >&2 echo 'Style error: call "mysql_run_query" instead of "mysql buildmaster":' >&2 echo "${errors}" exit 1 fi errors=$( - cd "${tmp_dir}" || \ - echo 'Cannot cd.' - shellcheck -x bin/* conf/* 2>&1 +# shellcheck disable=SC2016 + find bin conf lib -type f -executable -exec grep -H '="\$(' {} \; ) if [ -n "${errors}" ]; then - >&2 echo 'shellcheck complains about the following:' + >&2 echo 'Unnecessary quotes found:' >&2 echo "${errors}" exit 1 fi + +if which shellcheck >/dev/null 2>&1; then + errors=$( + find bin conf lib \ + -type f \ + -not -name 'opcode_list' \ + -not -name '.*' \ + -exec shellcheck -x '{}' \; 2>&1 + ) + + if [ -n "${errors}" ]; then + >&2 echo 'shellcheck complains about the following:' + >&2 echo "${errors}" + exit 1 + fi +fi diff --git a/bin/why-dont-you b/bin/why-dont-you index 2e3ea86..e873c71 100755 --- a/bin/why-dont-you +++ b/bin/why-dont-you @@ -7,6 +7,9 @@ # shellcheck source=conf/default.conf . "${0%/*}/../conf/default.conf" +# TODO: reintrocude "keep", "stubbornly_keep", "stabilize" and "unstage" +# using information from the database. + action="$1" shift @@ -17,342 +20,164 @@ case "${action}" in 'build') - for pkg in "$@"; do - { - grep "^$(str_to_regex "${pkg}") " "${work_dir}/build-list" || \ - >&2 printf '"%s" is not on the build list.\n' "${pkg}" - } | \ - while read -r package git_revision mod_git_revision repository; do - - if [ -f "${work_dir}/package-states/$1.$2.$3.$4.locked" ]; then - printf '"%s" is locked by ' "$1" - sort -u < \ - "${work_dir}/package-states/$1.$2.$3.$4.locked" \ - sed ' - :a - $!{ - N - s/\n/, / - ba - } - s/$/./ - ' - continue - fi - if [ -f "${work_dir}/package-states/$1.$2.$3.$4.blocked" ]; then - printf '"%s" is blocked: "' "${pkg}" - tr '[:space:]' ' ' < \ - "${work_dir}/package-states/$1.$2.$3.$4.blocked" | \ - sed ' - s| \+| | - s|^ || - s| $|| - ' - printf '"\n' - continue - fi - - unmet_dependencies=$(find_dependencies_on_build_list "${package}" "${git_revision}" "${mod_git_revision}" "${repository}") - if [ -n "${unmet_dependencies}" ]; then - printf '"%s" has unmet dependencies:\n' "${package}" - echo "${unmet_dependencies}" | \ - while read -r dep; do - grep -lxF "${dep}" "${work_dir}/package-infos/"*".builds" | \ - sed ' - s|^.*/|| - s|\(\.[^.]\+\)\{4\}|| - ' - done | \ - sort -u - printf '\n' - - continue - fi - - if [ -f "${work_dir}/package-states/${package}.${git_revision}.${mod_git_revision}.${repository}.broken" ]; then - printf '"%s" is broken (%sx built), but would be built\n' \ - "${pkg}" \ - "$(wc -l < "${work_dir}/package-states/${package}.${git_revision}.${mod_git_revision}.${repository}.broken")" - else - printf '"%s" would be built\n' "${pkg}" - fi - done - - done - - ;; - - 'stabilize'|'unstage') - - if [ "${action}" = 'stabilize' ]; then - suffix='tested' - else - suffix='done' - fi - + # shellcheck disable=SC2016 { - 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 + 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' } | \ - 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" | \ + mysql_run_query | \ + tr '\t' ' ' | \ + sort -k7,7 -k6,6 -k5,5 | \ sed ' - s|^|'"${work_dir}/package-infos/"'| - s|$|.run-depends| + / 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 ' | \ - # 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:| | + s/NULL,//g ' | \ - 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 + 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 - - 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 + 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 - 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 [ "${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 - 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 + 'stabilize'|'unstage') - build_depends=$( - echo "${build_depends}" | \ - sort -u - ) + printf 'Sry, "why-dont-you %s" is unavailable, until someone recodes it to look into the database.\n' "${action}" - 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}" + 'keep'|'stubbornly_keep') - done + printf 'Sry, "why-dont-you %s" is unavailable, until someone recodes it to look into the database.\n' "${action}" ;; diff --git a/conf/default.conf b/conf/default.conf index 6ee991b..11483a0 100755 --- a/conf/default.conf +++ b/conf/default.conf @@ -14,13 +14,13 @@ else base_dir=$(printf '%s/..' "$(dirname "$(readlink -f "$0")")") fi -# shellcheck source=bin/common-functions -. "${base_dir}/bin/common-functions" -# shellcheck source=bin/mysql-functions -. "${base_dir}/bin/mysql-functions" - work_dir="${base_dir}/work" +# shellcheck source=lib/common-functions +. "${base_dir}/lib/common-functions" +# shellcheck source=lib/mysql-functions +. "${base_dir}/lib/mysql-functions" + repo_names='packages community archlinux32' repo_paths__packages="${work_dir}/repos/packages" repo_paths__community="${work_dir}/repos/community" @@ -35,7 +35,7 @@ repo_key='0xdeadbeef' package_key='0x15eebadc0de' # what can be done via the email interface -possible_email_actions='stabilize block unblock schedule' +possible_email_actions='stabilize block unblock schedule copy-to-build-support' # how many lines of email logs should be kept maximally max_mail_log_lines=10 @@ -133,9 +133,6 @@ if [ -z "${package_database_lock_file}" ]; then fi mkdir -p "${work_dir}" -touch "${work_dir}/build-list" -touch "${work_dir}/deletion-list" -mkdir -p "${work_dir}/build-list.loops" for repo in ${repo_names}; do diff --git a/bin/common-functions b/lib/common-functions index d5b6eaa..8425e9a 100755 --- a/bin/common-functions +++ b/lib/common-functions @@ -2,8 +2,6 @@ # contains functions used by more than one script -# TODO: include link depenendencies in run-depends metadata - # shellcheck disable=SC2039 if [ -z "${base_dir}" ]; then @@ -11,6 +9,17 @@ if [ -z "${base_dir}" ]; then . 'conf/default.conf' fi +if [ ! -s "${work_dir}/build-master-sanity" ]; then + { + date + printf 'sourcing common-functions for %s\n' "$0" + printf '%s parameters:' "$#" + printf ' "%s"' "$@" + printf '\n' + } >> \ + "${work_dir}/command-log" +fi + # find_pkgbuilds package repository git_repository git_revision mod_git_revision # find the PKGBUILD and modification of $package from $repository # sets $PKGBUILD and $PKGBUILD_mod @@ -79,262 +88,25 @@ find_repository_with_commit() { find_git_repository_to_package_repository() { local repository - local package_repository - local repo_path - - package_repository="$1" - - if [ "$1" = 'build-support' ]; then - echo 'packages' - return 0 - fi - - for repository in ${repo_names}; do - if [ "${repository}" = "archlinux32" ]; then - continue - fi - eval 'repo_path="${repo_paths__'"${repository}"'}"' - if git -C "${repo_path}" archive "$(cat "${work_dir}/${repository}.revision")" -- | \ - tar -t --wildcards '*/repos' | \ - grep '^\([^/]\+/\)\{3\}PKGBUILD$' | \ - cut -d/ -f3 | \ - sed 's|-[^-]\+$||' | \ - sort -u | \ - grep -qxF "${package_repository}"; then - echo "${repository}" - return 0 - fi - done - >&2 echo "can't find git repository with package repository '$1'" - exit 1 - -} - -# generate_package_metadata $package $git_revision $mod_git_revision $repository -# or -# generate_package_metadata $package.$git_revision.$mod_git_revision.$repository -# generate the meta data files of a package (dependencies, built packages, ...) - -generate_package_metadata() { - - local package="$1" - local git_revision="$2" - local mod_git_revision="$3" - local repository="$4" - local file_prefix - local file - local PKGBUILD - - if [ $# -eq 1 ]; then - # second form - repository="${package##*.}" - package="${package%.*}" - mod_git_revision="${package##*.}" - package="${package%.*}" - git_revision="${package##*.}" - package="${package%.*}" - fi - - file_prefix="${work_dir}/package-infos/${package}.${git_revision}.${mod_git_revision}.${repository}" - - if [ -e "${file_prefix}.builds" ] && \ - [ -e "${file_prefix}.build-depends" ] && \ - [ -e "${file_prefix}.run-depends" ] && \ - [ -e "${file_prefix}.groups" ] && \ - [ -e "${file_prefix}.packages" ]; then - return 0 - fi - if ! make_source_info "${package}" "${repository}" "${git_revision}" "${mod_git_revision}" "${file_prefix}.SRCINFO"; then - printf '"make_source_info %s %s %s %s %s" failed.\n' "${package}" "${repository}" "${git_revision}" "${mod_git_revision}" "${file_prefix}.SRCINFO" - exit 1 - fi - if [ ! -s "${file_prefix}.SRCINFO" ]; then - >&2 printf '"%s" not created by "make_source_info" - eh, what?' "${file_prefix}.SRCINFO" - exit 1 - fi - - # otherwise this just calls for trouble - sed -i ' - /^[^=]*=\s*$/d - s/_i686\(\s*=\)/\1/ - ' "${file_prefix}.SRCINFO" - - # extract "groups" = groups \cup provides - grep "$(printf '^\t\\(groups\\|provides\\) = ')" "${file_prefix}.SRCINFO" | \ - cut -d= -f2 | \ - sed 's|^\s\+||; s|[<>]$||' | \ - sort -u > \ - "${file_prefix}.groups" - - # extract "packages" = pkgname - grep '^pkgname = ' "${file_prefix}.SRCINFO" | \ - cut -d= -f2 | \ - sed 's|^\s\+||; s|[<>]$||' | \ - sort -u > \ - "${file_prefix}.packages" - - # extract "builds" = provides \cup pkgname \cup groups - cat "${file_prefix}.groups" "${file_prefix}.packages" | \ - sort -u > \ - "${file_prefix}.builds" - - # extract "build-depends" = makedepends \cup checkdepends \cup depends \cup \{ base, base-devel \} \setminus "builds" - { - { - printf 'all_depend = %s\n' 'base' 'base-devel' - sed -n "$( - printf '/^\t%s = /p\n' \ - 'depends' \ - 'makedepends' \ - 'checkdepends' - )" "${file_prefix}.SRCINFO" - } | \ - cut -d= -f2 | \ - sed 's|^\s\+||; s|[<>]$||' | \ - sort -u - sed 'p' "${file_prefix}.builds" - } | \ - sort | \ - uniq -u > \ - "${file_prefix}.build-depends" - - # extract "run-depends" = depends \cup \{ base \} \setminus "builds" - { + repository=$( + # shellcheck disable=SC2016 { - printf 'all_depend = %s\n' 'base' - sed -n "$(printf '/^\tdepends = /p')" "${file_prefix}.SRCINFO" + printf 'SELECT `git_repositories`.`name` FROM `git_repositories`' + mysql_join_git_repositories_upstream_repositories + printf ' WHERE `upstream_repositories`.`name`=from_base64("%s");\n' \ + "$(printf '%s' "$1" | base64 -w0)" } | \ - cut -d= -f2 | \ - sed 's|^\s\+||; s|[<>]$||' | \ - sort -u - sed 'p' "${file_prefix}.builds" - } | \ - sort | \ - uniq -u > \ - "${file_prefix}.run-depends" - - rm "${file_prefix}.SRCINFO" - -} - -# delete_old_metadata -# delete old (=unneeded) meta data of packages - -delete_old_metadata() { - - local current_metadata - - current_metadata=$( - find "${work_dir}/package-infos" -mindepth 1 -maxdepth 1 -printf '%f\n' | \ - sed ' - s|\.[^.]\+$|| - s|\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)$| \1 \2 \3| - ' | \ - sort -u + mysql_run_query ) + if [ -z "${repository}" ]; then + >&2 echo "can't find git repository with package repository '$1'" + exit 1 + else + echo "${repository}" + return 0 + fi - ( # the new shell is intentional - # what we have - echo "${current_metadata}" - - # package-states should stay - find "${work_dir}/package-states" -mindepth 1 -maxdepth 1 -printf '%f\n' | \ - sed ' - s|\.\([^.]\+\)\.\([^.]\+\)\.\([^.]\+\)\.[^.]\+$| \1 \2 \3| - ' | \ - sort -u | \ - sed 'p' - - # build-list items should stay - sed 'p' "${work_dir}/build-list" - - tmp_dir=$(mktemp -d 'tmp.common-functions.delete_old_metadata.XXXXXXXXXX' --tmpdir) - trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT - - echo "${current_metadata}" | \ - sort -k1,1 > \ - "${tmp_dir}/current-metadata" - - # the newest of the following should stay: - { - # deletion-list items - cat "${work_dir}/deletion-list" - # all packages in the repos - for repo in ${repo_names}; do - eval 'git -C "${repo_paths__'"${repo}"'}" archive '"$(cat "${work_dir}/${repo}.revision")" | \ - tar -t | \ - sed ' - s|/$|| - /\//d - ' - done - } | \ - sort -u | \ - join -j 1 -o 2.2,2.3,2.4,2.1 - "${tmp_dir}/current-metadata" | \ - sort -k4,4 > \ - "${tmp_dir}/find-newest-revisions" - - uniq -uf3 < \ - "${tmp_dir}/find-newest-revisions" | \ - awk '{print $4 " " $1 " " $2 " " $3}' | \ - sed 'p' - - uniq -Df3 < \ - "${tmp_dir}/find-newest-revisions" | \ - uniq --group=append -f3 | \ - { - revs='' - mod_revs='' - opkg='' - orepo='' - while read -r rev mod_rev repo pkg; do - - if [ -z "${rev}" ] && \ - [ -z "${mod_rev}" ] && \ - [ -z "${repo}" ] && \ - [ -z "${pkg}" ]; then - - printf '%s %s %s %s\n' \ - "$( - printf '%s\n' ${revs} | \ - find_newest_of_git_revisions - )" \ - "$( - printf '%s\n' ${mod_revs} | \ - find_newest_of_git_revisions - )" \ - "${orepo}" \ - "${opkg}" - - revs='' - mod_revs='' - orepo='' - opkg='' - continue - fi - revs=$( - # shellcheck disable=SC2086 - printf '%s\n' ${revs} ${rev} | \ - sort -u - ) - mod_revs=$( - # shellcheck disable=SC2086 - printf '%s\n' ${mod_revs} ${mod_rev} | \ - sort -u - ) - orepo="${repo}" - opkg="${pkg}" - done - } | \ - awk '{print $4 " " $1 " " $2 " " $3}' | \ - sed 'p' - ) | \ - sort | \ - uniq -u | \ - while read -r pkg rev mod_rev repo; do - rm -f "${work_dir}/package-infos/${pkg}.${rev}.${mod_rev}.${repo}."* - done } # repository_of_package $package.$repo_revision.$mod_repo_revision.$repository @@ -365,7 +137,7 @@ repository_of_package() { # official_or_community $package.$repo_revision.$mod_repo_revision.$repository $ending # print wether the specified package is an official package (print # $ending) or a community package (print 'community-$ending') or a -# build-suppor package (print 'build-support') +# build-support package (print 'build-support') official_or_community() { local prepo @@ -394,6 +166,9 @@ ls_master_mirror() { } +# TODO: the actions of remove_old_package_versions should be done +# on basis of the information in the database + # remove_old_package_versions $arch $repository $package_file # removes all older (not-newer) versions of $package_file @@ -417,6 +192,84 @@ remove_old_package_versions() { local repository="$2" local package="$3" + local pkgname + local epoch + local pkgver + local pkgrel + local sub_pkgrel + pkgname="${package%-*}" + pkgrel="${pkgname##*-}" + sub_pkgrel="${pkgrel##*.}" + if [ "${sub_pkgrel}" = "${pkgrel}" ]; then + sub_pkgrel='0' + else + pkgrel="${pkgrel%.*}" + fi + pkgname="${pkgname%-*}" + pkgver="${pkgname##*-}" + epoch="${pkgver%%:*}" + if [ "${epoch}" = "${pkgver}" ]; then + epoch='0' + else + pkgver="${pkgver#*:}" + fi + pkgname="${pkgname%-*}" + + # shellcheck disable=SC2016 + { + printf 'SELECT "bogus",CONCAT(from_base64("%s"),"/",from_base64("%s")),1,from_base64("%s");\n' \ + "$( + printf '%s' "${repository}" | \ + base64 -w0 + )" \ + "$( + printf '%s' "${package}" | \ + base64 -w0 + )" \ + "$( + printf '%s' "${package}" | \ + sed ' + s/^.*-\([^-]\+-[^-]\+\)-[^-]\+$/\1/ + ' | \ + base64 -w0 + )" + printf 'SELECT ' + printf '`binary_packages`.`id`,' + printf 'CONCAT(`repositories`.`name`,"/",' + mysql_package_name_query + printf '),' + # should we delete packages of identical version? + printf 'IF((`more_stable_repos`.`id`!=`repositories`.`id`) AND (`more_stable_repos`.`stability`=`repositories`.`stability`),2,0),' + printf 'CONCAT(' + printf 'IF(`binary_packages`.`epoch`=0,"",CONCAT(`binary_packages`.`epoch`,":")),' + printf '`binary_packages`.`pkgver`,"-",' + printf '`binary_packages`.`pkgrel`,".",' + printf '`binary_packages`.`sub_pkgrel`' + printf ')' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_repositories + mysql_join_binary_packages_architectures + printf ' JOIN `repository_stability_relations` ON `repository_stability_relations`.`less_stable`=`repositories`.`stability`' + printf ' JOIN `repositories` AS `more_stable_repos` ON `repository_stability_relations`.`more_stable`=`more_stable_repos`.`stability`' + # name must match + printf ' WHERE `binary_packages`.`pkgname`=from_base64("%s")' \ + "$(printf '%s' "${package%-*-*-*}" | base64 -w0)" + # repository, where package should be deleted, should be less stable + printf ' AND `more_stable_repos`.`name`=from_base64("%s")' \ + "$(printf '%s' "${repository}" | base64 -w0)" + printf ';\n' + } | \ + mysql_run_query | \ + tr '\t' ' ' | \ + expand_version 4 | \ + sort -k4V,4 -k3r,3 | \ + shrink_version 4 | \ + sed -n ' + /^bogus /q + p + ' | \ + cut -d' ' -f1,2 >&2 + # repositories in which older packages should be deleted local delete_older_repositories # repositories in which not-newer packages should be deleted @@ -582,7 +435,7 @@ remove_old_package_versions() { sed 's/ AND$//' printf ';\n' done | \ - ${mysql_command} + mysql_run_query sed ' s| \(\S\+\)$|-\1| @@ -815,6 +668,7 @@ find_package_repository_to_package() { extract_source_directory() { local git_repo="$1" + # shellcheck disable=SC2034 local rev="$2" local mod_rev="$3" local output="$4" @@ -834,39 +688,18 @@ extract_source_directory() { "${output}/PKGBUILD" fi + # we do not want to update pkgver, so we just undefine it + printf 'unset -f pkgver\n' >> \ + "${output}/PKGBUILD" + mangle_pkgbuild "${output}/PKGBUILD" "${sub_pkgrel}" # shellcheck disable=SC2016 sed -i '/^\$Id\$$/d' "${output}/PKGBUILD" -} - -# find_dependencies_on_build_list $package $git_revision $mod_git_revision $repository -# return a list of packages on the build list which are (run- / build- / check-time) -# dependencies of the given package - -find_dependencies_on_build_list() { - - local package="$1" - local git_revision="$2" - local mod_git_revision="$3" - local repository="$4" - - generate_package_metadata "${package}" "${git_revision}" "${mod_git_revision}" "${repository}" - - { - cat "${work_dir}/package-infos/${package}.${git_revision}.${mod_git_revision}.${repository}.build-depends" - awk '{print $1 "." $2 "." $3 "." $4}' < \ - "${work_dir}/build-list" | \ - sed ' - s|^|'"${work_dir}"'/package-infos/| - s|$|\.builds| - ' | \ - xargs -r cat | \ - sort -u - } | \ - sort | \ - uniq -d + # we don't want write permissions on the PKGBUILD - otherwise pkgver() + # will change the version! (**HACK**) + chmod -w "${output}/PKGBUILD" } @@ -969,133 +802,6 @@ shrink_version() { ' } -# find_biggest_subset_of_packages $omega $keep $all_builds $all_depends [ $force ] - -# Return (to stdout) the biggest subset A of the packages in $omega whose -# runtime dependencies in $omega \cup $keep are also in A - -# $all_builds and $all_depends either point to an empty file - then they will get -# filled with cached data for subsequent calls - or to the same files of a previous -# call - -# If non-empty, $force contains packages which are assumed to match the above -# condition without checking. - -# The arguments are names of files with one $package.$revision.$mod_revision.$repository -# per line. - -find_biggest_subset_of_packages() { - - ( # the new shell is intentional - omega="$1" - keep="$2" - all_builds="$3" - all_depends="$4" - if [ $# -eq 4 ]; then - force='/dev/null' - elif [ $# -eq 5 ]; then - force="$5" - else - >&2 printf 'find_biggest_subset_of_packages: Wrong number of arguments: %s given, 4 or 5 expected.' "$#" - return 2 - fi - - if [ ! -s "${all_builds}" ]; then - find "${work_dir}/package-infos/" -maxdepth 1 -name '*.builds' \ - -exec sed ' - s|^|{} | - s|^\S\+/|| - s|\.builds | | - ' {} \; | \ - sort -k2,2 > \ - "${all_builds}" - fi - - if [ ! -s "${all_depends}" ]; then - find "${work_dir}/package-infos/" -maxdepth 1 -name '*.run-depends' \ - -exec sed ' - s|^|{} | - s|^\S\+/|| - s|\.run-depends | | - ' {} \; | \ - grep -v ' base$' | \ - sort -k2,2 > \ - "${all_depends}" - fi - - sort -u "${omega}" | \ - sponge "${omega}" - - temp_dir=$(mktemp -d 'tmp.common-functions.find_biggest_subset_of_packages.XXXXXXXXXX' --tmpdir) - trap 'rm -rf --one-file-system "${temp_dir}"' EXIT - - { - sort -u "${keep}" - cat "${force}" "${force}" - } | \ - sort | \ - uniq -u > \ - "${temp_dir}/keep.new" - touch "${temp_dir}/keep" - - while [ -s "${temp_dir}/keep.new" ]; do - cat "${temp_dir}/keep.new" "${temp_dir}/keep" | \ - sort -u | \ - sponge "${temp_dir}/keep" - - { - # we append all packages which are run-dependencies of keep-packages - # to the keep-list - sed ' - s|^|'"${work_dir}"'/package-infos/| - s|$|.run-depends| - ' "${temp_dir}/keep" | \ - xargs -r grep -HF '' | \ - sed ' - s|^.*/|| - s|\.run-depends:| | - ' | \ - sort -u | \ - sort -k2,2 | \ - uniq -f1 | \ - join -1 2 -2 2 -o 2.1 - "${all_builds}" - - # we append all packages with run-dependencies on the keep-list - # to the keep-list - sed ' - s|^|'"${work_dir}"'/package-infos/| - s|$|.builds| - ' "${temp_dir}/keep" | \ - xargs -r grep -HF '' | \ - sed ' - s|^.*/|| - s|\.builds:| | - ' | \ - sort -u | \ - sort -k2,2 | \ - uniq -f1 | \ - join -1 2 -2 2 -o 2.1 - "${all_depends}" - } | \ - sort -u | \ - join -1 1 -2 1 -o 2.1 - "${omega}" | \ - sort -u > \ - "${temp_dir}/keep.new" - - # "new" is only what has not been there before and what is not forced - cat "${temp_dir}/keep" "${temp_dir}/keep" "${force}" "${force}" "${temp_dir}/keep.new" | \ - sort | \ - uniq -u | \ - sponge "${temp_dir}/keep.new" - done - - cat "${omega}" "${temp_dir}/keep" "${temp_dir}/keep" | \ - sort | \ - uniq -u - - ) - -} - # sort_square_bracket_content $file # sort the content of [] in $file, print to stdout @@ -1168,167 +874,6 @@ smoothen_namcap_log() { sponge "${file}" } -# print_list_of_archaic_packages $source1 $source2 ... -# print a list of packages which have not been touched for a while, -# but which are still in the pipeline, e.g. in $source1, $source2 or ... - -print_list_of_archaic_packages() { - for source in "$@"; do - case "${source}" in - 'testing') - # packages remaining longer than $max_package_age_testing days in testing will be marked tested if no bug for them exists on FS32 - find "${work_dir}/package-states" -mindepth 1 -maxdepth 1 -name '*.testing' -mtime "+${max_package_age_testing}" \ - -exec head -n1 {} \; | \ - "${base_dir}/bin/modify-package-state" -n --tested /dev/stdin - # packages remaining longer than $max_package_age_broken_testing days in testing (w/o being tested!) will be considered outdated - # and no longer block other packages from being moved - find "${work_dir}/package-states" -mindepth 1 -maxdepth 1 -name '*.testing' -mtime "+${max_package_age_broken_testing}" -printf '%f\n' | \ - sed ' - s|\.testing$|| - ' - ;; - 'build-list') - while read -r pkg rev mod_rev repo; do - git_repo=$( - find_repository_with_commit "${rev}" - ) - eval repo_path='"${repo_paths__'"${git_repo}"'}"' - commit_date=$( - git -C "${repo_path}" show -s --format=%ct "${rev}" - ) - mod_commit_date=$( - git -C "${repo_paths__archlinux32}" show -s --format=%ct "${mod_rev}" - ) - if [ "${mod_commit_date}" -gt "${commit_date}" ]; then - commit_date="${mod_commit_date}" - fi - # packages remaining longer than $max_package_age_build_list days on the build list - if [ "$((commit_date + 24*60*60*max_package_age_build_list))" -lt "$(date '+%s')" ]; then - printf '%s %s %s %s\n' \ - "${pkg}" \ - "${rev}" \ - "${mod_rev}" \ - "${repo}" - fi - done < \ - "${work_dir}/build-list" - ;; - 'staging') - # packages remaining longer than $max_package_age_staging days in staging - find "${work_dir}/package-states" -mindepth 1 -maxdepth 1 -name '*.done' -mtime "+${max_package_age_staging}" -printf '%f\n' | \ - sed ' - s|\.done$|| - ' - ;; - *) - >&2 printf 'unknown archaic-source "%s" - skipped.\n' "${source}" - ;; - esac - done | \ - sort -u -} - -# receive_buglist $category -# receive the buglist from bugs.archlinux32.org for $category (e.g. 'Testing') -# and print it to stdout - -receive_buglist() { - local category - category="$1" - curl -LSs 'https://bugs.archlinux32.org/index.php?export_list=Export%20Tasklist' | \ - sed ' - 1 d - /^[^,]\+,"Packages: '"${category}"'",/ ! d - s/^\([^,]\+,\)\{3\}// - s/^\("[^"]\+"\|[^"][^,]*\),"// - s/".*$// - ' -} - -# next_sub_pkgrel $package $git_revision $mod_git_revision $repository -# giv out the next sub-pkgrel of the given package - -next_sub_pkgrel() { - ( # new shell is intentional - package="$1" - git_revision="$2" - mod_git_revision="$3" - repository="$4" - git_repo=$(find_git_repository_to_package_repository "${repository}") - - temp_dir=$(mktemp -d 'tmp.common-functions.next_sub_pkgrel.XXXXXXXXXX' --tmpdir) - trap 'rm -rf --one-file-system "${temp_dir}"' EXIT - - find_pkgbuilds "${package}" "${repository}" "${git_repo}" "${git_revision}" "${mod_git_revision}" - extract_source_directory "${git_repo}" "${git_revision}" "${mod_git_revision}" "${temp_dir}" '0' - - version=$( - cd "${temp_dir}" - makepkg --printsrcinfo | \ - sed -n ' - /^\s*\(epoch\|pkg\(name\|ver\|rel\)\) = /{s|^\s\+||;p} - ' | \ - sed ' - /^pkgname = /{ - s/^.*= // - w pkgnames - d - } - s|^epoch = \(.*\)$|1 \1:| - s|^pkgver = \(.*\)$|2 \1-| - s|^pkgrel = \([^.]*\)\(\..*\)\?$|3 \1| - ' | \ - sort -k1n,1 | \ - sed ' - s|^[0-9] || - :a - N - s|\n[0-9] \(\S\+\)$|\1| - ta - ' - ) - sub_pkgrel=$( - ls_master_mirror 'i686/*/' | \ - sed -n "$( - sed ' - s/$/-'"${version}"'/ - s/\./\\./g - s/^/^/ - s/$/\\(\\.\\([0-9]\\+\\)\\)\\?-[^-]\\+$/ - s|^.*$|/\0/{ s/\0/\\2/; s/^$/0/; p; b; }| - ' "${temp_dir}/pkgnames" - )" | \ - sort -n | \ - tail -n1 - ) - if [ -z "${sub_pkgrel}" ]; then - echo '0' - else - echo "$((sub_pkgrel+1))" - fi - ) -} - -# modification_revision_link "${mod_rev}" "${repo}" "${pkg}" -# print the given modification revision possibly with a html link to github - -modification_revision_link() { - local mod_rev="$1" - local repo="$2" - local pkg="$3" - - if git -C "${repo_paths__archlinux32}" archive "${mod_rev}" -- "${repo}/${pkg}/PKGBUILD" > /dev/null 2>&1; then - printf '<a href="https://github.com/archlinux32/packages/tree/%s/%s/%s">%s</a>\n' \ - "${mod_rev}" \ - "${repo}" \ - "${pkg}" \ - "${mod_rev}" - else - printf '%s\n' \ - "${mod_rev}" - fi -} - # trigger_mirror_refreshs # trigger a refresh of capable tier 1 mirrors (as backup for master mirror) @@ -1346,3 +891,35 @@ trigger_mirror_refreshs() { screen -S trigger-mirror-update -d -m curl -L "${trigger_url}" done } + +# extract_pkgname_epoch_pkgver_pkgrel_sub_pkgrel_arch_from_package_name +extract_pkgname_epoch_pkgver_pkgrel_sub_pkgrel_arch_from_package_name() { + pkgname="$1" + pkgname="${pkgname%.pkg.tar.xz}" + arch="${pkgname##*-}" + pkgname="${pkgname%-*}" + sub_pkgrel="${pkgname##*-}" + pkgname="${pkgname%-*}" + pkgrel="${sub_pkgrel%.*}" + if [ "${pkgrel}" = "${sub_pkgrel}" ]; then + sub_pkgrel='0' + else + sub_pkgrel="${sub_pkgrel##*.}" + fi + epoch="${pkgname##*-}" + pkgname="${pkgname%-*}" + pkgver="${epoch#*:}" + if [ "${pkgver}" = "${epoch}" ]; then + epoch='0' + else + epoch="${epoch%%:*}" + fi +} + +# irc_say +# say content of stdin in irc channel +irc_say() { + if [ -p "${irc_dir}/#archlinux32/in" ]; then + sponge "${irc_dir}/#archlinux32/in" + fi +} diff --git a/lib/mysql-functions b/lib/mysql-functions new file mode 100755 index 0000000..801eca2 --- /dev/null +++ b/lib/mysql-functions @@ -0,0 +1,1122 @@ +#!/bin/sh + +# contains functions used to access mysql db + +# shellcheck disable=SC2016,SC2039 + +if [ -z "${base_dir}" ]; then + # just to make shellcheck happy + . 'conf/default.conf' +fi + +# base64_encode_each encode each line of stdin with base64 + +base64_encode_each() { + local line + + while read -r line; do + printf '%s' \ + "${line}" | \ + base64 -w0 + printf '\n' + done +} + +# mysql_run_query +# wrapper function to query mysql +mysql_run_query() { + local query_file + if [ -s "${work_dir}/build-master-sanity" ]; then + # If the build master is insane, the calling command should only check + # if the build master is still insane - we do not want to log that. + ${mysql_command} -N --raw --batch "$@" + else + # we save the query in a file and delete that file if the query succeeded + query_file=$(mktemp "${work_dir}/tmp.mysql-functions.query.$(date +'%Y-%m-%dT%T').XXXXXX") + cat > "${query_file}" + for _ in {1..10}; do + { + printf '%s %s: ' "$0" "${mysql_command} -N --raw --batch $*" + date + } \ + | tee -a "${work_dir}/mysql.stdin" \ + | tee -a "${work_dir}/mysql.stdout" \ + >> "${work_dir}/mysql.stderr" + cat "${query_file}" \ + >> "${work_dir}/mysql.stdin" + { + ${mysql_command} -N --raw --batch "$@" \ + < "${query_file}" \ + 2>> "${work_dir}/mysql.stderr" \ + && rm "${query_file}" + } \ + | tee -a "${work_dir}/mysql.stdout" + if ! [ -f "${query_file}" ]; then + # success! + for s in \ + "${work_dir}/mysql.stdin" \ + "${work_dir}/mysql.stdout" \ + "${work_dir}/mysql.stderr"; do + { + tail -n 10000 "$s" + printf '%s %s done: ' "$0" "${mysql_command} $*" + date + } \ + | sponge "$s" + done + break + fi + for s in \ + "${work_dir}/mysql.stdin" \ + "${work_dir}/mysql.stdout" \ + "${work_dir}/mysql.stderr"; do + { + printf '%s %s FAILED: ' "$0" "${mysql_command} $*" + date + } \ + >> "$s" + done + done + # a present query_file means there was an error + if [ -f "${query_file}" ]; then + >&2 printf 'I could not complete a mysql query!\n' + if [ ! -s "${work_dir}/build-master-sanity" ]; then + printf '\001ACTION failed to execute a mysql query - can you have a look at "%s"?.\001\n' \ + "${query_file##*/}" \ + | irc_say + fi + echo 'A mysql query failed.' > \ + "${work_dir}/build-master-sanity" + return 2 + fi + fi +} + +# mysql_add_package_source $pkgbase $git_revision $mod_git_revision $upstream_package_repository + +# shellcheck disable=SC2086 +mysql_add_package_source() { + local names='pkgbase git_revision mod_git_revision upstream_package_repository' + local values + local uses_upstream + local uses_modification + local repo + + if git -C "${repo_paths__archlinux32}" archive "$3" -- "$4/$1" >/dev/null 2>&1; then + uses_modification=1 + else + uses_modification=0 + fi + uses_upstream=0 + for repo in ${repo_names}; do + if [ "${repo}" = 'archlinux32' ]; then + continue + fi + if eval 'git -C "${repo_paths__'"${repo}"'}" archive "$2" -- "$1/repos/$4-*/PKGBUILD" 2>/dev/null' | \ + tar -t 2>/dev/null | \ + sed 's,-x86_64/,-any/,' | \ + grep -qFx "$1/repos/$4-any/PKGBUILD"; then + uses_upstream=1 + fi + done + + for _ in ${names}; do + values="${values}$( + printf '%s' "$1" | \ + base64 -w0 + ) " + shift + done + values="${values% }" + + { + printf 'INSERT IGNORE INTO package_sources' + printf ' (' + printf '`%s`, ' ${names} + printf '`uses_upstream`,`uses_modification`' + printf ') SELECT' + printf ' from_base64("%s"), ' ${values% *} + printf ' `upstream_repositories`.`id`,%s,%s' \ + ${uses_upstream} ${uses_modification} + printf ' FROM `upstream_repositories`' + printf ' WHERE `upstream_repositories`.`name` = from_base64("%s");' \ + "${values##* }" + } | \ + mysql_run_query +} + +# mysql_show_binary_package $pkgname $pkgver $pkgrel $sub_pkgrel + +# shellcheck disable=SC2031,SC2086,SC2154 +mysql_show_binary_package() { + local names='pkgname pkgver pkgrel sub_pkgrel' + local name + for name in ${names}; do + eval 'local '"${name}" + eval "${name}"'=$( + printf "%s" "$1" | + base64 -w0 + )' + shift + done + + { + printf 'SELECT' + printf ' `%s`.`%s`,' \ + 'repositories' 'name' \ + 'binary_packages' 'pkgname' \ + 'package_sources' 'pkgver' \ + 'package_sources' 'pkgrel' \ + 'binary_packages' 'sub_pkgrel' \ + 'architectures' 'name' \ + 'package_sources' 'pkgbase' \ + 'package_sources' 'git_revision' \ + 'package_sources' 'mod_git_revision' \ + 'upstream_repositories' 'name' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_architectures + mysql_join_binary_packages_repositories + mysql_join_binary_packages_build_assignments + mysql_join_build_assignments_package_sources + mysql_join_package_sources_upstream_repositories + printf ' WHERE' + printf ' `%s`.`%s` = from_base64("%s") AND' \ + 'binary_packages' 'pkgname' "${pkgname}" \ + 'binary_packages' 'sub_pkgrel' "${sub_pkgrel}" \ + 'package_sources' 'pkgver' "${pkgver}" \ + 'package_sources' 'pkgrel' "${pkgrel}" + printf ';' + } | \ + sed ' + s|, FROM| FROM|g + s|AND;|;|g + ' | \ + mysql_run_query --html --column-names +} + +# mysql_generate_package_metadata $current_repository $package $git_revision $mod_git_revision $repository +# or +# mysql_generate_package_metadata $current_repository $package.$git_revision.$mod_git_revision.$repository +# if sub_pkgrel should be determined automatically +# and +# mysql_generate_package_metadata $sub_pkgrel $current_repository $package $git_revision $mod_git_revision $repository +# or +# mysql_generate_package_metadata $sub_pkgrel $current_repository $package.$git_revision.$mod_git_revision.$repository +# if $sub_pkgrel should be forced + +# generate the meta data of a package (dependencies, built packages, ...) in the database + +mysql_generate_package_metadata() { + + ( # new shell is intentional + case "$1" in + ''|*[!0-9]*) + unset forced_sub_pkgrel + ;; + *) + forced_sub_pkgrel=$( + printf '%s' "$1" | \ + base64 -w0 + ) + shift + ;; + esac + current_repository="$1" + package="$2" + + if [ $# -eq 2 ]; then + # second form + repository="${package##*.}" + package="${package%.*}" + mod_git_revision="${package##*.}" + package="${package%.*}" + git_revision="${package##*.}" + package="${package%.*}" + else + git_revision="$3" + mod_git_revision="$4" + repository="$5" + fi + + temp_dir=$(mktemp -d 'tmp.mysql-functions.mysql_generate_package_metadata.XXXXXXXXXX' --tmpdir) + trap 'rm -rf --one-file-system "${temp_dir}"' EXIT + + printf '.' >&2 + if ! make_source_info "${package}" "${repository}" "${git_revision}" "${mod_git_revision}" "${temp_dir}/SRCINFO"; then + printf '"make_source_info %s %s %s %s %s" failed.\n' "${package}" "${repository}" "${git_revision}" "${mod_git_revision}" "${temp_dir}/SRCINFO" + exit 2 + fi + # remove empty lines and unsupported architectures + sed -i ' + /^[^=]*=\s*$/d + /^\s*arch = /{ + / \(i686\|any\)$/!d + } + ' "${temp_dir}/SRCINFO" + + if [ ! -s "${temp_dir}/SRCINFO" ]; then + >&2 printf '"make_source_info" had empty output - eh, what?\n' + exit 2 + fi + printf '\n\n' >> "${temp_dir}/SRCINFO" + + printf '.' >&2 + pkgbase=$( + grep '^pkgbase = ' "${temp_dir}/SRCINFO" | \ + cut -d' ' -f3 + ) + if [ -z "${pkgbase}" ]; then + >&2 printf '"make_source_info" did not return a "pkgbase" - eh, what?\n' + exit 2 + fi + + # add the package source + mysql_add_package_source "${pkgbase}" "${git_revision}" "${mod_git_revision}" "${repository}" + printf '.' >&2 + + # now we encode everything in base64 + current_repository=$( + printf '%s' "${current_repository}" | \ + base64 -w0 + ) + pkgbase=$( + printf '%s' "${pkgbase}" | \ + base64 -w0 + ) + git_revision=$( + printf '%s' "${git_revision}" | \ + base64 -w0 + ) + mod_git_revision=$( + printf '%s' "${mod_git_revision}" | \ + base64 -w0 + ) + repository=$( + printf '%s' "${repository}" | \ + base64 -w0 + ) + + # add the build assignment(s) + { + archs=$( + sed -n ' + s/^\tarch = // + T + p + ' "${temp_dir}/SRCINFO" | \ + grep -vxF 'any' | \ + sort -u + ) + if [ -z "${archs}" ]; then + echo 'any' + else + printf '%s\n' "${archs}" + fi + } | \ + while read -r arch; do + printf 'INSERT IGNORE INTO `build_assignments` (`package_source`,`architecture`,`is_blocked`,`is_broken`,`priority`)' + printf ' SELECT `package_sources`.`id`,`architectures`.`id`,NULL,0,0' + printf ' FROM `architectures` JOIN `package_sources`' + printf ' WHERE `architectures`.`name` = from_base64("%s")' \ + "$( + printf '%s' "${arch}" | \ + base64 -w0 + )" + printf ' AND `package_sources`.`%s` = from_base64("%s")' \ + 'pkgbase' "${pkgbase}" \ + 'git_revision' "${git_revision}" \ + 'mod_git_revision' "${mod_git_revision}" + printf ';\n' + done > \ + "${temp_dir}/add-build-assignments-command" + + # TODO: correctly link between binary_packages and build_assignments using any_arch + + # shellcheck disable=SC2034 + # select any specific arch (which will be building the 'any' part of a split package) + any_arch=$( + { + sed -n ' + s/^\tarch = // + T + p + ' "${temp_dir}/SRCINFO" | \ + sort -r | \ + grep -vxFm 1 'any' || \ + echo 'any' + } | \ + base64_encode_each + ) + + grep '^pkgname = ' "${temp_dir}/SRCINFO" | \ + cut -d' ' -f3 | \ + while read -r pkgname; do + pkgname64=$( + printf '%s' "${pkgname}" | \ + base64 -w0 + ) + sed -n ' + /^pkgbase = \|^pkgname = '"$(str_to_regex "${pkgname}")"'$/{ + :a + N + /\n$/{ + p + T + } + ba + } + ' "${temp_dir}/SRCINFO" | \ + sed ' + /^\S/d + s/^\s*// + ' > \ + "${temp_dir}/BINARYINFO.${pkgname64}" + + grep '^arch = ' "${temp_dir}/BINARYINFO.${pkgname64}" | \ + cut -d' ' -f3 | \ + while read -r arch; do + arch64=$( + printf '%s' "${arch}" | \ + base64 -w0 + ) + sed ' + s/^\(\S\+\)_'"${arch}"' = /\1 = / + ' "${temp_dir}/BINARYINFO.${pkgname64}" > \ + "${temp_dir}/ARCHINFO ${pkgname64} ${arch64}" + done + done + find "${temp_dir}" -mindepth 1 -maxdepth 1 -name 'ARCHINFO * *' -printf '%f\n' | \ + while read -r _ pkgname arch; do + pkgver=$( + grep '^pkgver = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ + cut -d' ' -f3 | \ + base64_encode_each + ) + pkgrel=$( + grep '^pkgrel = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ + cut -d' ' -f3 | \ + sed 's/\.[0-9]\+$//' | \ + base64_encode_each + ) + epoch=$( + { + grep '^epoch = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" || \ + echo 'epoch = 0' + } | \ + cut -d' ' -f3 | \ + base64_encode_each + ) + provides=$( + grep '^\(groups\|provides\) = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ + cut -d' ' -f3 | \ + sed 's/[<>=].*$//' | \ + base64_encode_each + ) + makedepends=$( + grep '^makedepends = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ + cut -d' ' -f3 | \ + sed 's/[<>=].*$//' | \ + base64_encode_each + ) + checkdepends=$( + grep '^checkdepends = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ + cut -d' ' -f3 | \ + sed 's/[<>=].*$//' | \ + base64_encode_each + ) + rundepends=$( + grep '^depends = ' "${temp_dir}/ARCHINFO ${pkgname} ${arch}" | \ + cut -d' ' -f3 | \ + sed 's/[<>=].*$//' | \ + base64_encode_each + ) + if [ -n "${forced_sub_pkgrel}" ]; then + sub_pkgrel='from_base64("'"${forced_sub_pkgrel}"'")' + else + sub_pkgrel=$( + printf '(SELECT COALESCE(' + # do not add binary packages which are currently on the + # build-list or in $current_repository (beware of split + # packages!) + printf '(SELECT `sub_pkgrel` FROM `binary_packages`' + mysql_join_binary_packages_architectures + mysql_join_binary_packages_repositories + printf ' WHERE' + printf ' `binary_packages`.`%s`=from_base64("%s") AND' \ + 'epoch' "${epoch}" \ + 'pkgver' "${pkgver}" \ + 'pkgrel' "${pkgrel}" \ + 'pkgname' "${pkgname}" + printf ' `architectures`.`name`=from_base64("%s")' \ + "${arch}" + printf ' AND `repositories`.`name` IN ("build-list",from_base64("%s"))),' \ + "${current_repository}" + # max(sub_pkgrel)+1 + printf '(SELECT 1+MAX(`binary_packages`.`sub_pkgrel`) FROM `binary_packages`' + mysql_join_binary_packages_architectures + printf ' WHERE' + printf ' `binary_packages`.`%s`=from_base64("%s") AND' \ + 'epoch' "${epoch}" \ + 'pkgver' "${pkgver}" \ + 'pkgrel' "${pkgrel}" \ + 'pkgname' "${pkgname}" + if printf '%s' "${arch}" | base64 -d | grep -qxF 'any'; then + # 'any' gets higher sub_pkgrel than any architecture + printf ' 1' + else + # not-'any' gets higher sub_pkgrel than same or 'any' architecture + printf ' (`architectures`.`name`=from_base64("%s") OR `architectures`.`name`="any")' \ + "${arch}" + fi + printf ')' + printf ',0))' + ) + fi + { + printf 'INSERT IGNORE INTO `binary_packages` (' + printf '`%s`,' \ + 'build_assignment' \ + 'repository' \ + 'architecture' \ + 'epoch' \ + 'pkgver' \ + 'pkgrel' \ + 'pkgname' \ + 'sub_pkgrel' \ + 'has_issues' \ + 'is_tested' \ + 'is_to_be_deleted' + printf ') SELECT ' + printf '`%s`.`id`,' \ + 'build_assignments' \ + 'repositories' \ + 'architectures' + printf 'from_base64("%s"),' \ + "${epoch}" \ + "${pkgver}" \ + "${pkgrel}" \ + "${pkgname}" + printf '%s,0,0,0 FROM' \ + "${sub_pkgrel}" + printf ' `%s` JOIN' \ + 'repositories' \ + 'architectures' \ + 'build_assignments' + mysql_join_build_assignments_package_sources + mysql_join_package_sources_upstream_repositories + printf ' WHERE' + printf ' `%s`.`%s` = from_base64("%s") AND' \ + 'repositories' 'name' "${current_repository}" \ + 'architectures' 'name' "${arch}" \ + 'package_sources' 'pkgbase' "${pkgbase}" \ + 'package_sources' 'git_revision' "${git_revision}" \ + 'package_sources' 'mod_git_revision' "${mod_git_revision}" \ + 'upstream_repositories' 'name' "${repository}" + printf ';\n' + } | \ + sed ' + s|,)|)|g + s| JOIN JOIN | JOIN | + s| AND;$|;| + ' >> \ + "${temp_dir}/add-binary-packages-command" + { + printf 'CREATE TEMPORARY TABLE `%s` (`name` VARCHAR(64));\n' \ + 'provides' \ + 'makedepends' \ + 'checkdepends' \ + 'rundepends' + + printf 'INSERT INTO `provides` VALUES\n' + echo "${provides}" | \ + sort -u | \ + grep -vxF '' | \ + sed ' + s|^| (from_base64("| + s|$|")),| + ' + printf ' (CONCAT(from_base64("%s"),"-",from_base64("%s"),":",from_base64("%s"),"-",from_base64("%s"))),\n' \ + "${pkgname}" \ + "${epoch}" \ + "${pkgver}" \ + "${pkgrel}" + printf ' (CONCAT(from_base64("%s"),"-",from_base64("%s"),":",from_base64("%s"))),\n' \ + "${pkgname}" \ + "${epoch}" \ + "${pkgver}" + printf ' (from_base64("%s"));\n' \ + "${pkgname}" + + printf 'INSERT INTO `rundepends` VALUES\n' + echo "${rundepends}" | \ + sort -u | \ + grep -vxF '' | \ + sed ' + s|^| (from_base64("| + s|$|")),| + ' + if printf '%s' "${pkgname}" | \ + base64 -d | \ + grep -q -- '-doc$'; then + printf ' (CONCAT(from_base64("%s"),"-",from_base64("%s"),":",from_base64("%s"))),\n' \ + "$(printf '%s' "${pkgname}" | base64 -d | sed 's/-doc$//' | base64 -w0)" \ + "${epoch}" \ + "${pkgver}" + fi + printf ' ("base");\n' + + echo "${checkdepends}" | \ + sort -u | \ + grep -vxF '' | \ + sed ' + s|^| (from_base64("| + s|$|")),| + 1 s/^/INSERT INTO `checkdepends` VALUES \n/ + $ s/,$/;/ + ' + + printf 'INSERT INTO `makedepends` VALUES\n' + echo "${makedepends}" | \ + sort -u | \ + grep -vxF '' | \ + sed ' + s|^| (from_base64("| + s|$|")),| + ' + printf ' ("base-devel");\n' + + printf 'INSERT IGNORE INTO `install_targets` (`name`)' + printf ' SELECT (`name`) FROM `%s` UNION' \ + 'provides' \ + 'makedepends' \ + 'checkdepends' \ + 'rundepends' | \ + sed 's| UNION$|;\n|' + + for link in 'provides' 'makedepends' 'checkdepends' 'rundepends'; do + case "${link}" in + 'provides') + printf 'INSERT IGNORE INTO `install_target_providers` (`package`,`install_target`) SELECT' + printf ' `binary_packages`.`id`,`install_targets`.`id` FROM' + ;; + 'makedepends'|'checkdepends'|'rundepends') + printf 'INSERT IGNORE INTO `dependencies` (`dependent`,`depending_on`,`dependency_type`) SELECT' + printf ' `binary_packages`.`id`,`install_targets`.`id`,`dependency_types`.`id` FROM' + printf ' `dependency_types` JOIN' + ;; + esac + printf ' `binary_packages`' + mysql_join_binary_packages_architectures + printf ' JOIN `install_targets`' + printf ' JOIN `%s`' "${link}" + printf ' ON `%s`.`name` = `install_targets`.`name`' "${link}" + printf ' WHERE' + if [ "${link}" = 'makedepends' ] || \ + [ "${link}" = 'checkdepends' ] || \ + [ "${link}" = 'rundepends' ]; then + printf ' `dependency_types`.`name` = "%s" AND' \ + "${link%depends}" + fi + printf ' `binary_packages`.`%s` = from_base64("%s") AND' \ + 'epoch' "${epoch}" \ + 'pkgver' "${pkgver}" \ + 'pkgrel' "${pkgrel}" \ + 'pkgname' "${pkgname}" + # we do not want to match the sub_pkgrel: + # a) it is tedious to do so (because it may be calculated + # dynamically) + # b) it is not necessary to do so: if only the sub_pkgrel + # changed, the dependencies and provided install_targets + # should not have changed + printf ' `architectures`.`name` = from_base64("%s");\n' \ + "${arch}" + # the repository is of no relevance: it hardly matters for + # the dependencies + done + + printf 'DROP TABLE `%s`;\n' \ + 'provides' \ + 'makedepends' \ + 'checkdepends' \ + 'rundepends' + } >> \ + "${temp_dir}/add-install-targets-command" + done + printf '.' >&2 + + { + if [ -s "${temp_dir}/add-build-assignments-command" ]; then + cat "${temp_dir}/add-build-assignments-command" + fi + if [ -s "${temp_dir}/add-binary-packages-command" ]; then + cat "${temp_dir}/add-binary-packages-command" + fi + if [ -s "${temp_dir}/add-install-targets-command" ]; then + cat "${temp_dir}/add-install-targets-command" + fi + } | \ + mysql_run_query + printf '.' >&2 + + ) +} + +# mysql_sanity_check +# do a sanity check on the mysql database + +mysql_sanity_check() { + { + printf 'SELECT CONCAT("\\"any\\" build-assignment building \\"",`bp_arch`.`name`,"\\" binary package: ",`binary_packages`.`pkgname`)' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_build_assignments + mysql_join_binary_packages_architectures '' 'bp_arch' + mysql_join_build_assignments_architectures '' 'ba_arch' + printf ' WHERE `bp_arch`.`name`!="any"' + printf ' AND `ba_arch`.`name`="any";\n' + printf 'SELECT DISTINCT CONCAT("package multiple times on build list: ",`a`.`pkgname`)' + printf ' FROM `binary_packages` AS `a`' + printf ' JOIN `binary_packages` AS `b`' + printf ' ON `a`.`pkgname`=`b`.`pkgname`' + printf ' AND `a`.`repository`=`b`.`repository`' + printf ' AND `a`.`id`!=`b`.`id`' + mysql_join_binary_packages_repositories 'a' + printf ' WHERE `repositories`.`name`="build-list";\n' + printf 'SELECT DISTINCT CONCAT("\\"split-package with differing sub_pkgrels on the build-list: ",`a`.`pkgname`)' + printf ' FROM `binary_packages` AS `a`' + printf ' JOIN `binary_packages` AS `b` ON `a`.`build_assignment`=`b`.`build_assignment`' + mysql_join_binary_packages_repositories 'a' 'arep' + mysql_join_binary_packages_repositories 'b' 'brep' + printf ' WHERE `a`.`sub_pkgrel`!=`b`.`sub_pkgrel`' + printf ' AND `%srep`.`name`="build-list"' \ + 'a' 'b' + printf ';\n' + } | \ + mysql_run_query | \ + sed ' + s,^,<font color="#FF0000">, + s,$,</font>, + ' + ( # new shell is intentional + temp_dir=$(mktemp -d 'tmp.mysql-functions.mysql_sanity_check.XXXXXXXXXX' --tmpdir) + trap 'rm -rf --one-file-system "${temp_dir}"' EXIT + + for dir in $(ls_master_mirror 'i686'); do + ls_master_mirror "i686/${dir}" | \ + sed -n ' + s/\.pkg\.tar\.xz$// + T + s/-\([0-9]\+\)-\([^-]\+\)$/-\1.0-\2/ + s/-\([^-:]\+-[^-]\+-[^-]\+\)$/-0:\1/ + s|^|'"${dir}"'/| + p + ' + done | \ + sort > \ + "${temp_dir}/master-mirror-listing" + + { + printf 'SELECT `repositories`.`name`,`pkgname`,`epoch`,`pkgver`,`pkgrel`,`sub_pkgrel`,`architectures`.`name`' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_architectures + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`is_on_master_mirror`' + } | \ + mysql_run_query | \ + sed ' + s,\t,/, + s,\t,-, + s,\t,:, + s,\t,-, + s,\t,., + s,\t,-, + ' | \ + sort > \ + "${temp_dir}/mysql-packages" + + diff -u \ + "${temp_dir}/master-mirror-listing" \ + "${temp_dir}/mysql-packages" + ) +} + +mysql_find_build_assignment_loops() { + new_loops=$( + { + printf 'SELECT DISTINCT `packages_dependency`.`build_assignment`,`packages_dependent`.`build_assignment`' + printf ' FROM `dependencies`' + mysql_join_dependencies_install_target_providers + mysql_join_install_target_providers_binary_packages '' 'packages_dependency' + mysql_join_dependencies_binary_packages '' 'packages_dependent' + mysql_join_binary_packages_repositories 'packages_dependency' 'repositories_dependency' + mysql_join_binary_packages_repositories 'packages_dependent' 'repositories_dependent' + printf ' WHERE `repositories_dependent`.`name`="build-list" AND `repositories_dependency`.`name`="build-list"' + } | \ + mysql_run_query | \ + tr '\t' ' ' | \ + tsort 2>&1 >/dev/null | \ + sed 's/^tsort:\s*//' | \ + { + loop=0 + while read -r id; do + if [ "x${id}" = 'x-: input contains a loop:' ]; then + loop=$((loop+1)) + continue + fi + if ! printf '%s' "${id}" | tr '\n' ' ' | grep -q '^[0-9]\+$'; then + >&2 printf 'ERROR: non-numeric id "%s"\n' "${id}" + continue + fi + printf '(%s,%s),' "${loop}" "${id}" + done | \ + sed 's/,$//' + } + ) + { + printf 'DELETE FROM `build_dependency_loops`;\n' + if [ -n "${new_loops}" ]; then + printf 'INSERT INTO `build_dependency_loops` (`loop`,`build_assignment`) VALUES %s;\n' \ + "${new_loops}" + fi + } | \ + mysql_run_query +} + +# mysql_cleanup [dry] +# clean up left overs from mysql database +mysql_cleanup() { + local operator + if [ "$#" = '0' ]; then + operator='DELETE' + elif [ "$#" = '1' ] && [ "x$1" = 'xdry' ]; then + operator='SELECT COUNT(*)' + else + >&2 echo 'Unknown parameter' + >&2 echo 'Call "mysql_clean_up" or "mysql_clean_up dry".' + exit 2 + fi + { + # remove to-be-decided binary_packages + printf '%s ' \ + "${operator}" + if [ "${operator}" = 'DELETE' ]; then + printf '`binary_packages` ' + fi + printf 'FROM `binary_packages`' + mysql_join_binary_packages_repositories + printf ' WHERE `repositories`.`name`="to-be-decided";\n' + # remove dependencies w/o binary_package or install_target + printf '%s FROM `dependencies` ' \ + "${operator}" + printf 'WHERE NOT EXISTS ' + printf '(' + printf 'SELECT * FROM `binary_packages` ' + printf 'WHERE `dependencies`.`dependent`=`binary_packages`.`id`' + printf ') OR NOT EXISTS ' + printf '(' + printf 'SELECT * FROM `install_targets` ' + printf 'WHERE `dependencies`.`depending_on`=`install_targets`.`id`' + printf ');\n' + # remove install_target_providers w/o binary_package or install_target + printf '%s FROM `install_target_providers` ' \ + "${operator}" + printf 'WHERE NOT EXISTS ' + printf '(' + printf 'SELECT * FROM `binary_packages` ' + printf 'WHERE `install_target_providers`.`package`=`binary_packages`.`id`' + printf ') OR NOT EXISTS ' + printf '(' + printf 'SELECT * FROM `install_targets` ' + printf 'WHERE `install_target_providers`.`install_target`=`install_targets`.`id`' + printf ');\n' + # remove build_assignments w/o binary_package + printf '%s FROM `build_assignments` ' \ + "${operator}" + printf 'WHERE NOT EXISTS ' + printf '(' + printf 'SELECT * FROM `binary_packages` ' + printf 'WHERE `binary_packages`.`build_assignment`=`build_assignments`.`id`' + printf ');\n' + # remove failed_builds with unbroken build_assignments + printf '%s ' \ + "${operator}" + if [ "${operator}" = 'DELETE' ]; then + printf '`failed_builds` ' + fi + printf 'FROM `failed_builds` ' + mysql_join_failed_builds_build_assignments + printf 'WHERE NOT `build_assignments`.`is_broken`' + printf ';\n' + # remove failed_builds w/o build_assignment + printf '%s FROM `failed_builds` ' \ + "${operator}" + printf 'WHERE NOT EXISTS ' + printf '(' + printf 'SELECT * FROM `build_assignments` ' + printf 'WHERE `build_assignments`.`id`=`failed_builds`.`build_assignment`' + printf ');\n' + # remove package_sources w/o build_assignment + printf '%s FROM `package_sources` ' \ + "${operator}" + printf 'WHERE NOT EXISTS ' + printf '(' + printf 'SELECT * FROM `build_assignments` ' + printf 'WHERE `build_assignments`.`package_source`=`package_sources`.`id`' + printf ');\n' + printf 'UPDATE `build_slaves`' + mysql_join_build_slaves_binary_packages + mysql_join_binary_packages_repositories + printf ' SET `currently_building`=NULL' + printf ' WHERE `repositories`.`name`!="build-list";\n' + } | \ + mysql_run_query +} + +# mysql_query_has_pending_dependencies `build_assignment`.`id` +# print a mysql query giving wether dependencies are pending +mysql_query_has_pending_dependencies() { + printf 'EXISTS (' + printf 'SELECT * FROM `binary_packages` as `to_dos`' + mysql_join_binary_packages_repositories 'to_dos' 'to_do_repos' + mysql_join_binary_packages_dependencies 'to_dos' + mysql_join_dependencies_install_target_providers + mysql_join_install_target_providers_binary_packages '' 'bin_deps' + mysql_join_binary_packages_repositories 'bin_deps' 'dep_repos' + printf ' WHERE' + printf ' `%s`.`name`="build-list" AND' \ + 'dep_repos' 'to_do_repos' + printf ' `bin_deps`.`build_assignment`!=`to_dos`.`build_assignment` AND' + printf ' `to_dos`.`build_assignment`=%s' \ + "$1" + printf ')' +} + +# mysql_query_is_part_of_loop `build_assignment`.`id` +# print a mysql query giving wether the package is part of a loop +mysql_query_is_part_of_loop() { + printf 'EXISTS (' + printf 'SELECT * FROM `build_dependency_loops`' + printf ' WHERE `build_dependency_loops`.`build_assignment`=%s' \ + "$1" + printf ')' +} + +# mysql_query_select_pkgbase_and_revision +# print the part of a mysql query giving: +# pkgbase git_revision mod_git_revision upstream_package_repository +mysql_query_select_pkgbase_and_revision() { + printf '`package_sources`.`%s`,' \ + 'pkgbase' \ + 'git_revision' \ + 'mod_git_revision' + printf '`upstream_repositories`.`name`' + printf ' FROM `build_assignments`' + mysql_join_build_assignments_package_sources + mysql_join_package_sources_upstream_repositories +} + +# mysql_repair_binary_packages_without_build_assignment +# try to generate valid build assignments to binary packages without +# a valid one yet +mysql_repair_binary_packages_without_build_assignment() { + { + printf 'SELECT ' + printf '`binary_packages`.`id`' + printf ',replace(to_base64(%s),"\\n","")' \ + '`binary_packages`.`pkgname`' \ + '`architectures`.`name`' + printf ' FROM `binary_packages`' + mysql_join_binary_packages_architectures + printf ' WHERE `binary_packages`.`build_assignment`<0' + } | \ + mysql_run_query | \ + while read -r id pkgname arch; do + pkgname=$( + printf '%s' "${pkgname}" | \ + base64 -d + ) + pkgbase=$( + curl -Ss "$( + printf 'https://www.archlinux.org/packages/search/json/?name=%s' \ + "${pkgname}" + )" | \ + sed ' + s/^.*"results":\s*\[// + s/}\s*,\s*{/\n/g + ' | \ + grep '"pkgname":\s*"'"$(str_to_regex "${pkgname}")"'"' | \ + tr ',' '\n' | \ + grep '"pkgbase":' | \ + cut -d'"' -f4 | \ + sort -u | \ + head -n1 + ) + if [ -z "${pkgbase}" ] && \ + { + printf 'SELECT count(*) FROM `package_sources`' + printf ' WHERE `package_sources`.`pkgbase`=from_base64("%s")' \ + "$(printf '%s' "${pkgname}" | base64 -w0)" + } | \ + mysql_run_query | \ + grep -qvxF '0'; then + pkgbase="${pkgname}" + fi + if [ -z "${pkgbase}" ]; then + >&2 printf 'Could not find "%s" upstream.\n' "${pkgname}" + continue + fi + pkgbase=$( + printf '%s' "${pkgbase}" | \ + base64 -w0 + ) + printf 'INSERT IGNORE INTO `build_assignments` (`package_source`,`architecture`,`is_blocked`,`is_broken`,`priority`)' + printf ' SELECT `package_sources`.`id`,`architectures`.`id`,0,0,0' + printf ' FROM `package_sources`' + printf ' JOIN `architectures`' + printf ' WHERE `package_sources`.`pkgbase`=from_base64("%s")' "${pkgbase}" + printf ' AND `architectures`.`name`=from_base64("%s")' "${arch}" + printf ' LIMIT 1;\n' + printf 'UPDATE `binary_packages`' + printf ' JOIN `build_assignments`' + mysql_join_binary_packages_build_assignments + printf ' SET `binary_packages`.`build_assignment`=`build_assignments`.`id`' + printf ' WHERE `binary_packages`.`id`=%s' "${id}" + printf ' AND `package_sources`.`pkgbase`=from_base64("%s");\n' "${pkgbase}" + done | \ + mysql_run_query +} + +# mysql_remove_duplicate_binary_packages +# remove duplicate binary_packages, matching pkgname, epoch, pkgver, pkgrel, +# having difference of 1 in sub_pkgrel + +mysql_remove_duplicate_build_order() { + { + printf 'CREATE TEMPORARY TABLE `ren`' + printf ' (`old` BIGINT, `new` BIGINT, `repo` BIGINT, `sub_pkgrel` BIGINT);\n' + printf 'INSERT INTO `ren` (`old`,`new`,`repo`,`sub_pkgrel`)' + printf ' SELECT `old`.`id`,`new`.`id`,`old`.`repository`,`old`.`sub_pkgrel`' + printf ' FROM `binary_packages` as `old`' + printf ' JOIN `binary_packages` as `new` ON' + printf ' `old`.`%s`=`new`.`%s` AND' \ + 'pkgname' 'pkgname' \ + 'epoch' 'epoch' \ + 'pkgver' 'pkgver' \ + 'pkgrel' 'pkgrel' + printf ' `old`.`sub_pkgrel`+1=`new`.`sub_pkgrel`' + mysql_join_binary_packages_repositories 'old' 'orep' + mysql_join_binary_packages_repositories 'new' 'nrep' + printf ' WHERE `orep`.`name`!="to-be-decided"' + printf ' AND `nrep`.`name`="to-be-decided";\n' + printf 'UPDATE IGNORE `dependencies`' + printf ' JOIN `ren` ON `ren`.`old`=`dependencies`.`dependent`' + printf ' SET `dependencies`.`dependent`=`ren`.`new`;\n' + printf 'UPDATE IGNORE `install_target_providers`' + printf ' JOIN `ren` ON `ren`.`old`=`install_target_providers`.`package`' + printf ' SET `install_target_providers`.`package`=`ren`.`new`;\n' + printf 'DELETE FROM `binary_packages`' + printf ' WHERE EXISTS (' + printf 'SELECT * FROM `ren`' + printf ' WHERE `ren`.`old`=`binary_packages`.`id`' + printf ');\n' + printf 'UPDATE IGNORE `binary_packages`' + printf ' JOIN `ren` ON `ren`.`new`=`binary_packages`.`id`' + printf ' SET `binary_packages`.`repository`=`ren`.`repo`,' + printf ' `binary_packages`.`sub_pkgrel`=`ren`.`sub_pkgrel`;\n' + } | \ + mysql_run_query +} + +# mysql_package_name_query +# print a mysql query of the full name of a package file + +mysql_package_name_query() { + printf 'CONCAT(' + printf '`binary_packages`.`pkgname`,"-",' + printf 'IF(`binary_packages`.`epoch`=0,"",CONCAT(`binary_packages`.`epoch`,":")),' + printf '`binary_packages`.`pkgver`,"-",' + printf '`binary_packages`.`pkgrel`,".",' + printf '`binary_packages`.`sub_pkgrel`,"-",' + printf '`architectures`.`name`,".pkg.tar.xz"' + printf ')' +} + +# mysql_join_*_* +# print 'JOIN' part of mysql query to connect the respective tables +# these functions take 2 optional arguments, acting as aliases for +# the tables + +# mysql_join__generic $table_a $column_a $table_b $column_b +# create mysql_join_${table_a}_${table_b}() function + +mysql_join__generic() { + eval "$( + printf 'mysql_join_%s_%s() {\n' "$1" "$3" + printf ' printf '"'"' JOIN `%s`'"'"'\n' "$3" + printf ' if [ -n "$2" ]; then\n' + printf ' printf '"'"' AS `%%s`'"'"' "$2"\n' + printf ' fi\n' + printf ' if [ -n "$1" ]; then\n' + printf ' printf '"'"' ON `%%s`.`%s`='"'"' "$1"\n' "$2" + printf ' else\n' + printf ' printf '"'"' ON `%s`.`%s`='"'"'\n' "$1" "$2" + printf ' fi\n' + printf ' if [ -n "$2" ]; then\n' + printf ' printf '"'"'`%%s`.`%s`'"'"' "$2"\n' "$4" + printf ' else\n' + printf ' printf '"'"'`%s`.`%s`'"'"'\n' "$3" "$4" + printf ' fi\n' + printf '}\n' + )" +} + +for link in \ + 'binary_packages:architecture:architectures' \ + 'binary_packages:repository:repositories' \ + 'binary_packages:build_assignment:build_assignments' \ + \ + 'build_assignments:architecture:architectures' \ + 'build_assignments:package_source:package_sources' \ + \ + 'build_dependency_loops:build_assignment:build_assignments' \ + 'build_dependency_loops:build_assignment build_assignment:binary_packages' \ + \ + 'build_slaves:currently_building:build_assignments' \ + 'build_slaves:currently_building build_assignment:binary_packages' \ + \ + 'dependencies:depending_on:install_targets' \ + 'dependencies:dependent:binary_packages' \ + 'dependencies:dependency_type:dependency_types' \ + \ + 'failed_builds:reason:fail_reason' \ + 'failed_builds:build_assignment:build_assignments' \ + 'failed_builds:build_slave:build_slaves' \ + \ + 'install_target_providers:package:binary_packages' \ + 'install_target_providers:install_target:install_targets' \ + 'install_target_providers:install_target depending_on:dependencies' \ + \ + 'package_sources:upstream_package_repository:upstream_repositories' \ + \ + 'repositories:stability:repository_stabilities' \ + \ + 'repository_moves:upstream_package_repository:upstream_repositories' \ + \ + 'upstream_repositories:git_repository:git_repositories'; do +# A join for these cannot be done, because it's not clear on what to join: +# 'repository_stability_relations:more_stable:repository_stabilities' +# 'repository_stability_relations:less_stable:repository_stabilities' + + table_b="${link##*:}" + table_a="${link%:*}" + column_b="${table_a##*:}" + table_a="${table_a%:*}" + column_a="${column_b% *}" + if [ "${column_a}" = "${column_b}" ]; then + column_b='id' + else + column_b="${column_b##* }" + fi + + mysql_join__generic "${table_a}" "${column_a}" "${table_b}" "${column_b}" + mysql_join__generic "${table_b}" "${column_b}" "${table_a}" "${column_a}" +done diff --git a/web-scripts/blacklist.php b/web-scripts/blacklist.php new file mode 100644 index 0000000..c2810c6 --- /dev/null +++ b/web-scripts/blacklist.php @@ -0,0 +1,48 @@ +<html> +<head> +<title>Blacklisted packages</title> +<link rel="stylesheet" type="text/css" href="/static/style.css"> +</head> +<body> +<table> +<tr><th>architecture</th><th>package</th><th>reason</th></tr> +<?php + +$mysql = new mysqli("localhost", "webserver", "empty", "buildmaster"); +if ($mysql->connect_error) { + die("Connection failed: " . $mysql->connect_error); +} +if ( ! $result = $mysql -> query( + "SELECT DISTINCT `architectures`.`name` AS `architecture`,`package_sources`.`pkgbase`,`build_assignments`.`is_black_listed` " . + "FROM `build_assignments` " . + "JOIN `architectures` ON `build_assignments`.`architecture`=`architectures`.`id` " . + "JOIN `package_sources` ON `build_assignments`.`package_source`=`package_sources`.`id` " . + "WHERE `build_assignments`.`is_black_listed` IS NOT NULL " . + "ORDER BY `package_sources`.`pkgbase`")) { + die($mysql->error); +} +if ($result -> num_rows > 0) { + while($row = $result->fetch_assoc()) { + print "<tr><td>"; + print $row["architecture"]; + print "</td><td>"; + print $row["pkgbase"]; + print "</td><td>"; + print preg_replace( + array ( + "/FS32#(\\d+)/", + "/FS#(\\d+)/" + ), + array ( + "<a href=\"https://bugs.archlinux32.org/index.php?do=details&task_id=$1\">$0</a>", + "<a href=\"https://bugs.archlinux.org/task/$1\">$0</a>" + ), + $row["is_black_listed"] + ); + print "</td></tr>\n"; + } +} +?> +</table> +</body> +</html> diff --git a/web-scripts/broken-packages.php b/web-scripts/broken-packages.php deleted file mode 100644 index 268e296..0000000 --- a/web-scripts/broken-packages.php +++ /dev/null @@ -1,157 +0,0 @@ -<html> -<head> -<title>List of broken package builds</title> -<link rel="stylesheet" type="text/css" href="/static/style.css"> -</head> -<body> -<a href="build-logs/">build logs</a><br> -<?php - -$mysql = new mysqli("localhost", "webserver", "empty", "buildmaster"); -if ($mysql->connect_error) { - die("Connection failed: " . $mysql->connect_error); -} - -$result = $mysql -> query( - "SELECT " . - "`build_assignments`.`id`," . - "`build_assignments`.`is_blocked`," . - "`package_sources`.`pkgbase`," . - "`package_sources`.`git_revision`," . - "`package_sources`.`mod_git_revision`," . - "`upstream_repositories`.`name`," . - "EXISTS (SELECT * " . - "FROM `binary_packages` `broken_bin` " . - "JOIN `dependencies` ON `dependencies`.`dependent` = `broken_bin`.`id` " . - "JOIN `install_target_providers` ON `install_target_providers`.`install_target` = `dependencies`.`depending_on` " . - "JOIN `binary_packages` `to_be_built` ON `to_be_built`.`id` = `install_target_providers`.`package` " . - "JOIN `repositories` ON `to_be_built`.`repository` = `repositories`.`id` " . - "WHERE `broken_bin`.`build_assignment`=`build_assignments`.`id` ". - "AND `repositories`.`name`=\"community-testing\"" . - ") AS `dependencies_pending`," . - "(SELECT count(*) " . - "FROM `build_dependency_loops` " . - "WHERE `build_dependency_loops`.`build_assignment`=`build_assignments`.`id`" . - ") AS `loops` " . - "FROM `build_assignments` " . - "JOIN `package_sources` ON `build_assignments`.`package_source` = `package_sources`.`id` " . - "JOIN `upstream_repositories` ON `package_sources`.`upstream_package_repository` = `upstream_repositories`.`id` " . - "WHERE `build_assignments`.`is_broken` OR `build_assignments`.`is_blocked` IS NOT NULL" -); -if ($result -> num_rows > 0) { - - $count = 0; - - while($row = $result->fetch_assoc()) { - -foreach ($row as $key => $val) - - $fail_result = $mysql -> query( - "SELECT " . - "`fail_reasons`.`name`, " . - "`failed_builds`.`log_file` " . - "FROM `failed_builds` " . - "JOIN `build_assignments` ON `failed_builds`.`build_assignment`=`build_assignments`.`id` ". - "JOIN `fail_reasons` ON `failed_builds`.`reason`=`fail_reasons`.`id` ". - "WHERE `build_assignments`.`id`=".$row["id"]." " . - "ORDER BY `failed_builds`.`date`" - ); - - unset($reasons); - unset($last_log); - $rows[$count]["trials"] = $fail_result -> num_rows; - if ($rows[$count]["trials"] > 0) { - while($fail_row = $fail_result->fetch_assoc()) { - $reasons[$fail_row["name"]] = $fail_row["name"]; - $last_log = $fail_row["log_file"]; - } - } - if (isset($reasons)) { - $to_print=""; - foreach ($reasons as $reason) { - $to_print=$to_print.", ".$reason; - } - $rows[$count]["fail_reasons"]=substr($to_print,2); - } else { - $rows[$count]["fail_reasons"]=" "; - } - - $rows[$count]["loops"] = $row["loops"]; - $rows[$count]["pkgbase"] = $row["pkgbase"]; - if ($row["dependencies_pending"]=="1") - $rows[$count]["pkgbase_print"] = "(" . $rows[$count]["pkgbase"] . ")"; - else - $rows[$count]["pkgbase_print"] = $rows[$count]["pkgbase"]; - $rows[$count]["git_revision"] = $row["git_revision"]; - $rows[$count]["mod_git_revision"] = $row["mod_git_revision"]; - $rows[$count]["name"] = $row["name"]; - if (isset($last_log)) - $rows[$count]["print_trials"]="<a href=\"/build-logs/error/".$last_log."\">". $rows[$count]["trials"] ."</a>"; - else - $rows[$count]["print_trials"]=$rows[$count]["trials"]; - if ($row["is_blocked"]=="") { - $rows[$count]["is_blocked"]=" "; - } - else { - $rows[$count]["is_blocked"] = preg_replace( - array ( - "/FS32#(\\d+)/", - "/FS#(\\d+)/" - ), - array ( - "<a href=\"https://bugs.archlinux32.org/index.php?do=details&task_id=$1\">$0</a>", - "<a href=\"https://bugs.archlinux.org/task/$1\">$0</a>" - ), - $row["is_blocked"] - ); - } - $count++; - } - - usort( - $rows, - function (array $a, array $b) { - if ($a["trials"] < $b["trials"]) - return -1; - if ($a["trials"] > $b["trials"]) - return 1; - return strcmp($a["pkgbase"],$b["pkgbase"]); - } - ); - - print "<table>\n"; - print "<tr>"; - print "<th>package</th>"; - print "<th>git revision</th>"; - print "<th>modification git revision</th>"; - print "<th>package repository</th>"; - print "<th>compilations</th>"; - print "<th>loops</th>"; -// print "<th>dependent</th>"; - print "<th>build error</th>"; - print "<th>blocked</th>"; - print "</tr>\n"; - - foreach($rows as $row) { - - print "<tr>"; - - print "<td><a href=\"/graphs/".$row["pkgbase"].".png\">".$row["pkgbase_print"]."</a></td>"; - print "<td><p style=\"font-size:8px\">".$row["git_revision"]."</p></td>"; - print "<td><p style=\"font-size:8px\">".$row["mod_git_revision"]."</p></td>"; - print "<td>".$row["name"]."</td>"; - print "<td>".$row["print_trials"]."</td>"; - print "<td>".$row["loops"]."</td>"; -// <td>0</td> - print "<td>".$row["fail_reasons"]."</td>"; - print "<td>".$row["is_blocked"]."</td>"; - - print "</tr>\n"; - } - - print "</table>\n"; -} - -?> -</body> -</html> diff --git a/web-scripts/build-list.php b/web-scripts/build-list.php new file mode 100644 index 0000000..5cc7a47 --- /dev/null +++ b/web-scripts/build-list.php @@ -0,0 +1,220 @@ +<html> +<head> +<?php + +if (isset($_GET["show"])) + $to_show=$_GET["show"]; +else + $to_show="all"; + +if ($to_show == "all") + $match = ""; +elseif ($to_show == "broken") + $match = " AND (`build_assignments`.`is_broken` OR `build_assignments`.`is_blocked` IS NOT NULL)"; +elseif ($to_show == "next") + $match = ""; +else + die(); + +print "<title>List of " . $to_show . " package builds</title>\n"; +print "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/style.css\">\n"; +print "</head>\n"; +print "<body>\n"; +print "<a href=\"/\">Start page</a>\n"; +print "<a href=\"/build-logs/\">build logs</a><br>\n"; + +$mysql = new mysqli("localhost", "webserver", "empty", "buildmaster"); +if ($mysql->connect_error) { + die("Connection failed: " . $mysql->connect_error); +} + +$result = $mysql -> query( + "SELECT DISTINCT " . + "`build_assignments`.`id`," . + "`build_assignments`.`is_blocked`," . + "`package_sources`.`pkgbase`," . + "`package_sources`.`git_revision`," . + "`package_sources`.`mod_git_revision`," . + "`package_sources`.`uses_upstream`," . + "`package_sources`.`uses_modification`," . + "`upstream_repositories`.`name` AS `package_repository`," . + "`git_repositories`.`name` AS `git_repository`," . + "`architectures`.`name` AS `arch`," . + "EXISTS (SELECT * " . + "FROM `binary_packages` `broken_bin` " . + "JOIN `dependencies` ON `dependencies`.`dependent` = `broken_bin`.`id` " . + "JOIN `install_target_providers` ON `install_target_providers`.`install_target` = `dependencies`.`depending_on` " . + "JOIN `binary_packages` `to_be_built` ON `to_be_built`.`id` = `install_target_providers`.`package` " . + "JOIN `repositories` ON `to_be_built`.`repository` = `repositories`.`id` " . + "WHERE `broken_bin`.`build_assignment`=`build_assignments`.`id` ". + "AND `repositories`.`name`=\"build-list\" " . + "AND `to_be_built`.`build_assignment`!=`build_assignments`.`id`" . + ") AS `dependencies_pending`," . + "(SELECT count(*) " . + "FROM `build_dependency_loops` " . + "WHERE `build_dependency_loops`.`build_assignment`=`build_assignments`.`id`" . + ") AS `loops`, " . + "`build_slaves`.`name` AS `build_slave` " . + "FROM `build_assignments` " . + "JOIN `architectures` ON `build_assignments`.`architecture` = `architectures`.`id` " . + "JOIN `package_sources` ON `build_assignments`.`package_source` = `package_sources`.`id` " . + "JOIN `upstream_repositories` ON `package_sources`.`upstream_package_repository` = `upstream_repositories`.`id` " . + "JOIN `git_repositories` ON `upstream_repositories`.`git_repository`=`git_repositories`.`id` " . + "JOIN `binary_packages` ON `binary_packages`.`build_assignment` = `build_assignments`.`id` " . + "JOIN `repositories` ON `binary_packages`.`repository` = `repositories`.`id` " . + "LEFT JOIN `build_slaves` ON `build_slaves`.`currently_building`=`build_assignments`.`id` " . + "WHERE `repositories`.`name`=\"build-list\"" . $match +); +if ($result -> num_rows > 0) { + + $count = 0; + + while($row = $result->fetch_assoc()) { + + if (($to_show == "next") && + ($row["loops"]==0) && + ($row["dependencies_pending"]==1)) + continue; + + $fail_result = $mysql -> query( + "SELECT " . + "`fail_reasons`.`name`, " . + "`failed_builds`.`log_file` " . + "FROM `failed_builds` " . + "JOIN `fail_reasons` ON `failed_builds`.`reason`=`fail_reasons`.`id` " . + "WHERE `failed_builds`.`build_assignment`=".$row["id"]." " . + "ORDER BY `failed_builds`.`date`" + ); + + unset($reasons); + $rows[$count]["trials"] = $fail_result -> num_rows; + if ($rows[$count]["trials"] > 0) { + while($fail_row = $fail_result->fetch_assoc()) { + $reasons[$fail_row["name"]] = $fail_row["log_file"]; + } + } + if (isset($reasons)) { + $to_print=""; + foreach ($reasons as $reason => $last_log) { + if (file_exists("/srv/http/build-logs/error/".$last_log)) { + $to_print= $to_print . + ", <a href=\"/build-logs/error/" . + $last_log . + "\">" . + $reason . + "</a>"; + } else { + $to_print= $to_print . ", " . $reason; + } + } + $rows[$count]["fail_reasons"]=substr($to_print,2); + } else { + $rows[$count]["fail_reasons"]=" "; + } + + $rows[$count]["loops"] = $row["loops"]; + $rows[$count]["pkgbase"] = $row["pkgbase"]; + if ($row["dependencies_pending"]=="0") + $rows[$count]["pkgbase_print"] = $rows[$count]["pkgbase"]; + else + $rows[$count]["pkgbase_print"] = "(" . $rows[$count]["pkgbase"] . ")"; + if ($row["uses_upstream"]) { + $rows[$count]["git_revision"] = + "<a href=\"https://git.archlinux.org/svntogit/" . + $row["git_repository"] . ".git/tree/" . + $row["pkgbase"] . "/repos/" . + $row["package_repository"] . "-"; + if ($row["arch"]=="any") + $rows[$count]["git_revision"] = + $rows[$count]["git_revision"] . "any"; + else + $rows[$count]["git_revision"] = + $rows[$count]["git_revision"] . "x86_64"; + $rows[$count]["git_revision"] = + $rows[$count]["git_revision"] . "?id=" . + $row["git_revision"]; + $rows[$count]["git_revision"] = + $rows[$count]["git_revision"] . "\">" . + $row["git_revision"] . "</a>"; + } else + $rows[$count]["git_revision"] = $row["git_revision"]; + if ($row["uses_modification"]) + $rows[$count]["mod_git_revision"] = + "<a href=\"https://github.com/archlinux32/packages/tree/" . + $row["mod_git_revision"] . "/" . + $row["package_repository"] . "/" . + $row["pkgbase"] . "\">" . + $row["mod_git_revision"] . "</a>"; + else + $rows[$count]["mod_git_revision"] = $row["mod_git_revision"]; + $rows[$count]["package_repository"] = $row["package_repository"]; + if ($row["is_blocked"]=="") { + $rows[$count]["is_blocked"]=" "; + } + else { + $rows[$count]["is_blocked"] = preg_replace( + array ( + "/FS32#(\\d+)/", + "/FS#(\\d+)/" + ), + array ( + "<a href=\"https://bugs.archlinux32.org/index.php?do=details&task_id=$1\">$0</a>", + "<a href=\"https://bugs.archlinux.org/task/$1\">$0</a>" + ), + $row["is_blocked"] + ); + } + if (isset($row["build_slave"])) + $rows[$count]["build_slave"] = $row["build_slave"]; + else + $rows[$count]["build_slave"] = " "; + $count++; + } + + usort( + $rows, + function (array $a, array $b) { + if ($a["trials"] < $b["trials"]) + return -1; + if ($a["trials"] > $b["trials"]) + return 1; + return strcmp($a["pkgbase"],$b["pkgbase"]); + } + ); + + print "<table>\n"; + print "<tr>"; + print "<th>package</th>"; + print "<th>git revision</th>"; + print "<th>modification git revision</th>"; + print "<th>package repository</th>"; + print "<th>compilations</th>"; + print "<th>loops</th>"; + print "<th>build error</th>"; + print "<th>blocked</th>"; + print "<th>handed out to</th>"; + print "</tr>\n"; + + foreach($rows as $row) { + + print "<tr>"; + + print "<td><a href=\"/scripts/dependencies.php?b=".$row["pkgbase"]."&r=build-list\">".$row["pkgbase_print"]."</a></td>"; + print "<td><p style=\"font-size:8px\">".$row["git_revision"]."</p></td>"; + print "<td><p style=\"font-size:8px\">".$row["mod_git_revision"]."</p></td>"; + print "<td>".$row["package_repository"]."</td>"; + print "<td>".$row["trials"]."</td>"; + print "<td>".$row["loops"]."</td>"; + print "<td>".$row["fail_reasons"]."</td>"; + print "<td>".$row["is_blocked"]."</td>"; + print "<td>".$row["build_slave"]."</td>"; + + print "</tr>\n"; + } + + print "</table>\n"; +} + +?> +</body> +</html> diff --git a/web-scripts/dependencies.php b/web-scripts/dependencies.php new file mode 100644 index 0000000..3a38ac8 --- /dev/null +++ b/web-scripts/dependencies.php @@ -0,0 +1,179 @@ +<?php + +$mysql = new mysqli("localhost", "webserver", "empty", "buildmaster"); +if ($mysql->connect_error) { + die("Connection failed: " . $mysql->connect_error); +} + +$match = ""; + +if (isset($_GET["a"])) + $match .= " AND `architectures`.`name`=from_base64(\"" . base64_encode($_GET["a"]) . "\")"; +if (isset($_GET["b"])) + $match .= " AND `package_sources`.`pkgbase`=from_base64(\"" . base64_encode($_GET["b"]) . "\")"; +if (isset($_GET["p"])) + $match .= " AND `binary_packages`.`pkgname`=from_base64(\"" . base64_encode($_GET["p"]) . "\")"; +if (isset($_GET["r"])) + $match .= " AND `repositories`.`name`=from_base64(\"" . base64_encode($_GET["r"]) . "\")"; + +$ignore_install_targets = " AND NOT `install_targets`.`name` IN (\"base\",\"base-devel\")"; + +$colors["stable"]="#000000"; +$colors["testing"]="#008000"; +$colors["staging"]="#00ff00"; +$colors["standalone"]="#000000"; +$colors["unbuilt"]="#ff0000"; +$colors["forbidden"]="#808080"; +$colors["virtual"]="#800080"; + +$limit=200; + +if (! $result = $mysql -> query( + "CREATE TEMPORARY TABLE `cons` (" . + "`dep` BIGINT, " . + "`itp` BIGINT, " . + "UNIQUE KEY `content` (`dep`,`itp`)" . + ")")) + die($mysql->error); + +if (! $result = $mysql -> query( + "INSERT IGNORE INTO `cons` (`dep`,`itp`)" . + " SELECT `dependencies`.`id`,`install_target_providers`.`id`". + " FROM `binary_packages`" . + " JOIN `repositories` ON `binary_packages`.`repository`=`repositories`.`id`" . + " JOIN `repository_stabilities` ON `repositories`.`stability`=`repository_stabilities`.`id`" . + " JOIN `architectures` ON `binary_packages`.`architecture`=`architectures`.`id`" . + " JOIN `build_assignments` ON `binary_packages`.`build_assignment`= `build_assignments`.`id`" . + " JOIN `package_sources` ON `build_assignments`.`package_source`= `package_sources`.`id`" . + $match . + " JOIN `dependencies` ON `dependencies`.`dependent`=`binary_packages`.`id`" . + " JOIN `dependency_types` ON `dependencies`.`dependency_type`=`dependency_types`.`id`" . + " JOIN `install_targets` ON `dependencies`.`depending_on`=`install_targets`.`id`" . + $ignore_install_targets . + " JOIN `install_target_providers` ON `install_target_providers`.`install_target`=`dependencies`.`depending_on`" . + " WHERE (`dependency_types`.`relevant_for_binary_packages` OR `repository_stabilities`.`name`=\"unbuilt\")" . + " LIMIT " . $limit + )) + die($mysql->error); + +if (! $result = $mysql -> query( + "INSERT IGNORE INTO `cons` (`dep`,`itp`)" . + " SELECT `dependencies`.`id`,`install_target_providers`.`id`". + " FROM `binary_packages`" . + " JOIN `repositories` ON `binary_packages`.`repository`=`repositories`.`id`" . + " JOIN `architectures` ON `binary_packages`.`architecture`=`architectures`.`id`" . + " JOIN `build_assignments` ON `binary_packages`.`build_assignment`= `build_assignments`.`id`" . + " JOIN `package_sources` ON `build_assignments`.`package_source`= `package_sources`.`id`" . + $match . + " JOIN `install_target_providers` ON `install_target_providers`.`package`=`binary_packages`.`id`" . + " JOIN `dependencies` ON `install_target_providers`.`install_target`=`dependencies`.`depending_on`" . + " JOIN `binary_packages` AS `d_bp` ON `dependencies`.`dependent`=`d_bp`.`id`" . + " JOIN `repositories` AS `d_r` ON `d_bp`.`repository`=`d_r`.`id`" . + " JOIN `repository_stabilities` AS `d_rs` ON `d_r`.`stability`=`d_rs`.`id`" . + " JOIN `dependency_types` ON `dependencies`.`dependency_type`=`dependency_types`.`id`" . + " WHERE (`dependency_types`.`relevant_for_binary_packages` OR `d_rs`.`name`=\"unbuilt\")" . + " LIMIT " . $limit + )) + die($mysql->error); + +unset($knots); +unset($edges); + +if (! $result = $mysql -> query( + "SELECT DISTINCT `install_target_providers`.`install_target`,`install_target_providers`.`package`" . + " FROM `cons`" . + " JOIN `install_target_providers` ON `cons`.`itp`=`install_target_providers`.`id`" + )) + die($mysql->error); + +if ($result -> num_rows > 0) + while ($row = $result->fetch_assoc()) + $edges .= "\"p" . $row["package"] . "\" -> \"i" . $row["install_target"] . "\" [color = \"#000080\"];\n"; + +if (! $result = $mysql -> query( + "SELECT DISTINCT `dependencies`.`dependent`,`dependencies`.`depending_on`,`dependency_types`.`name`" . + " FROM `cons`" . + " JOIN `dependencies` ON `cons`.`dep`=`dependencies`.`id`" . + " JOIN `dependency_types` ON `dependencies`.`dependency_type`=`dependency_types`.`id`" + )) + die($mysql->error); + +if ($result -> num_rows > 0) + while ($row = $result->fetch_assoc()) + $edges .= "\"i" . $row["depending_on"] . "\" -> \"p" . $row["dependent"] . "\" [taillabel = \"" . $row["name"] . "\"];\n"; + +if (! $result = $mysql -> query( + "SELECT DISTINCT `install_targets`.`id`,`install_targets`.`name`" . + " FROM `cons`" . + " JOIN `dependencies` ON `cons`.`dep`=`dependencies`.`id`" . + " JOIN `install_targets` ON `dependencies`.`depending_on`=`install_targets`.`id`" + )) + die($mysql->error); + +if ($result -> num_rows > 0) + while ($row = $result->fetch_assoc()) + $knots .= "\"i" . $row["id"] . "\" [label = \"" . $row["name"] . "\", fontcolor = \"#000080\"];\n"; + +$pkgfile_query = + "CONCAT(". + "`repositories`.`name`,\"/\"," . + "`binary_packages`.`pkgname`,\"-\"," . + "IF(`binary_packages`.`epoch`=0,\"\",CONCAT(`binary_packages`.`epoch`,\":\"))," . + "`binary_packages`.`pkgver`,\"-\"," . + "`binary_packages`.`pkgrel`,\".\"," . + "`binary_packages`.`sub_pkgrel`,\"-\"," . + "`architectures`.`name`" . + ") AS `filename`"; + +if (! $result = $mysql -> query( + "SELECT DISTINCT " . + "`binary_packages`.`id`," . + "`repository_stabilities`.`name` AS `stability`," . + $pkgfile_query . + " FROM `cons`" . + " JOIN `dependencies` ON `cons`.`dep`=`dependencies`.`id`" . + " JOIN `binary_packages` ON `dependencies`.`dependent`=`binary_packages`.`id`" . + " JOIN `architectures` ON `architectures`.`id`=`binary_packages`.`architecture`" . + " JOIN `repositories` ON `repositories`.`id`=`binary_packages`.`repository`" . + " JOIN `repository_stabilities` ON `repository_stabilities`.`id`=`repositories`.`stability`" + )) + die($mysql->error); + +if ($result -> num_rows > 0) + while ($row = $result->fetch_assoc()) + $knots .= "\"p" . $row["id"] . "\" [label = \"" . $row["filename"] . "\", fontcolor = \"" . $colors[$row["stability"]] . "\"];\n"; + +if (! $result = $mysql -> query( + "SELECT DISTINCT " . + "`binary_packages`.`id`," . + "`repository_stabilities`.`name` AS `stability`," . + $pkgfile_query . + " FROM `cons`" . + " JOIN `install_target_providers` ON `cons`.`itp`=`install_target_providers`.`id`" . + " JOIN `binary_packages` ON `install_target_providers`.`package`=`binary_packages`.`id`" . + " JOIN `architectures` ON `architectures`.`id`=`binary_packages`.`architecture`" . + " JOIN `repositories` ON `repositories`.`id`=`binary_packages`.`repository`" . + " JOIN `repository_stabilities` ON `repository_stabilities`.`id`=`repositories`.`stability`" + )) + die($mysql->error); + +if ($result -> num_rows > 0) + while ($row = $result->fetch_assoc()) + $knots .= "\"p" . $row["id"] . "\" [label = \"" . $row["filename"] . "\", fontcolor = \"" . $colors[$row["stability"]] . "\"];\n"; + +$knots = str_replace("\$","\\\$",$knots); +$edges = str_replace("\$","\\\$",$edges); + +header ("Content-type: image/png"); +passthru( + "dot -Tpng -o/dev/stdout /dev/stdin <<EOF\n" . + "digraph dependencies {\n" . + "rankdir=LR;\n" . + "fontname=dejavu;\n" . + $knots . + $edges . + "}\n" . + "EOF\n" +); + +?> diff --git a/web-scripts/mysql-issues.php b/web-scripts/mysql-issues.php new file mode 100755 index 0000000..2f1d99d --- /dev/null +++ b/web-scripts/mysql-issues.php @@ -0,0 +1,136 @@ +<html> +<head> +<title>More and less critical issues with the database</title> +<link rel="stylesheet" type="text/css" href="/static/style.css"> +</head> +<body> +<a href="/">Start page</a><br> +<?php + + if (isset($_GET["ignore-haskell"])) + $ignore = " AND `install_targets`.`name` NOT LIKE \"libHS%\""; + else + $ignore = ""; + + $mysql = new mysqli("localhost", "webserver", "empty", "buildmaster"); + if ($mysql->connect_error) { + die("Connection failed: " . $mysql->connect_error); + } + + if (! $result = $mysql -> query( + "SELECT CONCAT(" . + "`repositories`.`name`,\"/\"," . + "`binary_packages`.`pkgname`,\"-\"," . + "IF(`binary_packages`.`epoch`=0,\"\",CONCAT(`binary_packages`.`epoch`,\":\"))," . + "`binary_packages`.`pkgver`,\"-\"," . + "`binary_packages`.`pkgrel`,\".\"," . + "`binary_packages`.`sub_pkgrel`,\"-\"," . + "`architectures`.`name`) AS `pkgfile`," . + "`install_targets`.`name` AS `install_target`," . + "IF(`binary_packages`.`is_to_be_deleted`,1,0) AS `is_to_be_deleted`," . + "`subst_r`.`name` AS `subst_repository`," . + "`subst_buildlist_bp`.`id` AS `subst_buildlist`" . + " FROM `binary_packages`" . + " JOIN `repositories` ON `binary_packages`.`repository`=`repositories`.`id`" . + " AND `repositories`.`is_on_master_mirror`" . + " JOIN `dependencies` ON `dependencies`.`dependent`=`binary_packages`.`id`" . + " JOIN `dependency_types` ON `dependencies`.`dependency_type`=`dependency_types`.`id`" . + " AND `dependency_types`.`relevant_for_binary_packages`" . + " JOIN `install_targets` ON `dependencies`.`depending_on`=`install_targets`.`id`" . + " JOIN `architectures` ON `binary_packages`.`architecture`=`architectures`.`id`" . + " LEFT JOIN (`binary_packages` AS `subst_bp`" . + " JOIN `repositories` AS `subst_r` ON `subst_bp`.`repository`=`subst_r`.`id`" . + " JOIN `repository_stability_relations` ON `repository_stability_relations`.`less_stable`=`subst_r`.`id`" . + ")" . + " ON `subst_bp`.`pkgname`=`binary_packages`.`pkgname`" . + " AND `subst_bp`.`id`!=`binary_packages`.`id`" . + " AND `repository_stability_relations`.`more_stable`=`repositories`.`id`" . + " LEFT JOIN (`binary_packages` AS `subst_buildlist_bp`" . + " JOIN `repositories` AS `subst_buildlist_r`" . + " ON `subst_buildlist_bp`.`repository`=`subst_buildlist_r`.`id`" . + " AND `subst_buildlist_r`.`name`=\"build-list\"". + ") ON `subst_buildlist_bp`.`pkgname`=`binary_packages`.`pkgname`" . + " WHERE NOT EXISTS (" . + "SELECT * FROM `install_target_providers`" . + " WHERE `install_target_providers`.`install_target` = `dependencies`.`depending_on`" . + ")" . + $ignore . + " ORDER BY `is_to_be_deleted`, `binary_packages`.`pkgname`" + )) + die($mysql -> error); + + print "Found " . ($result -> num_rows) . " serious issues.<br>\n"; + + if ($result -> num_rows > 0) { + + while ($row = $result->fetch_assoc()) { + if ($row["is_to_be_deleted"]==1) + print "<font color=\"#00ff00\">(marked as to-be-deleted) "; + else + print "<font color=\"#ff0000\">"; + print $row["pkgfile"] . " depends on " . $row["install_target"] . " which is not provided by any package"; + if (isset($row["subst_repository"])) + print " - but can be replaced by the one in " . $row["subst_repository"]; + elseif (isset($row["subst_buildlist"])) + print " - but is already rescheduled"; + print ".<br>"; + print "</font>\n"; + } + + } + + if (! $result = $mysql -> query( + "SELECT CONCAT(" . + "`repositories`.`name`,\"/\"," . + "`binary_packages`.`pkgname`,\"-\"," . + "IF(`binary_packages`.`epoch`=0,\"\",CONCAT(`binary_packages`.`epoch`,\":\"))," . + "`binary_packages`.`pkgver`,\"-\"," . + "`binary_packages`.`pkgrel`,\".\"," . + "`binary_packages`.`sub_pkgrel`,\"-\"," . + "`architectures`.`name`) AS `pkgfile`," . + "`install_targets`.`name` AS `install_target`," . + "`repository_stabilities`.`name` AS `stability`," . + "IF(`binary_packages`.`is_to_be_deleted`,1,0) AS `is_to_be_deleted`" . + " FROM `binary_packages`" . + " JOIN `repositories` ON `binary_packages`.`repository`=`repositories`.`id`" . + " AND `repositories`.`is_on_master_mirror`" . + " JOIN `repository_stabilities` ON `repositories`.`stability`=`repository_stabilities`.`id`" . + " JOIN `dependencies` ON `dependencies`.`dependent`=`binary_packages`.`id`" . + " JOIN `dependency_types` ON `dependencies`.`dependency_type`=`dependency_types`.`id`" . + " AND `dependency_types`.`relevant_for_binary_packages`" . + " JOIN `install_targets` ON `dependencies`.`depending_on`=`install_targets`.`id`" . + " JOIN `architectures` ON `binary_packages`.`architecture`=`architectures`.`id`" . + " WHERE EXISTS (" . + "SELECT * FROM `install_target_providers`" . + " WHERE `install_target_providers`.`install_target` = `dependencies`.`depending_on`" . + ")" . + " AND NOT EXISTS (" . + "SELECT * FROM `install_target_providers`" . + " JOIN `binary_packages` AS `prov_bp` ON `prov_bp`.`id`=`install_target_providers`.`package`" . + " JOIN `repositories` AS `prov_r` ON `prov_bp`.`repository`=`prov_r`.`id`" . + " JOIN `repository_stability_relations` ON `prov_r`.`stability`=`repository_stability_relations`.`more_stable`" . + " WHERE `install_target_providers`.`install_target` = `dependencies`.`depending_on`" . + " AND `repositories`.`stability`=`repository_stability_relations`.`less_stable`" . + ")" . + $ignore . + " ORDER BY `is_to_be_deleted`, `binary_packages`.`pkgname`" + )) + die($mysql -> error); + + print "Found " . ($result -> num_rows) . " stability issues.<br>\n"; + + if ($result -> num_rows > 0) { + while ($row = $result->fetch_assoc()) { + if ($row["is_to_be_deleted"]==1) + print "<font color=\"#00ff00\">(marked as to-be-deleted) "; + else + print "<font color=\"#800000\">"; + print $row["pkgfile"] . " depends on " . $row["install_target"] . " which is not provided by any package installable from enabled " . $row["stability"] . " repositories.<br>"; + print "</font>\n"; + } + + } + +?> +</body> +</html> diff --git a/web-scripts/statistics.php b/web-scripts/statistics.php index 279aa39..c919f93 100644 --- a/web-scripts/statistics.php +++ b/web-scripts/statistics.php @@ -1,54 +1,49 @@ <?php -$columns = array( - 'stable', - 'tasks', - 'pending_packages', - 'staging', - 'testing', - 'broken', - 'loops', - 'looped_packages', - 'locked', - 'blocked', - 'next_pending', - 'tested' -); - -$print_columns = array( - 'tasks', - 'pending_packages', - 'staging', - 'testing', - 'tested', - 'broken', - 'loops', - 'looped_packages', - 'locked', - 'blocked', - 'next_pending' -); +if (isset($_GET["from"])) + $min_time="from_base64(\"" . base64_encode("-".$_GET["from"]) . "\")"; +else + $min_time="\"-7 00:00:00\""; + +$mysql = new mysqli("localhost", "webserver", "empty", "buildmaster"); +if ($mysql->connect_error) { + die("Connection failed: " . $mysql->connect_error); +} +if (! $result = $mysql -> query( + "SELECT DISTINCT ". + "UNIX_TIMESTAMP(`statistics`.`date`) AS `date`," . + "`statistics`.`pending_tasks_count`," . + "`statistics`.`pending_packages_count`," . + "`statistics`.`staging_packages_count`," . + "`statistics`.`testing_packages_count`," . + "`statistics`.`tested_packages_count`," . + "`statistics`.`broken_tasks_count`," . + "`statistics`.`dependency_loops_count`," . + "`statistics`.`dependency_looped_tasks_count`," . + "`statistics`.`locked_tasks_count`," . + "`statistics`.`blocked_tasks_count`," . + "`statistics`.`next_tasks_count`" . + "FROM `statistics` " . + "WHERE `statistics`.`date`>=ADDTIME(NOW()," . $min_time . ") " . + "ORDER BY `statistics`.`date`" + )) + die($mysql->error); $t_min = -1; $t_max = -1; $val_max = -1; -foreach (explode("\n",trim(file_get_contents('/srv/http/statistics'))) as $val) { - $val = explode(" ",$val); - if (($t_min == -1) || ($t_min > $val[0])) - $t_min = $val[0]; - if (($t_max == -1) || ($t_max < $val[0])) - $t_max = $val[0]; - foreach ($columns as $id => $column) { - if (count($val) > $id+1) - $values[$column][$val[0]] = $val[$id+1]; - }; - foreach ($print_columns as $column) { - if (array_key_exists($column,$values)) - if (($val_max == -1) || ($val_max < $values[$column][$val[0]])) - $val_max = $values[$column][$val[0]]; - } +while($vals = $result->fetch_assoc()) { + if ($t_min == -1) + $t_min = $vals["date"]; + $t_max = $vals["date"]; + foreach ($vals as $column => $val) + if ($column != "date") { + $values[$column][$vals["date"]] = $val; + $val_max = max($val_max,$val); + } }; +$print_columns = array_keys($values); $max_len = 0; foreach ($print_columns as $column) { @@ -61,7 +56,7 @@ $width = 1600; $height = 600; $border = 5; $legend_line_length = 10; -$legend_height = 3 * ImageFontHeight(5) + $legend_line_length; +$legend_height = 4 * ImageFontHeight(5) + $legend_line_length; $im = @ImageCreate ($width + $legend_line_length + $max_len * ImageFontWidth(5), $height + $legend_height) or die ("Cannot create new gd-image-stream"); @@ -69,18 +64,18 @@ $im = @ImageCreate ($width + $legend_line_length + $max_len * ImageFontWidth(5), $background_color = ImageColorAllocate ($im, 255, 255, 255); $foreground_color = ImageColorAllocate ($im, 0, 0, 0); -$colors['stable'] = ImageColorAllocate ($im, 0, 0, 0); -$colors['tasks'] = ImageColorAllocate ($im, 0, 0, 128); -$colors['pending_packages'] = ImageColorAllocate ($im, 0, 0, 255); -$colors['staging'] = ImageColorAllocate ($im, 0, 100, 0); -$colors['testing'] = ImageColorAllocate ($im, 0, 200, 0); -$colors['tested'] = ImageColorAllocate ($im, 100, 255, 0); -$colors['broken'] = ImageColorAllocate ($im, 255, 0, 0); -$colors['loops'] = ImageColorAllocate ($im, 128, 128, 0); -$colors['looped_packages'] = ImageColorAllocate ($im, 255, 128, 128); -$colors['locked'] = ImageColorAllocate ($im, 128, 128, 128); -$colors['blocked'] = ImageColorAllocate ($im, 128, 0, 0); -$colors['next_pending'] = ImageColorAllocate ($im, 0, 255, 255); +$colors['stable_packages_count'] = ImageColorAllocate ($im, 0, 0, 0); +$colors['pending_tasks_count'] = ImageColorAllocate ($im, 0, 0, 128); +$colors['pending_packages_count'] = ImageColorAllocate ($im, 0, 0, 255); +$colors['staging_packages_count'] = ImageColorAllocate ($im, 0, 100, 0); +$colors['testing_packages_count'] = ImageColorAllocate ($im, 0, 200, 0); +$colors['tested_packages_count'] = ImageColorAllocate ($im, 100, 255, 0); +$colors['broken_tasks_count'] = ImageColorAllocate ($im, 255, 0, 0); +$colors['dependency_loops_count'] = ImageColorAllocate ($im, 128, 128, 0); +$colors['dependency_looped_tasks_count'] = ImageColorAllocate ($im, 255, 128, 128); +$colors['locked_tasks_count'] = ImageColorAllocate ($im, 128, 128, 128); +$colors['blocked_tasks_count'] = ImageColorAllocate ($im, 128, 0, 0); +$colors['next_tasks_count'] = ImageColorAllocate ($im, 0, 255, 255); function scale($x, $x_min, $x_max, $scale, $log) { if ($log) { @@ -128,13 +123,13 @@ function print_graph($data, $color) { ImageRectangle($im, $legend_line_length, 0, $width-1+$legend_line_length, $height-1, $foreground_color); -ImageString($im, 5, $legend_line_length, $height + $legend_line_length + 2*ImageFontHeight(5), "( ".trim(shell_exec("uptime | sed 's|^.*\\s\\(load\\)|\\1|'"))." )", $foreground_color); +ImageString($im, 5, $legend_line_length, $height + 2*$legend_line_length + 2*ImageFontHeight(5), "( ".trim(shell_exec("uptime | sed 's|^.*\\s\\(load\\)|\\1|'"))." )", $foreground_color); $xpos = $legend_line_length; foreach ($print_columns as $column) { print_graph($values[$column], $colors[$column]); - ImageString($im, 5, $xpos, $height + $legend_line_length + ImageFontHeight(5), $column, $colors[$column]); - $xpos += (strlen($column) + 1.75) * ImageFontWidth(5); + ImageString($im, 5, $xpos, $height + $legend_line_length + ImageFontHeight(5), substr($column,0,-strlen("_count")), $colors[$column]); + $xpos += (strlen($column) - strlen("_count") + 1.75) * ImageFontWidth(5); } ImageString($im, 5, $legend_line_length, $height + $legend_line_length, date('Y-m-d H:i', $t_min), $foreground_color); @@ -174,6 +169,4 @@ header ("Content-type: image/png"); ImagePNG ($im); -// passthru('wc -l /srv/http/statistics'); - ?> diff --git a/web-scripts/to-delete.php b/web-scripts/to-delete.php new file mode 100644 index 0000000..dfa5a39 --- /dev/null +++ b/web-scripts/to-delete.php @@ -0,0 +1,61 @@ +<html> +<head> +<title>List of packages to be deleted</title> +<link rel="stylesheet" type="text/css" href="/static/style.css"> +</head> +<body> +<?php + +$mysql = new mysqli("localhost", "webserver", "empty", "buildmaster"); +if ($mysql->connect_error) { + die("Connection failed: " . $mysql->connect_error); +} + +$result = $mysql -> query( + "SELECT " . + "`repositories`.`name` AS `repo`," . + "`binary_packages`.`pkgname`," . + "`binary_packages`.`epoch`," . + "`binary_packages`.`pkgver`," . + "`binary_packages`.`pkgrel`," . + "`binary_packages`.`sub_pkgrel`," . + "`architectures`.`name` AS `arch` " . + "FROM `binary_packages` " . + "JOIN `architectures` ON `binary_packages`.`architecture`=`architectures`.`id` " . + "JOIN `repositories` ON `binary_packages`.`repository`=`repositories`.`id` " . + "WHERE `binary_packages`.`is_to_be_deleted` " . + "AND NOT `repositories`.`name` IN (\"build-support\",\"build-list\",\"deletion-list\")" +); +if ($result -> num_rows > 0) { + + $count = 0; + + while ($row = $result->fetch_assoc()) { + $rows[$count] = + $row["repo"] . "/" . + $row["pkgname"] . "-"; + if ($row["epoch"] != "0") + $rows[$count] = + $rows[$count] . + $row["epoch"] . ":"; + $rows[$count] = + $rows[$count] . + $row["pkgver"] . "-" . + $row["pkgrel"] . "." . + $row["sub_pkgrel"] . "-" . + $row["arch"] . ".pkg.tar.xz"; + $count++; + } + + sort($rows); + + foreach ($rows as $row) { + print $row."<br>\n"; + } +} else { + print "No packages are to be deleted.\n"; +} + +?> +</body> +</html> diff --git a/web-scripts/todos.php b/web-scripts/todos.php new file mode 100644 index 0000000..e4ab2c9 --- /dev/null +++ b/web-scripts/todos.php @@ -0,0 +1,66 @@ +<?php + +$mysql = new mysqli("localhost", "webserver", "empty", "buildmaster"); +if ($mysql->connect_error) { + die("Connection failed: " . $mysql->connect_error); +} + +$result = $mysql -> query( + "SELECT DISTINCT " . + "`todos`.`id`," . + "`todos`.`file`," . + "`todos`.`line`," . + "`todos`.`description` " . + "FROM `todos`;" +); + +if ($result -> num_rows > 0) { + + while ($row = $result->fetch_assoc()) + $knot_rows[$row["id"]] = + $row["file"]. " (line ".$row["line"]."):\\n".$row["description"]; + + unset($knots); + foreach ($knot_rows as $knot) + $knots=$knots . "\"" . $knot . "\";\n"; + +} + +$result = $mysql -> query( + "SELECT DISTINCT " . + "`todo_links`.`dependent`," . + "`todo_links`.`depending_on` " . + "FROM `todo_links`;" +); + +if ($result -> num_rows > 0) { + $count = 0; + while ($row = $result->fetch_assoc()) { + $link_rows[$count]["dependent"] = + $knot_rows[$row["dependent"]]; + $link_rows[$count]["depending_on"] = + $knot_rows[$row["depending_on"]]; + $count++; + } + + unset($edges); + foreach ($link_rows as $link) + $edges=$edges . "\"" . $link["depending_on"] . "\" -> \"" . $link["dependent"] . "\";\n"; +} + +$knots = str_replace("\$","\\\$",$knots); +$edges = str_replace("\$","\\\$",$edges); + +header ("Content-type: image/png"); +passthru( + "dot -Tpng -o/dev/stdout /dev/stdin <<EOF\n" . + "digraph dependencies {\n" . + "rankdir=LR;\n" . + "fontname=dejavu;\n" . + $knots . + $edges . + "}\n" . + "EOF\n" +); + +?> |