diff options
-rw-r--r-- | config | 45 | ||||
-rw-r--r-- | config.local.import-community | 5 | ||||
-rw-r--r-- | config.local.import-packages | 5 | ||||
-rw-r--r-- | config.local.parabola | 14 | ||||
-rw-r--r-- | config.local.svn-community | 13 | ||||
-rw-r--r-- | config.local.svn-packages | 13 | ||||
-rw-r--r-- | config.testing | 10 | ||||
-rwxr-xr-x | cron-jobs/devlist-mailer | 5 | ||||
-rwxr-xr-x | cron-jobs/ftpdir-cleanup | 10 | ||||
-rwxr-xr-x | cron-jobs/repo-sanity-check | 57 | ||||
-rwxr-xr-x | cron-jobs/sourceballs | 11 | ||||
-rw-r--r-- | cron-jobs/sourceballs.skip | 24 | ||||
-rw-r--r-- | db-functions | 150 | ||||
-rwxr-xr-x | db-import | 288 | ||||
-rwxr-xr-x | db-import-pick-mirror | 25 | ||||
-rw-r--r-- | db-import.conf | 23 | ||||
-rwxr-xr-x | db-move | 13 | ||||
-rwxr-xr-x | db-update | 22 | ||||
-rw-r--r-- | test/lib/common.inc | 37 | ||||
-rwxr-xr-x | test/test.d/create-filelists.sh | 2 | ||||
-rwxr-xr-x | test/test.d/db-update.sh | 6 | ||||
-rwxr-xr-x | test/test.d/testing2x.sh | 2 |
22 files changed, 564 insertions, 216 deletions
@@ -3,34 +3,53 @@ # Instead, create separate ${toolname}.conf files. Only add a # variable here if multiple tools start needing the option. -FTP_BASE="/srv/repo/main" +case "$USER" in + db-import-packages) _name=import-packages;; + db-import-community) _name=import-community;; + *) _name=parabola;; +esac -PKGREPOS=( - 'core' 'testing' 'extra' 'community' 'multilib' 'multilib-testing' - 'libre' 'libre-testing' 'libre-multilib' 'libre-multilib-testing' - '~smv' '~xihh' '~brendan' '~lukeshu' '~emulatorman' '~aurelien' '~jorginho' '~coadde' '~drtan' - 'nonsystemd' 'nonsystemd-testing' 'nonprism' 'nonprism-testing' 'pcr' 'kernels' 'cross' 'java') -PKGPOOL='pool/parabola_gnu+linux' -SRCPOOL='src/parabola_gnu+linux' +FTP_BASE="/srv/repo/main" +PKGREPOS=() +# PKGREPOS=( +# 'core' 'testing' 'extra' 'community' 'multilib' 'multilib-testing' +# 'libre' 'libre-testing' 'libre-multilib' 'libre-multilib-testing' +# '~smv' '~xihh' '~brendan' '~lukeshu' '~emulatorman' '~aurelien' '~jorginho' '~coadde' '~drtan' +# 'nonsystemd' 'nonsystemd-testing' 'nonprism' 'nonprism-testing' 'pcr' 'kernels' 'cross' 'java') +PKGPOOL='' +SRCPOOL='' -CLEANUP_DESTDIR="$FTP_BASE/old/pkg" +CLEANUP_DESTDIR="/srv/repo/private/${_name}/package-cleanup" CLEANUP_DRYRUN=false # Time in days to keep moved packages CLEANUP_KEEP=30 -SOURCE_CLEANUP_DESTDIR="$FTP_BASE/old/src" -SOURCE_CLEANUP_DRYRUN=true +SOURCE_CLEANUP_DESTDIR="/srv/repo/private/${_name}/source-cleanup" +SOURCE_CLEANUP_DRYRUN=false # Time in days to keep moved sourcepackages -SOURCE_CLEANUP_KEEP=30 +SOURCE_CLEANUP_KEEP=14 REQUIRE_SIGNATURE=true LOCK_DELAY=10 [ -n "${STAGING:-}" ] || STAGING="$HOME/staging/unknown/staging" -TMPDIR="/tmp" +export TMPDIR="${TMPDIR:-/tmp}" ARCHES=(i686 x86_64 armv7h) DBEXT=".db.tar.gz" FILESEXT=".files.tar.gz" PKGEXT=".pkg.tar.?z" SRCEXT=".src.tar.gz" + +# Allowed licenses: get sourceballs only for licenses in this array +# Empty (commented out) to get sourceballs for all packages +#ALLOWED_LICENSES=('GPL' 'GPL1' 'GPL2' 'LGPL' 'LGPL1' 'LGPL2' 'LGPL2.1') + +# Where to send error emails, and who they are from +LIST="dev@lists.parabola.nu" +FROM="dbscripts+${_name}@$(hostname -f)" + +# Override default config with config.local +[ -f "$(dirname "${BASH_SOURCE[0]}")/config.local" ] && . "$(dirname "${BASH_SOURCE[0]}")/config.local" +[ -f "$(dirname "${BASH_SOURCE[0]}")/config.local.${_name}" ] && . "$(dirname "${BASH_SOURCE[0]}")/config.local.${_name}" +unset _name diff --git a/config.local.import-community b/config.local.import-community new file mode 100644 index 0000000..393fd57 --- /dev/null +++ b/config.local.import-community @@ -0,0 +1,5 @@ +#!/hint/bash + +PKGREPOS=({community,multilib}{,-testing,-staging}) +PKGPOOL='pool/community' +SRCPOOL='sources/community' diff --git a/config.local.import-packages b/config.local.import-packages new file mode 100644 index 0000000..c699b28 --- /dev/null +++ b/config.local.import-packages @@ -0,0 +1,5 @@ +#!/hint/bash + +PKGREPOS=('core' 'extra' 'testing' 'staging' {kde,gnome}-unstable) +PKGPOOL='pool/packages' +SRCPOOL='sources/packages' diff --git a/config.local.parabola b/config.local.parabola new file mode 100644 index 0000000..648dfbb --- /dev/null +++ b/config.local.parabola @@ -0,0 +1,14 @@ +#!/hint/bash + +PKGREPOS=( + # Main repos + libre{,-testing} + libre-multilib{,-testing} + # Community project repos + {nonsystemd,nonprism}{,-testing} + pcr kernels cross java + # User repos + '~smv' '~xihh' '~brendan' '~lukeshu' '~emulatorman' '~aurelien' '~jorginho' '~coadde' '~drtan' +) +PKGPOOL='pool/parabola' +SRCPOOL='sources/parabola' diff --git a/config.local.svn-community b/config.local.svn-community new file mode 100644 index 0000000..5d61b5e --- /dev/null +++ b/config.local.svn-community @@ -0,0 +1,13 @@ +#!/hint/bash + +PKGREPOS=('community' 'community-testing' 'community-staging' 'multilib' 'multilib-testing' 'multilib-staging') +PKGPOOL='pool/community' +SRCPOOL='sources/community' +SVNREPO='file:///srv/repos/svn-community/svn' +SVNUSER='svn-community' +TESTING_REPO='community-testing' +STABLE_REPOS=('community') + +CLEANUP_DESTDIR="/srv/repos/svn-community/package-cleanup" +SOURCE_CLEANUP_DESTDIR="/srv/repos/svn-community/source-cleanup" +TMPDIR="/srv/repos/svn-community/tmp" diff --git a/config.local.svn-packages b/config.local.svn-packages new file mode 100644 index 0000000..34aab35 --- /dev/null +++ b/config.local.svn-packages @@ -0,0 +1,13 @@ +#!/hint/bash + +PKGREPOS=('core' 'extra' 'testing' 'staging' 'kde-unstable' 'gnome-unstable') +PKGPOOL='pool/packages' +SRCPOOL='sources/packages' +SVNREPO='file:///srv/repos/svn-packages/svn' +SVNUSER='svn-packages' +TESTING_REPO='testing' +STABLE_REPOS=('core' 'extra') + +CLEANUP_DESTDIR="/srv/repos/svn-packages/package-cleanup" +SOURCE_CLEANUP_DESTDIR="/srv/repos/svn-packages/source-cleanup" +TMPDIR="/srv/repos/svn-packages/tmp" diff --git a/config.testing b/config.testing new file mode 100644 index 0000000..24c2283 --- /dev/null +++ b/config.testing @@ -0,0 +1,10 @@ +#!/hint/bash + +# The host architecture +ARCH_HOST=$(uname -m) +# Which architectures should we test building for? +ARCH_BUILD=("$ARCH_HOST"); if [[ $ARCH_HOST == x86_64 ]]; then ARCH_BUILD+=("i686"); fi + +# override the default TMPDIR +[[ -n $MASTER_TMPDIR ]] || export MASTER_TMPDIR="$(mktemp -dt "${0##*/}.XXXXXXXXXX")" +TMPDIR=$MASTER_TMPDIR diff --git a/cron-jobs/devlist-mailer b/cron-jobs/devlist-mailer index 1a05521..7f298b9 100755 --- a/cron-jobs/devlist-mailer +++ b/cron-jobs/devlist-mailer @@ -2,9 +2,8 @@ #Dummy helper to send email to arch-dev # It does nothing if no output -LIST="arch-dev-public@archlinux.org" -#LIST="aaronmgriffin@gmail.com" -FROM="repomaint@archlinux.org" +# Load $LIST and $FROM from the config file +. "$(dirname "$(readlink -e "$0")")/../config" SUBJECT="Repository Maintenance $(date +"%d-%m-%Y")" if [ $# -ge 1 ]; then diff --git a/cron-jobs/ftpdir-cleanup b/cron-jobs/ftpdir-cleanup index 4a2b418..4063c09 100755 --- a/cron-jobs/ftpdir-cleanup +++ b/cron-jobs/ftpdir-cleanup @@ -22,6 +22,14 @@ clean_pkg() { fi } +script_lock + +for repo in "${PKGREPOS[@]}"; do + for arch in "${ARCHES[@]}"; do + repo_lock "${repo}" "${arch}" || exit 1 + done +done + "${CLEANUP_DRYRUN}" && warning 'dry run mode is active' for repo in "${PKGREPOS[@]}"; do @@ -56,7 +64,7 @@ done # get a list of all available packages in the pacakge pool find "$FTP_BASE/${PKGPOOL}" -name "*${PKGEXT}" -printf '%f\n' | sort > "${WORKDIR}/pool" # create a list of packages in our db -cat "${WORKDIR}/db-"* | sort -u > "${WORKDIR}/db" +find "${WORKDIR}" -maxdepth 1 -type f -name 'db-*' -exec cat {} \; | sort -u > "${WORKDIR}/db" old_pkgs=($(comm -23 "${WORKDIR}/pool" "${WORKDIR}/db")) if [ ${#old_pkgs[@]} -ge 1 ]; then diff --git a/cron-jobs/repo-sanity-check b/cron-jobs/repo-sanity-check deleted file mode 100755 index 239f042..0000000 --- a/cron-jobs/repo-sanity-check +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -# Solves issue165... on the old roundup install. From the database -# backups, the title was "Older/deprecated packages never leave the -# repo", I don't know how the body of the issue is stored in the DB, -# but the title says enough, I think. - -. "$(dirname "$(readlink -e "$0")")/../config" -. "$(dirname "$(readlink -e "$0")")/../db-functions" - -# Traverse all repos -for _repo in "${PKGREPOS[@]}"; do - msg "Cleaning up [%s]" "${_repo}" - - # Find all pkgnames on this repo's abs - on_abs=($( - find "${SVNREPO}/${_repo}" -name PKGBUILD | \ - while read pkgbuild; do - source "${pkgbuild}" >/dev/null 2>&1 - # cleanup to save memory - unset build package source md5sums pkgdesc pkgver pkgrel epoch \ - url license arch depends makedepends optdepends options \ - >/dev/null 2>&1 - - # also cleanup package functions - for _pkg in "${pkgname[@]}"; do - unset "package_${pkg}" >/dev/null 2>&1 - done - - # this fills the on_abs array - echo "${pkgname[@]}" - done - )) - - # quit if abs is empty - if [ ${#on_abs[*]} -eq 0 ]; then - warning "[%s]'s ABS tree is empty, skipping" "${_repo}" - break - fi - - # Find all pkgnames on repos - on_repo=($( - find "${FTP_BASE}/${_repo}" -name "*.pkg.tar.?z" \ - -printf "%f\n" | sed "s/^\(.\+\)-[^-]\+-[^-]\+-[^-]\+$/\1/" - )) - - # Compares them, whatever is on repos but not on abs should be removed - remove=($(comm -13 \ - <(printf '%s\n' "${on_abs[@]}" | sort -u) \ - <(printf '%s\n' "${on_repo[@]}" | sort -u) )) - - # Remove them from databases, ftpdir-cleanup will take care of the rest - find "${FTP_BASE}/${_repo}" -name "*.db.tar.?z" \ - -exec repo-remove {} "${remove[@]}" \; >/dev/null 2>&1 - - msg2 "Removed the following packages:" - plain '%s' "${remove[@]}" -done diff --git a/cron-jobs/sourceballs b/cron-jobs/sourceballs index c12a128..c02912a 100755 --- a/cron-jobs/sourceballs +++ b/cron-jobs/sourceballs @@ -63,11 +63,10 @@ for repo in "${PKGREPOS[@]}"; do if grep -Fqx "${pkgbase}" "${dirname}/sourceballs.skip"; then continue fi - # Commenting out, we'll sourceball everything # Check if the license or .force file does not enforce creating a source package -# if ! (chk_license ${pkglicense[@]} || grep -Fqx "${pkgbase}" "${dirname}/sourceballs.force"); then -# continue -# fi + if ! ([[ -z ${ALLOWED_LICENSES[*]} ]] || chk_license "${pkglicense[@]}" || grep -Fqx "${pkgbase}" "${dirname}/sourceballs.force"); then + continue + fi # Store the expected file name of the source package echo "${pkgbase}-${pkgver}${SRCEXT}" >> "${WORKDIR}/expected-src-pkgs" @@ -118,8 +117,8 @@ for repo in "${PKGREPOS[@]}"; do done # Cleanup old source packages -cat "${WORKDIR}/expected-src-pkgs" | sort -u > "${WORKDIR}/expected-src-pkgs.sort" -cat "${WORKDIR}/available-src-pkgs" | sort -u > "${WORKDIR}/available-src-pkgs.sort" +find "${WORKDIR}" -maxdepth 1 -type f -name 'expected-src-pkgs' -exec cat {} \; | sort -u > "${WORKDIR}/expected-src-pkgs.sort" +find "${WORKDIR}" -maxdepth 1 -type f -name 'available-src-pkgs' -exec cat {} \; | sort -u > "${WORKDIR}/available-src-pkgs.sort" old_pkgs=($(comm -23 "${WORKDIR}/available-src-pkgs.sort" "${WORKDIR}/expected-src-pkgs.sort")) if [ ${#old_pkgs[@]} -ge 1 ]; then diff --git a/cron-jobs/sourceballs.skip b/cron-jobs/sourceballs.skip index 14d6f4b..0e1731c 100644 --- a/cron-jobs/sourceballs.skip +++ b/cron-jobs/sourceballs.skip @@ -1,14 +1,28 @@ -nexuiz-data +0ad-data +alienarena-data +blobwars-data +btanks-data +dangerdeep-data +egoboo-data +fillets-ng-data +flightgear-data +frogatto-data +gcompris-data +naev-data +openarena-data +rocksndiamonds-data +smc-data +speed-dreams-data torcs-data tremulous-data ufoai-data -frogatto-data vdrift-data -naev-data -btanks-data +warmux-data wesnoth-data -texlive-bin +widelands-data +xonotic-data texlive-bibtexextra +texlive-bin texlive-core texlive-fontsextra texlive-formatsextra diff --git a/db-functions b/db-functions index d76aa41..1d37123 100644 --- a/db-functions +++ b/db-functions @@ -1,7 +1,7 @@ #!/hint/bash # Some PKGBUILDs need CARCH to be set -CARCH="x86_64" +CARCH=$(. "$(librelib conf.sh)"; load_files makepkg; echo "$CARCH") # Useful functions UMASK="" @@ -26,73 +26,21 @@ mv_acl() { # set up general environment WORKDIR=$(mktemp -dt "${0##*/}.XXXXXXXXXX") +if [ -n "${SVNUSER}" ]; then + setfacl -m u:"${SVNUSER}":rwx "${WORKDIR}" + setfacl -m d:u:"${USER}":rwx "${WORKDIR}" + setfacl -m d:u:"${SVNUSER}":rwx "${WORKDIR}" +fi LOCKS=() REPO_MODIFIED=0 -# check if messages are to be printed using color -unset ALL_OFF BOLD BLUE GREEN RED YELLOW -if [[ -t 2 ]]; then - ALL_OFF="$(tput sgr0)" - BOLD="$(tput bold)" - BLUE="${BOLD}$(tput setaf 4)" - GREEN="${BOLD}$(tput setaf 2)" - RED="${BOLD}$(tput setaf 1)" - YELLOW="${BOLD}$(tput setaf 3)" -fi -readonly ALL_OFF BOLD BLUE GREEN RED YELLOW - -plain() { - local mesg=$1; shift - printf "${BOLD} ${mesg}${ALL_OFF}\n" "$@" -} - -msg() { - local mesg=$1; shift - printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" -} - -msg2() { - local mesg=$1; shift - printf "${BLUE} ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" -} - -warning() { - local mesg=$1; shift - printf "${YELLOW}==> WARNING:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 -} - -error() { - local mesg=$1; shift - printf "${RED}==> ERROR${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 -} - -## -# usage : in_array( $needle, $haystack ) -# return : 0 - found -# 1 - not found -## -in_array() { - local needle=$1; shift - [[ -z $1 ]] && return 1 # Not Found - local item - for item in "$@"; do - [[ $item = "$needle" ]] && return 0 # Found - done - return 1 # Not Found -} - -## -# usage : get_full_version( $epoch, $pkgver, $pkgrel ) -# return : full version spec, including epoch (if necessary), pkgver, pkgrel -## -get_full_version() { - if [[ $1 -eq 0 ]]; then - # zero epoch case, don't include it in version - echo "$2-$3" - else - echo "$1:$2-$3" - fi -} +# Used: plain, msg, msg2, warning, error, in_array, get_full_version, abort, die +# Overwritten: cleanup +# Ignored: stat_busy, stat_done, +# setup_workdir, trap_abort, trap_exit, +# lock, slock, lock_close +# pkgver_equal, find_cached_package, check_root +. "$(librelib common)" script_lock() { local LOCKDIR="$TMPDIR/.scriptlock.${0##*/}" @@ -141,22 +89,11 @@ cleanup() { if (( REPO_MODIFIED )); then date +%s > "${FTP_BASE}/lastupdate" - date -u +%s > "${FTP_BASE}/lastsync" fi [ "$1" ] && exit "$1" } -abort() { - msg 'Aborting...' - cleanup 0 -} - -die() { - error "$@" - cleanup 1 -} - trap abort INT QUIT TERM HUP trap cleanup EXIT @@ -165,7 +102,6 @@ trap cleanup EXIT repo_lock () { local LOCKDIR="$TMPDIR/.repolock.$1.$2" local DBLOCKFILE="${FTP_BASE}/${1}/os/${2}/${1}${DBEXT}.lck" - local FILESLOCKFILE="${FTP_BASE}/${1}/os/${2}/${1}${FILESEXT}.lck" local _count local _trial local _timeout @@ -177,10 +113,6 @@ repo_lock () { error "Repo [%s] (%s) is already locked by repo-{add,remove} process %s" "$1" "$2" "$(<"$DBLOCKFILE")" return 1 fi - if [ -f "${FILESLOCKFILE}" ]; then - error "Repo [%s] (%s) is already locked by repo-{add,remove} process %s" "$1" "$2" "$(<"$FILESLOCKFILE")" - return 1 - fi if [ $# -eq 2 ]; then _lockblock=true @@ -231,6 +163,13 @@ _grep_pkginfo() { echo "${_ret#${2} = }" } +# usage: _grep_buildinfo pkgfile pattern +_grep_buildinfo() { + local _ret + + _ret="$(/usr/bin/bsdtar -xOqf "$1" .BUILDINFO | grep -m 1 "^${2} = ")" + echo "${_ret#${2} = }" +} # Get the package base or name as fallback getpkgbase() { @@ -293,6 +232,24 @@ getpkgarch() { echo "$_ver" } +check_packager() { + local _packager + + _packager=$(_grep_pkginfo "$1" "packager") + [[ $_packager && $_packager != 'Unknown Packager' ]] +} + +check_buildinfo() { + /usr/bin/bsdtar -tf "$1" .BUILDINFO >/dev/null 2>&1 +} + +check_builddir() { + local _builddir + + _builddir=$(_grep_buildinfo "$1" "builddir") + [[ $_builddir && $_builddir = '/build' ]] +} + getpkgfile() { if [[ ${#} -ne 1 ]]; then error 'No canonical package found!' @@ -340,7 +297,7 @@ check_pkgfile() { in_array "${pkgarch}" "${ARCHES[@]}" 'any' || return 1 - if echo "${pkgfile##*/}" | grep -q "${pkgname}-${pkgver}-${pkgarch}"; then + if echo "${pkgfile##*/}" | grep "${pkgname}-${pkgver}-${pkgarch}" &>/dev/null; then return 0 else return 1 @@ -384,7 +341,6 @@ check_splitpkgs() { for pkgfile in "${pkgfiles[@]}"; do issplitpkg "${pkgfile}" || continue local _pkgbase="$(getpkgbase "${pkgfile}")" - msg2 "Checking %s" "$_pkgbase" local _pkgname="$(getpkgname "${pkgfile}")" local _pkgarch="$(getpkgarch "${pkgfile}")" mkdir -p "${repo}/${_pkgarch}/${_pkgbase}" @@ -422,17 +378,6 @@ check_pkgrepos() { [ -f "${FTP_BASE}/${PKGPOOL}/${pkgfile##*/}" ] && return 1 [ -f "${FTP_BASE}/${PKGPOOL}/${pkgfile##*/}.sig" ] && return 1 - local repo - local arch - for repo in "${PKGREPOS[@]}"; do - for arch in "${ARCHES[@]}"; do - [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT} ] && return 1 - [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT}.sig ] && return 1 - [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkgfile##*/}" ] && return 1 - [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkgfile##*/}.sig" ] && return 1 - done - done - return 0 } @@ -492,10 +437,8 @@ arch_repo_add() { printf -v pkgs_str -- '%q ' "${pkgs[@]}" # package files might be relative to repo dir pushd "${FTP_BASE}/${repo}/os/${arch}" >/dev/null - /usr/bin/repo-add -q "${repo}${DBEXT}" "${pkgs[@]}" >/dev/null \ + /usr/bin/repo-add -q "${repo}${DBEXT}" "${pkgs[@]}" \ || error 'repo-add %q %s' "${repo}${DBEXT}" "${pkgs_str% }" - /usr/bin/repo-add -f -q "${repo}${FILESEXT}" "${pkgs[@]}" \ - || error 'repo-add -f %q %s' "${repo}${FILESEXT}" "${pkgs_str% }" popd >/dev/null set_repo_permission "${repo}" "${arch}" @@ -507,18 +450,23 @@ arch_repo_remove() { local arch=$2 local pkgs=("${@:3}") local dbfile="${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT}" - local filesfile="${FTP_BASE}/${repo}/os/${arch}/${repo}${FILESEXT}" if [ ! -f "${dbfile}" ]; then error "No database found at '%s'" "${dbfile}" return 1 fi printf -v pkgs_str -- '%q ' "${pkgs[@]}" - /usr/bin/repo-remove -q "${dbfile}" "${pkgs[@]}" >/dev/null \ + /usr/bin/repo-remove -q "${dbfile}" "${pkgs[@]}" \ || error 'repo-remove %q %s' "${dbfile}" "${pkgs_str% }" - /usr/bin/repo-remove -q "${filesfile}" "${pkgs[@]}" \ - || error 'repo-remove %q %s' "${filesfile}" "${pkgs_str% }" set_repo_permission "${repo}" "${arch}" REPO_MODIFIED=1 } + +arch_svn() { + if [ -z "${SVNUSER}" ]; then + /usr/bin/svn "${@}" + else + sudo -u "${SVNUSER}" -- /usr/bin/svn --username "${USER}" "${@}" + fi +} diff --git a/db-import b/db-import new file mode 100755 index 0000000..a8a073d --- /dev/null +++ b/db-import @@ -0,0 +1,288 @@ +#!/bin/bash +set -euE +# Imports Arch-like repos, running them through a blacklist +# License: GPLv3 + +. "$(dirname "$(readlink -e "$0")")/config" # for: FTP_BASE DBEXT +. "$(dirname "$(readlink -e "$0")")/db-import.conf" # for: IMPORTDIR IMPORTS +. "$(librelib messages)" +. "$(librelib blacklist)" + +# DBs = pacman DataBases + +# This replaces two scripts: +# - abslibre : imported ABS tree from Arch +# - db-sync : imported pacman DBs from Arch + +# The flow here is: +# 1. "${IMPORTDIR}/cache/${name}/dbs/" # Download the pacman databases +# 2. "${IMPORTDIR}/cache/${name}/abs/" # Download the ABS tree +# 3. "${IMPORTDIR}/clean/${name}/dbs/" # Run the pacman DBs through the blacklist +# 4. "${IMPORTDIR}/clean/${name}/pkgs/" # Download the pkg files mentioned in "clean/${name}/dbs/" +# 5. "${IMPORTDIR}/staging/${tag}" # Copy all the package files we just downloaded to here +# 6. Run `db-update on` with STAGING="${IMPORTDIR}/staging/${tag}" + +# generic arguments to pass to rsync, borrowed from `abs` +SYNCARGS='-mrtvlH --no-motd --no-p --no-o --no-g' + +main() { + blacklist-update + + local importStr + for importStr in "${IMPORTS[@]}"; do + local importAry=($importStr) + local name=${importAry[0]} + local pkgmirror=${importAry[1]} + local absmirror=${importAry[2]} + local tags=("${importAry[@]:3}") + + msg "Fetching remote package source: %s" "$name" + fetch_dbs "$name" "$pkgmirror" + fetch_abs "$name" "$absmirror" "${tags[@]}" + msg "Filtering blacklisted packages from remote package source: %s" "$name" + clean_dbs "$name" "${tags[@]}" + fetch_pkgs "$name" "${tags[@]}" + msg "Publishing changes from remote package source: %s" "$name" + publish "$name" "${tags[@]}" + done + return $r +} + +fetch_dbs() { + local name=$1 + local pkgmirror=$2 + + msg2 'Synchronizing package databases...' + + mkdir -p -- "${IMPORTDIR}/cache/${name}/dbs" + # Grab just the .db files from $pkgmirror + rsync $SYNCARGS --delete-after \ + --include="*/" \ + --include="*.db" \ + --include="*${DBEXT}" \ + --exclude="*" \ + "rsync://${pkgmirror}/" "${IMPORTDIR}/cache/${name}/dbs" +} + +fetch_abs() { + local name=$1 + local absmirror=$2 + local tags=("${@:3}") + + local fake_home + local absroot + + # Sync the ABS tree from $absmirror + local arch + for arch in $(list_arches "${tags[@]}"); do + msg2 'Synchronizing %s ABS tree...' "$arch" + + absroot="${IMPORTDIR}/cache/${name}/abs/${arch}" + mkdir -p -- "$absroot" + + # Configure `abs` for this mirror + fake_home="${IMPORTDIR}/homes/${name}/${arch}" + mkdir -p -- "$fake_home" + { + printf "ABSROOT='%s'\n" "$absroot" + printf "SYNCSERVER='%s'\n" "$absmirror" + printf "ARCH='%s'\n" "$arch" + printf 'REPOS=(\n' + list_repos "$arch" "${tags[@]}" + printf ')\n' + } > "${fake_home}/.abs.conf" + + # Run `abs` + HOME=$fake_home abs + done +} + +clean_dbs() { + local name=$1 + local tags=("${@:2}") + + rm -rf -- "${IMPORTDIR}/clean/$name" + + local tag + for tag in "${tags[@]}"; do + msg2 'Creating clean version of %s package database...' "$tag" + + local cache="${IMPORTDIR}/cache/$name/dbs/$(db_file "$tag")" + local clean="${IMPORTDIR}/clean/$name/dbs/$(db_file "$tag")" + install -Dm644 "$cache" "$clean" + + blacklist-cat | blacklist-get-pkg | xargs -d '\n' repo-remove "$clean" + done +} + +fetch_pkgs() { + local name=$1 + local tags=("${@:2}") + + local repo arch dbfile whitelist + + local tag + for tag in "${tags[@]}"; do + msg2 'Syncronizing package files for %s...' "$tag" + repo=${tag%-*} + arch=${tag##*-} + + dbfile="${IMPORTDIR}/clean/$name/dbs/$(db_file "$tag")" + whitelist="${IMPORTDIR}/clean/$name/dbs/$tag.whitelist" + + list_pkgs "$dbfile" > "$whitelist" + + # fetch the architecture-specific packages + rsync $SYNCARGS --delete-after --delete-excluded \ + --delay-updates \ + --include-from=<(sed "s|\$|-$arch.tar.?z|" "$whitelist") \ + --exclude='*' \ + "rsync://${pkgmirror}/$(db_dir "$tag")/" \ + "${IMPORTDIR}/clean/${name}/pkgs/${tag}/" + + # fetch the architecture-independent packages + rsync $SYNCARGS --delete-after --delete-excluded \ + --delay-updates \ + --include-from=<(sed "s|\$|-any.tar.?z|" "$whitelist") \ + --exclude='*' \ + "rsync://${pkgmirror}/$(db_dir "$tag")/" \ + "${IMPORTDIR}/clean/${name}/pkgs/${repo}-any/" + done +} + +publish() { + local name=$1 + local tags=("${@:2}") + + local tag + for tag in "${tags[@]}"; do + msg2 'Publishing changes to %s...' "$tag" + publish_tag "$name" "$tag" + done +} + +publish_tag() { + local name=$1 + local tag=$2 + + local repo=${tag%-*} + local arch=${tag##*-} + local dir="${IMPORTDIR}/clean/${name}/pkgs/${tag}" + + local found + local error=false + local files=() + + local pkgid pkgarch + for pkgid in $(list_added_pkgs "$name" "$tag"); do + found=false + + for pkgarch in "${arch}" any; do + file="${dir}/${pkgid}-${arch}".pkg.tar.?z + if ! $found && [[ -r $file ]]; then + files+=("$file") + found=true + fi + done + + if ! $found; then + error 'Could not find package file for %s' "$pkgid" + error=true + fi + done + + if $error; then + error 'Quitting...' + return 1 + fi + + mkdir -p -- "${IMPORTDIR}/staging/${tag}/${repo}" + cp -al -- "${files[@]}" "${IMPORTDIR}/staging/${tag}/${repo}/" + STAGING="${IMPORTDIR}/staging/${tag}" db-update + + # XXX: db-remove wants pkgbase, not pkgname + list_removed_pkgs "$name" "$tag" | xargs -d '\n' db-remove "$repo" "$arch" +} + +################################################################################ + +# Usage: list_arches repo-arch... +# Returns a list of the architectures mentioned in a list of "repo-arch" pairs. +list_arches() { + local tags=("$@") + printf '%s\n' "${tags[@]##*-}" | sort -u +} + +# Usage: list_repos arch repo-arch... +# Returns a list of all the repositories mentioned for a given architecture in a +# list of "repo-arch" pairs. +list_repos() { + local arch=$1 + local tags=("${@:2}") + printf '%s\n' "${tags[@]}" | sed -n "s/-$arch\$//p" +} + +# Usage: db_dir repo-arch +db_dir() { + local tag=$1 + local repo=${tag%-*} + local arch=${tag##*-} + echo "${repo}/os/${arch}" +} + +# Usage; db_file repo-arch +db_file() { + local tag=$1 + local repo=${tag%-*} + local arch=${tag##*-} + echo "${repo}/os/${arch}/${repo}${DBEXT}" +} + +# Usage: list_pkgs dbfile +# Prints "$pkgname-$(get_full_version "$pkgname")" for every package in $dbfile +list_pkgs() { + local dbfile=$1 + bsdtar tf "$dbfile" | cut -d/ -f1 +} + +# Usage: list_pkgs | sep_ver +# Separates the pkgname from the version (replaces the '-' with ' ') for the +# list provided on stdin. +sep_ver() { + sed -r 's/-([^-]*-[^-]*)$/ \1/' +} + +# Usage: list_removed_pkgs importsrc repo-arch +# Prints "$pkgname-$(get_full_version "$pkgname")" for every removed package. +list_removed_pkgs() { + local name=$1 + local tag=$2 + + local old="${FTP_BASE}/$(db_file "$tag")" + local new="${IMPORTDIR}/clean/$name/dbs/$(db_file "$tag")" + + # make a list of: + # pkgname oldver[ newver] + # It will include removed or updated packages (changed packages) + join -a1 \ + <(list_pkgs "$old"|sep_ver|sort) \ + <(list_pkgs "$new"|sep_ver|sort) + | grep -v ' .* ' # remove updated packages + | sed 's/ /-/' # re-combine the pkgname and version +} + +# Usage: list_added_pkgs importsrc repo-arch +# slightly a misnomer; added and updated +# Prints "$pkgname-$(get_full_version "$pkgname")" for every added or updated +# package. +list_added_pkgs() { + local name=$1 + local tag=$2 + + local old="${FTP_BASE}/$(db_file "$tag")" + local new="${IMPORTDIR}/clean/$name/dbs/$(db_file "$tag")" + + comm -13 <(list_pkgs "$old") <(list_pkgs "$new") +} + +main "$@" diff --git a/db-import-pick-mirror b/db-import-pick-mirror new file mode 100755 index 0000000..4d01b95 --- /dev/null +++ b/db-import-pick-mirror @@ -0,0 +1,25 @@ +#!/usr/bin/env ruby + +require 'json' +require 'net/http' + +protocol = ARGV[0] +jsonurl = ARGV[1] + +data = JSON::parse(Net::HTTP.get(URI(jsonurl))) + +if data["version"] != 3 + print "Data format version != 3" + exit 1 +end + +# Filter out URLs with incomplete information +urls = data["urls"].select{|a| a.none?{|k,v|v.nil?}} +rsync_urls = urls.select{|a| a["protocol"]==protocol} + +# By score ( (delay+speed)/completion ) +#best = rsync_urls.sort{|a,b| (a["score"] || Float::INFINITY) <=> (b["score"] || Float::INFINITY) }.first +# By delay/completion +best = rsync_urls.sort{|a,b| a["delay"]/a["completion_pct"] <=> b["delay"]/b["completion_pct"] }.first + +puts best["url"] diff --git a/db-import.conf b/db-import.conf new file mode 100644 index 0000000..e00e7b0 --- /dev/null +++ b/db-import.conf @@ -0,0 +1,23 @@ +#!/hint/bash + +IMPORTDIR=/srv/repo/import + +case "$USER" in + db-import-packages) + _archrepos=( + {core,extra,testing,staging}-{i686,x86_64} + {gnome,kde}-unstable-{i686,x86_64} + );; + db-import-community) + _archrepos=( + community{,-testing,-staging}-{i686,x86_64} + multilib{,-testing,-staging}-x86_64 + );; +esac + +_archpkgmirror=$(db-import-pick-mirror rsync https://www.archlinux.org/mirrors/status/tier/1/json/) + +# name pkgmirror absmirror repo-arch... +IMPORTS=("archlinux ${_archpkgmirror} rsync.archlinux.org ${_archrepos[*]}") + +unset _archrepos _archpkgmirror @@ -34,11 +34,6 @@ for pkgbase in "${args[@]:2}"; do die "Could not read pkgname" fi - pkgver=$(. "${xbsrepo_from}/PKGBUILD"; get_full_version "${epoch:-0}" "${pkgver}" "${pkgrel}") - if [ -z "${pkgver}" ]; then - die "Could not read pkgver" - fi - if [ "${pkgarch}" == 'any' ]; then tarches=("${ARCHES[@]}") else @@ -46,6 +41,10 @@ for pkgbase in "${args[@]:2}"; do fi for pkgname in "${pkgnames[@]}"; do + pkgver=$(. "${xbsrepo_from}/PKGBUILD"; get_full_version "${pkgname}") + if [ -z "${pkgver}" ]; then + die "Could not read pkgver" + fi for tarch in "${tarches[@]}"; do getpkgfile "${ftppath_from}/${tarch}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT} >/dev/null done @@ -72,11 +71,11 @@ for pkgbase in "${args[@]:2}"; do else tarches=("${pkgarch}") fi - msg2 '%s (%s)' "${pkgbase}" "${tarches[*]}" + msg2 "%s (%s)" "${pkgbase}" "${tarches[*]}" pkgnames=($(. "${xbsrepo_to}/PKGBUILD"; echo "${pkgname[@]}")) - pkgver=$(. "${xbsrepo_to}/PKGBUILD"; get_full_version "${epoch:-0}" "${pkgver}" "${pkgrel}") for pkgname in "${pkgnames[@]}"; do + pkgver=$(. "${xbsrepo_to}/PKGBUILD"; get_full_version "${pkgname}") for tarch in "${tarches[@]}"; do pkgpath=$(getpkgfile "${ftppath_from}/${tarch}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT}) pkgfile="${pkgpath##*/}" @@ -43,14 +43,28 @@ for repo in "${repos[@]}"; do if ! check_pkgfile "${pkg}"; then die "Package %s is not consistent with its meta data" "${repo}/${pkg##*/}" fi + if "${REQUIRE_SIGNATURE}" && ! pacman-key -v "${pkg}.sig" >/dev/null 2>&1; then + die "Package %s does not have a valid signature" "${repo}/${pkg##*/}" + fi + if ! check_pkgxbs "${pkg}" "${repo}"; then + die "Package %s is not consistent with %s" "${repo}/${pkg##*/}" "$(xbs name)" + fi if ! check_pkgrepos "${pkg}"; then die "Package %s already exists in another repository" "${repo}/${pkg##*/}" fi + if ! check_packager "${pkg}"; then + die "Package ${repo}/${pkg##*/} does not have a valid packager" + fi + if ! check_buildinfo "${pkg}"; then + die "Package ${repo}/${pkg##*/} does not have a .BUILDINFO file" + fi + if ! check_builddir "${pkg}"; then + die "Package ${repo}/${pkg##*/} was not built in a chroot" + fi done - # This is fucking obnoxious - #if ! check_splitpkgs ${repo} "${pkgs[@]}"; then - # die "Missing split packages for %s" "${repo}" - #fi + if ! check_splitpkgs "${repo}" "${pkgs[@]}"; then + die "Missing split packages for %s" "${repo}" + fi else die "Could not read %s" "${STAGING}" fi diff --git a/test/lib/common.inc b/test/lib/common.inc index bef8749..1831602 100644 --- a/test/lib/common.inc +++ b/test/lib/common.inc @@ -46,21 +46,19 @@ oneTimeSetUp() { if ! "${build}"; then if [ "${pkgarch[0]}" == 'any' ]; then - sudo extra-x86_64-build || die 'extra-x86_64-build failed' + sudo libremakepkg || die 'libremakepkg failed' else for a in "${pkgarch[@]}"; do if in_array "$a" "${ARCH_BUILD[@]}"; then - sudo "extra-${a}-build" || die "extra-${a}-build failed" + sudo setarch "$a" libremakepkg -n "$a" || die "setarch ${a} libremakepkg -n ${a} failed" + for p in "${pkgname[@]}"; do + cp "${p}-${pkgversion}-${a}"${PKGEXT} "$(dirname "${BASH_SOURCE[0]})/../packages/${d##*/}")" + done + else + warning "skipping arch %s" "$a" fi done fi - for a in "${pkgarch[@]}"; do - if in_array "$a" "${ARCH_BUILD[@]}"; then - for p in "${pkgname[@]}"; do - cp "${p}-${pkgversion}-${a}"${PKGEXT} "$(dirname "${BASH_SOURCE[0]}")/../packages/${d##*/}" - done - fi - done fi popd >/dev/null done @@ -82,6 +80,7 @@ setUp() { PKGREPOS=('core' 'extra' 'testing') PKGPOOL='pool/packages' + SRCPOOL='pool/sources' mkdir -p "${TMP}/"{ftp,tmp,staging,{package,source}-cleanup,svn-packages-{copy,repo}} for r in "${PKGREPOS[@]}"; do @@ -105,11 +104,21 @@ setUp() { arch_svn commit -q -m"initial commit of ${pkg}" "${TMP}/svn-packages-copy" done + mkdir -p "${TMP}/home/.config/libretools" + export XDG_CONFIG_HOME="${TMP}/home/.config" + printf '%s\n' \ + 'SVNURL=foo' \ + "SVNREPO=\"${TMP}/svn-packages-copy\"" \ + "ARCHES=($(printf '%q ' "${BUILD_ARCHES[@]}"))" \ + > "$XDG_CONFIG_HOME/libretools/xbs-abs.conf" + printf '%s\n' 'BUILDSYSTEM=abs' > "$XDG_CONFIG_HOME/xbs.conf" + cat <<eot > "$(dirname "${BASH_SOURCE[0]}")/../../config.local" FTP_BASE="${TMP}/ftp" SVNREPO="file://${TMP}/svn-packages-repo" PKGREPOS=("${PKGREPOS[@]}") PKGPOOL="${PKGPOOL}" + SRCPOOL="${SRCPOOL}" TESTING_REPO='testing' STABLE_REPOS=('core' 'extra') CLEANUP_DESTDIR="${TMP}/package-cleanup" @@ -139,7 +148,7 @@ releasePackage() { local pkgname pushd "${TMP}/svn-packages-copy/${pkgbase}/trunk/" >/dev/null - archrelease "${repo}-${arch}" >/dev/null 2>&1 + xbs release "${repo}" "${arch}" >/dev/null 2>&1 pkgver=$(. PKGBUILD; get_full_version) pkgname=($(. PKGBUILD; echo "${pkgname[@]}")) popd >/dev/null @@ -167,12 +176,12 @@ checkAnyPackageDB() { for arch in "${ARCH_BUILD[@]}"; do [ -L "${FTP_BASE}/${repo}/os/${arch}/${pkg}" ] || fail "${repo}/os/${arch}/${pkg} is not a symlink" - [ "$(readlink -e "${FTP_BASE}/${repo}/os/${arch}/${pkg}")" == "${FTP_BASE}/${PKGPOOL}/${pkg}" ] \ + [ "$(readlink -e "${FTP_BASE}/${repo}/os/${arch}/${pkg}")" == "$(readlink -e "${FTP_BASE}/${PKGPOOL}/${pkg}")" ] \ || fail "${repo}/os/${arch}/${pkg} does not link to ${PKGPOOL}/${pkg}" if "${REQUIRE_SIGNATURE}"; then [ -L "${FTP_BASE}/${repo}/os/${arch}/${pkg}.sig" ] || fail "${repo}/os/${arch}/${pkg}.sig is not a symlink" - [ "$(readlink -e "${FTP_BASE}/${repo}/os/${arch}/${pkg}.sig")" == "${FTP_BASE}/${PKGPOOL}/${pkg}.sig" ] \ + [ "$(readlink -e "${FTP_BASE}/${repo}/os/${arch}/${pkg}.sig")" == "$(readlink -e "${FTP_BASE}/${PKGPOOL}/${pkg}.sig")" ] \ || fail "${repo}/os/${arch}/${pkg}.sig does not link to ${PKGPOOL}/${pkg}.sig" fi @@ -208,7 +217,7 @@ checkPackageDB() { [ -L "${FTP_BASE}/${repo}/os/${arch}/${pkg}" ] || fail "${repo}/os/${arch}/${pkg} not a symlink" [ -r "${STAGING}/${repo}/${pkg}" ] && fail "${repo}/${pkg} found in staging dir" - [ "$(readlink -e "${FTP_BASE}/${repo}/os/${arch}/${pkg}")" == "${FTP_BASE}/${PKGPOOL}/${pkg}" ] \ + [ "$(readlink -e "${FTP_BASE}/${repo}/os/${arch}/${pkg}")" == "$(readlink -e "${FTP_BASE}/${PKGPOOL}/${pkg}")" ] \ || fail "${repo}/os/${arch}/${pkg} does not link to ${PKGPOOL}/${pkg}" if "${REQUIRE_SIGNATURE}"; then @@ -216,7 +225,7 @@ checkPackageDB() { [ -L "${FTP_BASE}/${repo}/os/${arch}/${pkg}.sig" ] || fail "${repo}/os/${arch}/${pkg}.sig is not a symlink" [ -r "${STAGING}/${repo}/${pkg}.sig" ] && fail "${repo}/${pkg}.sig found in staging dir" - [ "$(readlink -e "${FTP_BASE}/${repo}/os/${arch}/${pkg}.sig")" == "${FTP_BASE}/${PKGPOOL}/${pkg}.sig" ] \ + [ "$(readlink -e "${FTP_BASE}/${repo}/os/${arch}/${pkg}.sig")" == "$(readlink -e "${FTP_BASE}/${PKGPOOL}/${pkg}.sig")" ] \ || fail "${repo}/os/${arch}/${pkg}.sig does not link to ${PKGPOOL}/${pkg}.sig" fi diff --git a/test/test.d/create-filelists.sh b/test/test.d/create-filelists.sh index 20dafc6..837c432 100755 --- a/test/test.d/create-filelists.sh +++ b/test/test.d/create-filelists.sh @@ -59,7 +59,7 @@ testCreateSplitFileLists() { ../db-update for pkgbase in "${pkgs[@]}"; do - pkgnames=($(source "${TMP}/svn-packages-copy/${pkgbase}/trunk/PKGBUILD"; echo ${pkgname[@]})) + pkgnames=($(source "${TMP}/svn-packages-copy/${pkgbase}/trunk/PKGBUILD"; echo "${pkgname[@]}")) for pkgname in "${pkgnames[@]}"; do for arch in "${ARCH_BUILD[@]}"; do if ! bsdtar -xOf "${FTP_BASE}/extra/os/${arch}/extra${FILESEXT}" | grep "usr/bin/${pkgname}" &>/dev/null; then diff --git a/test/test.d/db-update.sh b/test/test.d/db-update.sh index 540eccf..5d3c833 100755 --- a/test/test.d/db-update.sh +++ b/test/test.d/db-update.sh @@ -80,7 +80,7 @@ testUpdateAnyPackage() { pushd "${TMP}/svn-packages-copy/pkg-any-a/trunk/" >/dev/null sed 's/pkgrel=1/pkgrel=2/g' -i PKGBUILD arch_svn commit -q -m"update pkg to pkgrel=2" >/dev/null - sudo extra-i686-build + sudo libremakepkg mv pkg-any-a-1-2-any.pkg.tar.xz "${pkgdir}/pkg-any-a/" popd >/dev/null @@ -98,7 +98,7 @@ testUpdateAnyPackageToDifferentRepositoriesAtOnce() { pushd "${TMP}/svn-packages-copy/pkg-any-a/trunk/" >/dev/null sed 's/pkgrel=1/pkgrel=2/g' -i PKGBUILD arch_svn commit -q -m"update pkg to pkgrel=2" >/dev/null - sudo extra-i686-build + sudo libremakepkg mv pkg-any-a-1-2-any.pkg.tar.xz "${pkgdir}/pkg-any-a/" popd >/dev/null @@ -130,7 +130,7 @@ testUpdateSameAnyPackageToDifferentRepositories() { ../db-update >/dev/null 2>&1 && (fail 'Adding an existing package to another repository should fail'; return 1) local arch - for arch in i686 x86_64; do + for arch in "${ARCH_BUILD[@]}"; do ( [ -r "${FTP_BASE}/testing/os/${arch}/testing${DBEXT%.tar.*}" ] \ && bsdtar -xf "${FTP_BASE}/testing/os/${arch}/testing${DBEXT%.tar.*}" -O | grep "${pkgbase}" &>/dev/null) \ && fail "${pkgbase} should not be in testing/os/${arch}/testing${DBEXT%.tar.*}" diff --git a/test/test.d/testing2x.sh b/test/test.d/testing2x.sh index c611ce4..8232490 100755 --- a/test/test.d/testing2x.sh +++ b/test/test.d/testing2x.sh @@ -10,7 +10,7 @@ testTesting2xAnyPackage() { pushd "${TMP}/svn-packages-copy/pkg-any-a/trunk/" >/dev/null sed 's/pkgrel=1/pkgrel=2/g' -i PKGBUILD arch_svn commit -q -m"update pkg to pkgrel=2" >/dev/null - sudo extra-i686-build + sudo libremakepkg mv pkg-any-a-1-2-any.pkg.tar.xz "${pkgdir}/pkg-any-a/" popd >/dev/null |