summaryrefslogtreecommitdiff
path: root/extra/xbs-lukeshu
diff options
context:
space:
mode:
Diffstat (limited to 'extra/xbs-lukeshu')
-rw-r--r--extra/xbs-lukeshu/config48
-rw-r--r--extra/xbs-lukeshu/config.local.import-community5
-rw-r--r--extra/xbs-lukeshu/config.local.import-packages5
-rw-r--r--extra/xbs-lukeshu/config.local.parabola14
-rw-r--r--extra/xbs-lukeshu/config.local.svn-community11
-rw-r--r--extra/xbs-lukeshu/config.local.svn-packages11
-rwxr-xr-xextra/xbs-lukeshu/cron-jobs/check_archlinux/check_packages.py508
-rwxr-xr-xextra/xbs-lukeshu/cron-jobs/check_archlinux/parse_pkgbuilds.sh153
-rwxr-xr-xextra/xbs-lukeshu/cron-jobs/devlist-mailer27
-rwxr-xr-xextra/xbs-lukeshu/cron-jobs/ftpdir-cleanup96
-rwxr-xr-xextra/xbs-lukeshu/cron-jobs/integrity-check32
-rwxr-xr-xextra/xbs-lukeshu/cron-jobs/make_repo_torrents70
-rwxr-xr-xextra/xbs-lukeshu/cron-jobs/sourceballs150
-rw-r--r--extra/xbs-lukeshu/cron-jobs/sourceballs.force4
-rw-r--r--extra/xbs-lukeshu/cron-jobs/sourceballs.skip43
-rw-r--r--extra/xbs-lukeshu/db-functions456
-rwxr-xr-xextra/xbs-lukeshu/db-import288
-rw-r--r--extra/xbs-lukeshu/db-import.conf23
-rwxr-xr-xextra/xbs-lukeshu/db-init6
-rwxr-xr-xextra/xbs-lukeshu/db-list-nonfree.py28
-rwxr-xr-xextra/xbs-lukeshu/db-list-unsigned-packages38
-rwxr-xr-xextra/xbs-lukeshu/db-list-unsigned-packages.py96
-rwxr-xr-xextra/xbs-lukeshu/db-move105
-rwxr-xr-xextra/xbs-lukeshu/db-pick-mirror24
-rwxr-xr-xextra/xbs-lukeshu/db-remove49
-rwxr-xr-xextra/xbs-lukeshu/db-repo-add41
-rwxr-xr-xextra/xbs-lukeshu/db-repo-remove37
-rwxr-xr-xextra/xbs-lukeshu/db-update126
-rwxr-xr-xextra/xbs-lukeshu/make_individual_torrent52
-rw-r--r--extra/xbs-lukeshu/test/__init__.py0
-rw-r--r--extra/xbs-lukeshu/test/blacklist_sample2
-rw-r--r--extra/xbs-lukeshu/test/core.db.tar.gzbin0 -> 1345 bytes
-rw-r--r--extra/xbs-lukeshu/test/depends4
-rw-r--r--extra/xbs-lukeshu/test/desc39
-rw-r--r--extra/xbs-lukeshu/test/lib/common.inc321
-rwxr-xr-xextra/xbs-lukeshu/test/lib/shunit21048
-rw-r--r--extra/xbs-lukeshu/test/packages/pkg-any-a/PKGBUILD12
-rw-r--r--extra/xbs-lukeshu/test/packages/pkg-any-b/PKGBUILD12
l---------extra/xbs-lukeshu/test/packages/pkg-simple-a/Makefile1
-rw-r--r--extra/xbs-lukeshu/test/packages/pkg-simple-a/PKGBUILD22
l---------extra/xbs-lukeshu/test/packages/pkg-simple-a/test.c1
l---------extra/xbs-lukeshu/test/packages/pkg-simple-b/Makefile1
-rw-r--r--extra/xbs-lukeshu/test/packages/pkg-simple-b/PKGBUILD22
l---------extra/xbs-lukeshu/test/packages/pkg-simple-b/test.c1
l---------extra/xbs-lukeshu/test/packages/pkg-simple-epoch/Makefile1
-rw-r--r--extra/xbs-lukeshu/test/packages/pkg-simple-epoch/PKGBUILD23
l---------extra/xbs-lukeshu/test/packages/pkg-simple-epoch/test.c1
l---------extra/xbs-lukeshu/test/packages/pkg-split-a/Makefile1
-rw-r--r--extra/xbs-lukeshu/test/packages/pkg-split-a/PKGBUILD28
l---------extra/xbs-lukeshu/test/packages/pkg-split-a/test.c1
l---------extra/xbs-lukeshu/test/packages/pkg-split-b/Makefile1
-rw-r--r--extra/xbs-lukeshu/test/packages/pkg-split-b/PKGBUILD29
l---------extra/xbs-lukeshu/test/packages/pkg-split-b/test.c1
-rw-r--r--extra/xbs-lukeshu/test/rsync_output_sample14
-rwxr-xr-xextra/xbs-lukeshu/test/runTest15
-rw-r--r--extra/xbs-lukeshu/test/src/Makefile5
-rw-r--r--extra/xbs-lukeshu/test/src/test.c7
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/create-filelists.sh105
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/db-move.sh122
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/db-remove.sh77
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/db-repo-add.sh54
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/db-repo-remove.sh58
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/db-update.sh175
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/ftpdir-cleanup.sh121
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/packages.sh11
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/signed-packages.sh36
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/sourceballs.sh84
-rwxr-xr-xextra/xbs-lukeshu/test/test.d/testing2x.sh27
-rw-r--r--extra/xbs-lukeshu/test/test_filter.py196
69 files changed, 5225 insertions, 0 deletions
diff --git a/extra/xbs-lukeshu/config b/extra/xbs-lukeshu/config
new file mode 100644
index 0000000..bd05ac8
--- /dev/null
+++ b/extra/xbs-lukeshu/config
@@ -0,0 +1,48 @@
+#!/hint/bash
+
+case "$USER" in
+ db-import-packages) _name=import-packages;;
+ db-import-community) _name=import-community;;
+ *) _name=parabola;;
+esac
+
+FTP_BASE="/srv/repo/main"
+PKGREPOS=()
+PKGPOOL=''
+SRCPOOL=''
+
+CLEANUP_DESTDIR="/srv/repo/private/${_name}/package-cleanup"
+CLEANUP_DRYRUN=false
+# Time in days to keep moved packages
+CLEANUP_KEEP=30
+
+SOURCE_CLEANUP_DESTDIR="/srv/repo/private/${_name}/source-cleanup"
+SOURCE_CLEANUP_DRYRUN=false
+# Time in days to keep moved sourcepackages
+SOURCE_CLEANUP_KEEP=14
+
+REQUIRE_SIGNATURE=true
+
+LOCK_DELAY=10
+LOCK_TIMEOUT=300
+
+[ -n "${STAGING:-}" ] || STAGING="$HOME/staging/unknown/staging"
+export TMPDIR="${TMPDIR:-/tmp}"
+ARCHES=(i686 x86_64)
+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/extra/xbs-lukeshu/config.local.import-community b/extra/xbs-lukeshu/config.local.import-community
new file mode 100644
index 0000000..393fd57
--- /dev/null
+++ b/extra/xbs-lukeshu/config.local.import-community
@@ -0,0 +1,5 @@
+#!/hint/bash
+
+PKGREPOS=({community,multilib}{,-testing,-staging})
+PKGPOOL='pool/community'
+SRCPOOL='sources/community'
diff --git a/extra/xbs-lukeshu/config.local.import-packages b/extra/xbs-lukeshu/config.local.import-packages
new file mode 100644
index 0000000..c699b28
--- /dev/null
+++ b/extra/xbs-lukeshu/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/extra/xbs-lukeshu/config.local.parabola b/extra/xbs-lukeshu/config.local.parabola
new file mode 100644
index 0000000..648dfbb
--- /dev/null
+++ b/extra/xbs-lukeshu/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/extra/xbs-lukeshu/config.local.svn-community b/extra/xbs-lukeshu/config.local.svn-community
new file mode 100644
index 0000000..105ea66
--- /dev/null
+++ b/extra/xbs-lukeshu/config.local.svn-community
@@ -0,0 +1,11 @@
+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/extra/xbs-lukeshu/config.local.svn-packages b/extra/xbs-lukeshu/config.local.svn-packages
new file mode 100644
index 0000000..958a483
--- /dev/null
+++ b/extra/xbs-lukeshu/config.local.svn-packages
@@ -0,0 +1,11 @@
+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/extra/xbs-lukeshu/cron-jobs/check_archlinux/check_packages.py b/extra/xbs-lukeshu/cron-jobs/check_archlinux/check_packages.py
new file mode 100755
index 0000000..ac0194f
--- /dev/null
+++ b/extra/xbs-lukeshu/cron-jobs/check_archlinux/check_packages.py
@@ -0,0 +1,508 @@
+#!/usr/bin/env python2
+#
+# check_archlinux.py
+#
+# Original script by Scott Horowitz <stonecrest@gmail.com>
+# Rewritten by Xavier Chantry <shiningxc@gmail.com>
+#
+# This script currently checks for a number of issues in your ABS tree:
+# 1. Directories with missing PKGBUILDS
+# 2. Invalid PKGBUILDs (bash syntax error for instance)
+# 3. PKGBUILD names that don't match their directory
+# 4. Duplicate PKGBUILDs
+# 5. Valid arch's in PKGBUILDS
+# 6. Missing (make-)dependencies
+# 7. Hierarchy of repos (e.g., that a core package doesn't depend on
+# a non-core package)
+# 8. Circular dependencies
+
+import os,re,commands,getopt,sys,tarfile
+import pdb
+
+import ctypes
+_alpm = ctypes.cdll.LoadLibrary("libalpm.so")
+
+DBEXT='.db.tar.gz'
+
+packages = {} # pkgname : PacmanPackage
+repopkgs = {} # pkgname : PacmanPackage
+provisions = {} # provision : PacmanPackage
+pkgdeps,makepkgdeps = {},{} # PacmanPackage : list of the PacmanPackage dependencies
+invalid_pkgbuilds = []
+missing_pkgbuilds = []
+dups = []
+
+dbonly = []
+absonly = []
+
+mismatches = []
+missing_deps = []
+missing_makedeps = []
+invalid_archs = []
+dep_hierarchy = []
+makedep_hierarchy = []
+circular_deps = [] # pkgname>dep1>dep2>...>pkgname
+checked_deps = []
+
+class PacmanPackage:
+ def __init__(self):
+ self.name,self.version = "",""
+ self.base = ""
+ self.path,self.repo = "",""
+ self.deps,self.makedeps = [],[]
+ self.provides,self.conflicts = [],[]
+ self.archs = []
+
+class Depend:
+ def __init__(self,name,version,mod):
+ self.name = name
+ self.version = version
+ self.mod = mod
+
+def parse_pkgbuilds(repos,arch):
+ for absroot in absroots:
+ for repo in repos:
+ cmd = os.path.dirname(os.path.realpath(sys.argv[0])) + '/parse_pkgbuilds.sh '
+ cmd += arch + ' ' + absroot + '/' + repo
+ (status,output) = commands.getstatusoutput(cmd)
+ if status != 0:
+ print "Error : failed to run '%s'" % cmd
+ sys.exit()
+ parse_data(repo,output)
+
+def parse_data(repo,data):
+ attrname = None
+
+ for line in data.split('\n'):
+ if line.startswith('%'):
+ attrname = line.strip('%').lower()
+ elif line.strip() == '':
+ attrname = None
+ elif attrname == "invalid":
+ if repo in repos:
+ invalid_pkgbuilds.append(line)
+ elif attrname == "missing":
+ if repo in repos:
+ missing_pkgbuilds.append(line)
+ elif attrname == "name":
+ pkg = PacmanPackage()
+ pkg.name = line
+ pkg.repo = repo
+ dup = None
+ if pkg.name in packages:
+ dup = packages[pkg.name]
+ else:
+ packages[pkg.name] = pkg
+ elif attrname == "base":
+ pkg.base = line
+ elif attrname == "version":
+ pkg.version = line
+ elif attrname == "path":
+ pkg.path = line
+ if dup != None and (pkg.repo in repos or dup.repo in repos):
+ dups.append(pkg.path + " vs. " + dup.path)
+ elif attrname == "arch":
+ pkg.archs.append(line)
+ elif attrname == "depends":
+ pkg.deps.append(line)
+ elif attrname == "makedepends":
+ pkg.makedeps.append(line)
+ elif attrname == "conflicts":
+ pkg.conflicts.append(line)
+ elif attrname == "provides":
+ pkg.provides.append(line)
+
+def parse_dbs(repos,arch):
+ dbpkgs = {}
+ for repo in repos:
+ pkgs = set([])
+ db = tarfile.open(os.path.join(repodir,repo,'os',arch,repo + DBEXT))
+ for line in db.getnames():
+ if not '/' in line:
+ pkgs.add(line.rsplit('-',2)[0])
+ dbpkgs[repo] = pkgs
+ return(dbpkgs)
+
+def splitdep(dep):
+ name = dep
+ version = ""
+ mod = ""
+ for char in (">=", "<=", "=", ">", "<"):
+ pos = dep.find(char)
+ if pos > -1:
+ name = dep[:pos]
+ version = dep[pos:].replace(char, "")
+ mod = char
+ break
+ return Depend(name,version,mod)
+
+def splitprov(prov):
+ name = prov
+ version = ""
+ pos = prov.find("=")
+ if pos > -1:
+ name = prov[:pos]
+ version = prov[pos:].replace("=", "")
+ return (name,version)
+
+def vercmp(v1,mod,v2):
+ """
+ >>> vercmp("1.0", "<=", "2.0")
+ True
+ >>> vercmp("1:1.0", ">", "2.0")
+ True
+ >>> vercmp("1.0.2", ">=", "2.1.0")
+ False
+ """
+ s1 = ctypes.c_char_p(v1)
+ s2 = ctypes.c_char_p(v2)
+ res = _alpm.alpm_pkg_vercmp(s1,s2)
+ if res == 0:
+ return (mod.find("=") > -1)
+ elif res < 0:
+ return (mod.find("<") > -1)
+ elif res > 0:
+ return (mod.find(">") > -1)
+ return False
+
+
+def depcmp(name,version,dep):
+ if name != dep.name:
+ return False
+ if dep.version == "" or dep.mod == "":
+ return True
+ if version == "":
+ return False
+ return vercmp(version,dep.mod,dep.version)
+
+def provcmp(pkg,dep):
+ for prov in pkg.provides:
+ (provname,provver) = splitprov(prov)
+ if depcmp(provname,provver,dep):
+ return True
+ return False
+
+def verify_dep(dep):
+ dep = splitdep(dep)
+ if dep.name in packages:
+ pkg = packages[dep.name]
+ if depcmp(pkg.name,pkg.version,dep):
+ return [pkg]
+ if dep.name in provisions:
+ provlist = provisions[dep.name]
+ results = []
+ for prov in provlist:
+ if provcmp(prov,dep):
+ results.append(prov)
+ return results
+ return []
+
+def verify_deps(name,repo,deps):
+ pkg_deps = []
+ missdeps = []
+ hierarchy = []
+ for dep in deps:
+ pkglist = verify_dep(dep)
+ if pkglist == []:
+ missdeps.append(repo + "/" + name + " --> '" + dep + "'")
+ else:
+ valid_repos = get_repo_hierarchy(repo)
+ pkgdep = None
+ for pkg in pkglist:
+ if pkg.repo in valid_repos:
+ pkgdep = pkg
+ break
+ if not pkgdep:
+ pkgdep = pkglist[0]
+ hierarchy.append((repo,name,pkgdep))
+
+ pkg_deps.append(pkgdep)
+
+ return (pkg_deps,missdeps,hierarchy)
+
+def compute_deplist(pkg):
+ list = []
+ stack = [pkg]
+ while stack != []:
+ dep = stack.pop()
+ if dep in pkgdeps:
+ for dep2 in pkgdeps[dep]:
+ if dep2 not in list:
+ list.append(dep2)
+ stack.append(dep2)
+ if dep in makepkgdeps:
+ for dep2 in makepkgdeps[dep]:
+ if dep2 not in list:
+ list.append(dep2)
+ stack.append(dep2)
+ return list
+
+def check_hierarchy(deph):
+ hierarchy = []
+ for (repo,name,pkgdep) in deph:
+ deplist = compute_deplist(pkgdep)
+ valid_repos = get_repo_hierarchy(repo)
+ extdeps = []
+ for dep in deplist:
+ if dep.repo not in valid_repos:
+ extdeps.append(dep.name)
+ string = repo + "/" + name + " depends on " + pkgdep.repo + "/" + pkgdep.name + " ("
+ string += "%s extra (make)deps to pull" % len(extdeps)
+ if 0 < len(extdeps) < 10:
+ string += " : " + ' '.join(extdeps)
+ string += ")"
+ hierarchy.append(string)
+ return hierarchy
+
+def get_repo_hierarchy(repo):
+ repo_hierarchy = {'core': ['core'], \
+ 'extra': ['core', 'extra'], \
+ 'community': ['core', 'extra', 'community'], \
+ 'multilib': ['core', 'extra', 'community', 'multilib'] }
+ if repo in repo_hierarchy:
+ return repo_hierarchy[repo]
+ else:
+ return ['core','extra','community']
+
+def verify_archs(name,repo,archs):
+ valid_archs = ['any', 'i686', 'x86_64']
+ invalid_archs = []
+ for arch in archs:
+ if arch not in valid_archs:
+ invalid_archs.append(repo + "/" + name + " --> " + arch)
+ return invalid_archs
+
+def find_scc(packages):
+ # reset all variables
+ global index,S,pkgindex,pkglowlink
+ index = 0
+ S = []
+ pkgindex = {}
+ pkglowlink = {}
+ cycles = []
+ for pkg in packages:
+ tarjan(pkg)
+
+def tarjan(pkg):
+ global index,S,pkgindex,pkglowlink,cycles
+ pkgindex[pkg] = index
+ pkglowlink[pkg] = index
+ index += 1
+ checked_deps.append(pkg)
+ S.append(pkg)
+ deps = []
+ if pkg in pkgdeps:
+ deps = pkgdeps[pkg]
+ for dep in deps:
+ if dep not in pkgindex:
+ tarjan(dep)
+ pkglowlink[pkg] = min(pkglowlink[pkg],pkglowlink[dep])
+ elif dep in S:
+ pkglowlink[pkg] = min(pkglowlink[pkg],pkgindex[dep])
+ if pkglowlink[pkg] == pkgindex[pkg]:
+ dep = S.pop()
+ if pkg == dep:
+ return
+ path = pkg.name
+ while pkg != dep:
+ path = dep.repo + "/" + dep.name + ">" + path
+ dep = S.pop()
+ path = dep.name + ">" + path
+ if pkg.repo in repos:
+ circular_deps.append(path)
+
+def print_heading(heading):
+ print ""
+ print "=" * (len(heading) + 4)
+ print "= " + heading + " ="
+ print "=" * (len(heading) + 4)
+
+def print_subheading(subheading):
+ print ""
+ print subheading
+ print "-" * (len(subheading) + 2)
+
+def print_missdeps(pkgname,missdeps) :
+ for d in missdeps:
+ print pkgname + " : " + d
+
+def print_result(list, subheading):
+ if len(list) > 0:
+ list.sort()
+ print_subheading(subheading)
+ for item in list:
+ print item
+
+def print_results():
+ print_result(missing_pkgbuilds, "Missing PKGBUILDs")
+ print_result(invalid_pkgbuilds, "Invalid PKGBUILDs")
+ print_result(mismatches, "Mismatched Pkgnames")
+ print_result(dups, "Duplicate PKGBUILDs")
+ print_result(invalid_archs, "Invalid Archs")
+ print_result(missing_deps, "Missing Dependencies")
+ print_result(missing_makedeps, "Missing Makedepends")
+ print_result(dep_hierarchy, "Repo Hierarchy for Dependencies")
+ print_result(makedep_hierarchy, "Repo Hierarchy for Makedepends")
+ print_result(circular_deps, "Circular Dependencies")
+ print_result(dbonly, "Packages found in db, but not in tree")
+ print_result(absonly,"Packages found in tree, but not in db")
+ print_subheading("Summary")
+ print "Missing PKGBUILDs: ", len(missing_pkgbuilds)
+ print "Invalid PKGBUILDs: ", len(invalid_pkgbuilds)
+ print "Mismatching PKGBUILD names: ", len(mismatches)
+ print "Duplicate PKGBUILDs: ", len(dups)
+ print "Invalid archs: ", len(invalid_archs)
+ print "Missing (make)dependencies: ", len(missing_deps)+len(missing_makedeps)
+ print "Repo hierarchy problems: ", len(dep_hierarchy)+len(makedep_hierarchy)
+ print "Circular dependencies: ", len(circular_deps)
+ print "In db, but not in tree: ", len(dbonly)
+ print "In tree, but not in db: ", len(absonly)
+ print ""
+
+def print_usage():
+ print ""
+ print "Usage: ./check_packages.py [OPTION]"
+ print ""
+ print "Options:"
+ print " --abs-tree=<path[,path]> Check the specified tree(s) (default : /var/abs)"
+ print " --repos=<r1,r2,...> Check the specified repos (default : core,extra)"
+ print " --arch=<i686|x86_64> Check the specified arch (default : i686)"
+ print " --repo-dir=<path> Check the dbs at the specified path (default : /srv/ftp)"
+ print " -h, --help Show this help and exit"
+ print ""
+ print "Examples:"
+ print "\n Check core and extra in existing abs tree:"
+ print " ./check_packages.py --abs-tree=/var/abs --repos=core,extra --arch=i686"
+ print "\n Check community:"
+ print " ./check_packages.py --abs-tree=/var/abs --repos=community --arch=i686"
+ print ""
+
+if __name__ == "__main__":
+ ## Default path to the abs root directory
+ absroots = ["/var/abs"]
+ ## Default list of repos to check
+ repos = ['core', 'extra']
+ ## Default arch
+ arch = "i686"
+ ## Default repodir
+ repodir = "/srv/ftp"
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "", ["abs-tree=", "repos=",
+ "arch=", "repo-dir="])
+ except getopt.GetoptError:
+ print_usage()
+ sys.exit()
+ if opts != []:
+ for o, a in opts:
+ if o in ("--abs-tree"):
+ absroots = a.split(',')
+ elif o in ("--repos"):
+ repos = a.split(",")
+ elif o in ("--arch"):
+ arch = a
+ elif o in ("--repo-dir"):
+ repodir = a
+ else:
+ print_usage()
+ sys.exit()
+ if args != []:
+ print_usage()
+ sys.exit()
+
+ for absroot in absroots:
+ if not os.path.isdir(absroot):
+ print "Error : the abs tree " + absroot + " does not exist"
+ sys.exit()
+ for repo in repos:
+ repopath = absroot + "/" + repo
+ if not os.path.isdir(repopath):
+ print("Warning : the repository " + repo + " does not exist in " + absroot)
+
+ if not os.path.isdir(repodir):
+ print "Error: the repository directory %s does not exist" % repodir
+ sys.exit()
+ for repo in repos:
+ path = os.path.join(repodir,repo,'os',arch,repo + DBEXT)
+ if not os.path.isfile(path):
+ print "Error : repo DB %s : File not found" % path
+ sys.exit()
+ if not tarfile.is_tarfile(path):
+ print "Error : Cant open repo DB %s, not a valid tar file" % path
+ sys.exit()
+ # repos which need to be loaded
+ loadrepos = set([])
+ for repo in repos:
+ loadrepos = loadrepos | set(get_repo_hierarchy(repo))
+
+ print_heading("Integrity Check " + arch + " of " + ",".join(repos))
+ print("\nPerforming integrity checks...")
+
+ print("==> parsing pkgbuilds")
+ parse_pkgbuilds(loadrepos,arch)
+
+ # fill provisions
+ for name,pkg in packages.iteritems():
+ for prov in pkg.provides:
+ provname=prov.split("=")[0]
+ if provname not in provisions:
+ provisions[provname] = []
+ provisions[provname].append(pkg)
+
+ # fill repopkgs
+ for name,pkg in packages.iteritems():
+ if pkg.repo in repos:
+ repopkgs[name] = pkg
+
+ print("==> parsing db files")
+ dbpkgs = parse_dbs(repos,arch)
+
+ print("==> checking mismatches")
+ for name,pkg in repopkgs.iteritems():
+ pkgdirname = pkg.path.split("/")[-1]
+ if name != pkgdirname and pkg.base != pkgdirname:
+ mismatches.append(name + " vs. " + pkg.path)
+
+ print("==> checking archs")
+ for name,pkg in repopkgs.iteritems():
+ archs = verify_archs(name,pkg.repo,pkg.archs)
+ invalid_archs.extend(archs)
+
+ deph,makedeph = [],[]
+
+ print("==> checking dependencies")
+ for name,pkg in repopkgs.iteritems():
+ (deps,missdeps,hierarchy) = verify_deps(name,pkg.repo,pkg.deps)
+ pkgdeps[pkg] = deps
+ missing_deps.extend(missdeps)
+ deph.extend(hierarchy)
+
+ print("==> checking makedepends")
+ for name,pkg in repopkgs.iteritems():
+ (makedeps,missdeps,hierarchy) = verify_deps(name,pkg.repo,pkg.makedeps)
+ makepkgdeps[pkg] = makedeps
+ missing_makedeps.extend(missdeps)
+ makedeph.extend(hierarchy)
+
+ print("==> checking hierarchy")
+ dep_hierarchy = check_hierarchy(deph)
+ makedep_hierarchy = check_hierarchy(makedeph)
+
+ print("==> checking for circular dependencies")
+ # make sure pkgdeps is filled for every package
+ for name,pkg in packages.iteritems():
+ if pkg not in pkgdeps:
+ (deps,missdeps,_) = verify_deps(name,pkg.repo,pkg.deps)
+ pkgdeps[pkg] = deps
+ find_scc(repopkgs.values())
+
+ print("==> checking for differences between db files and pkgbuilds")
+ for repo in repos:
+ for pkg in dbpkgs[repo]:
+ if not (pkg in repopkgs and repopkgs[pkg].repo == repo):
+ dbonly.append("%s/%s" % (repo,pkg))
+ for name,pkg in repopkgs.iteritems():
+ if not name in dbpkgs[pkg.repo]:
+ absonly.append("%s/%s" % (pkg.repo,name))
+
+ print_results()
diff --git a/extra/xbs-lukeshu/cron-jobs/check_archlinux/parse_pkgbuilds.sh b/extra/xbs-lukeshu/cron-jobs/check_archlinux/parse_pkgbuilds.sh
new file mode 100755
index 0000000..b857ac8
--- /dev/null
+++ b/extra/xbs-lukeshu/cron-jobs/check_archlinux/parse_pkgbuilds.sh
@@ -0,0 +1,153 @@
+#!/bin/bash
+
+# Usage : parse_pkgbuilds.sh arch <pkgbuilds_dir1,dir2,...>
+# Example : parse_pkgbuilds.sh i686 /var/abs/core /var/abs/extra
+
+exit() { return; }
+
+splitpkg_overrides=('depends' 'optdepends' 'provides' 'conflicts')
+variables=('pkgname' 'pkgbase' 'epoch' 'pkgver' 'pkgrel' 'makedepends' 'arch' "${splitpkg_overrides[@]}")
+readonly -a variables splitpkg_overrides
+
+backup_package_variables() {
+ for var in "${splitpkg_overrides[@]}"; do
+ indirect="${var}_backup"
+ eval "${indirect}=(\"\${$var[@]}\")"
+ done
+}
+
+restore_package_variables() {
+ for var in "${splitpkg_overrides[@]}"; do
+ indirect="${var}_backup"
+ if [ -n "${!indirect}" ]; then
+ eval "${var}=(\"\${$indirect[@]}\")"
+ else
+ unset "${var}"
+ fi
+ done
+}
+
+print_info() {
+ echo -e "%NAME%\n$pkgname\n"
+ if [ -n "$epoch" ]; then
+ echo -e "%VERSION%\n$epoch:$pkgver-$pkgrel\n"
+ else
+ echo -e "%VERSION%\n$pkgver-$pkgrel\n"
+ fi
+ echo -e "%PATH%\n$dir\n"
+
+ if [ -n "$pkgbase" ]; then
+ echo -e "%BASE%\n$pkgbase\n"
+ fi
+
+ if [ -n "$arch" ]; then
+ echo "%ARCH%"
+ for i in "${arch[@]}"; do echo "$i"; done
+ echo ""
+ fi
+ if [ -n "$depends" ]; then
+ echo "%DEPENDS%"
+ for i in "${depends[@]}"; do
+ echo "$i"
+ done
+ echo ""
+ fi
+ if [ -n "$makedepends" ]; then
+ echo "%MAKEDEPENDS%"
+ for i in "${makedepends[@]}"; do
+ echo "$i"
+ done
+ echo ""
+ fi
+ if [ -n "$conflicts" ]; then
+ echo "%CONFLICTS%"
+ for i in "${conflicts[@]}"; do echo "$i"; done
+ echo ""
+ fi
+ if [ -n "$provides" ]; then
+ echo "%PROVIDES%"
+ for i in "${provides[@]}"; do echo "$i"; done
+ echo ""
+ fi
+}
+
+source_pkgbuild() {
+ ret=0
+ dir=$1
+ pkgbuild=$dir/PKGBUILD
+ for var in "${variables[@]}"; do
+ unset "${var}"
+ done
+ source "$pkgbuild" &>/dev/null || ret=$?
+
+ # ensure $pkgname and $pkgver variables were found
+ if [ $ret -ne 0 -o -z "$pkgname" -o -z "$pkgver" ]; then
+ echo -e "%INVALID%\n$pkgbuild\n"
+ return 1
+ fi
+
+ if [ "${#pkgname[@]}" -gt "1" ]; then
+ pkgbase=${pkgbase:-${pkgname[0]}}
+ for pkg in "${pkgname[@]}"; do
+ if [ "$(type -t "package_${pkg}")" != "function" ]; then
+ echo -e "%INVALID%\n$pkgbuild\n"
+ return 1
+ else
+ backup_package_variables
+ pkgname=$pkg
+ while IFS= read -r line; do
+ var=${line%%=*}
+ var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
+ for realvar in "${variables[@]}"; do
+ if [ "$var" == "$realvar" ]; then
+ eval $line
+ break
+ fi
+ done
+ done < <(type "package_${pkg}")
+ print_info
+ restore_package_variables
+ fi
+ done
+ else
+ echo
+ print_info
+ fi
+
+ return 0
+}
+
+find_pkgbuilds() {
+ #Skip over some dirs
+ local d="${1##*/}"
+ if [ "$d" = "CVS" -o "$d" = ".svn" ]; then
+ return
+ fi
+
+ if [ -f "$1/PKGBUILD" ]; then
+ source_pkgbuild "$1"
+ return
+ fi
+ empty=1
+ for dir in "$1"/*; do
+ if [ -d "$dir" ]; then
+ find_pkgbuilds "$dir"
+ unset empty
+ fi
+ done
+ if [ -n "$empty" ]; then
+ echo -e "%MISSING%\n$1\n"
+ fi
+}
+
+if [ -z "$1" -o -z "$2" ]; then
+ exit 1
+fi
+
+CARCH=$1
+shift
+for dir in "$@"; do
+ find_pkgbuilds "$dir"
+done
+
+exit 0
diff --git a/extra/xbs-lukeshu/cron-jobs/devlist-mailer b/extra/xbs-lukeshu/cron-jobs/devlist-mailer
new file mode 100755
index 0000000..7f298b9
--- /dev/null
+++ b/extra/xbs-lukeshu/cron-jobs/devlist-mailer
@@ -0,0 +1,27 @@
+#!/bin/bash
+#Dummy helper to send email to arch-dev
+# It does nothing if no output
+
+# Load $LIST and $FROM from the config file
+. "$(dirname "$(readlink -e "$0")")/../config"
+
+SUBJECT="Repository Maintenance $(date +"%d-%m-%Y")"
+if [ $# -ge 1 ]; then
+ SUBJECT="$1 $(date +"%d-%m-%Y")"
+fi
+
+if [ $# -ge 2 ]; then
+ LIST="$2"
+fi
+
+stdin="$(cat)"
+#echo used to strip whitespace for checking for actual data
+if [ -n "$(echo $stdin)" ]; then
+
+ echo "Subject: $SUBJECT
+To: $LIST
+From: $FROM
+
+$stdin" | /usr/sbin/sendmail -F"$FROM" "$LIST"
+
+fi
diff --git a/extra/xbs-lukeshu/cron-jobs/ftpdir-cleanup b/extra/xbs-lukeshu/cron-jobs/ftpdir-cleanup
new file mode 100755
index 0000000..4063c09
--- /dev/null
+++ b/extra/xbs-lukeshu/cron-jobs/ftpdir-cleanup
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+. "$(dirname "$(readlink -e "$0")")/../config"
+. "$(dirname "$(readlink -e "$0")")/../db-functions"
+
+clean_pkg() {
+ local pkg
+ local target
+
+ if ! "${CLEANUP_DRYRUN}"; then
+ for pkg in "$@"; do
+ if [ -h "$pkg" ]; then
+ rm -f "$pkg" "$pkg.sig"
+ else
+ mv_acl "$pkg" "$CLEANUP_DESTDIR/${pkg##*/}"
+ if [ -e "$pkg.sig" ]; then
+ mv_acl "$pkg.sig" "$CLEANUP_DESTDIR/${pkg##*/}.sig"
+ fi
+ touch "${CLEANUP_DESTDIR}/${pkg##*/}"
+ fi
+ done
+ 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
+ for arch in "${ARCHES[@]}"; do
+ if [ ! -f "${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT}" ]; then
+ continue
+ fi
+ # get a list of actual available package files
+ find "${FTP_BASE}/${repo}/os/${arch}" -xtype f -name "*${PKGEXT}" -printf '%f\n' | sort > "${WORKDIR}/repo-${repo}-${arch}"
+ # get a list of package files defined in the repo db
+ bsdtar -xOf "${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT}" | awk '/^%FILENAME%/{getline;print}' | sort > "${WORKDIR}/db-${repo}-${arch}"
+
+ missing_pkgs=($(comm -13 "${WORKDIR}/repo-${repo}-${arch}" "${WORKDIR}/db-${repo}-${arch}"))
+ if [ ${#missing_pkgs[@]} -ge 1 ]; then
+ error "Missing packages in [%s] (%s)..." "${repo}" "${arch}"
+ for missing_pkg in "${missing_pkgs[@]}"; do
+ msg2 '%s' "${missing_pkg}"
+ done
+ fi
+
+ old_pkgs=($(comm -23 "${WORKDIR}/repo-${repo}-${arch}" "${WORKDIR}/db-${repo}-${arch}"))
+ if [ ${#old_pkgs[@]} -ge 1 ]; then
+ msg "Removing old packages from [%s] (%s)..." "${repo}" "${arch}"
+ for old_pkg in "${old_pkgs[@]}"; do
+ msg2 '%s' "${old_pkg}"
+ clean_pkg "${FTP_BASE}/${repo}/os/${arch}/${old_pkg}"
+ done
+ fi
+ done
+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
+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
+ msg "Removing old packages from package pool..."
+ for old_pkg in "${old_pkgs[@]}"; do
+ msg2 '%s' "${old_pkg}"
+ clean_pkg "$FTP_BASE/${PKGPOOL}/${old_pkg}"
+ done
+fi
+
+old_pkgs=($(find "${CLEANUP_DESTDIR}" -type f -name "*${PKGEXT}" -mtime +"${CLEANUP_KEEP}" -printf '%f\n'))
+if [ ${#old_pkgs[@]} -ge 1 ]; then
+ msg "Removing old packages from the cleanup directory..."
+ for old_pkg in "${old_pkgs[@]}"; do
+ msg2 '%s' "${old_pkg}"
+ if ! "${CLEANUP_DRYRUN}"; then
+ rm -f "${CLEANUP_DESTDIR}/${old_pkg}"
+ rm -f "${CLEANUP_DESTDIR}/${old_pkg}.sig"
+ fi
+ done
+fi
+
+for repo in "${PKGREPOS[@]}"; do
+ for arch in "${ARCHES[@]}"; do
+ repo_unlock "${repo}" "${arch}"
+ done
+done
+
+script_unlock
diff --git a/extra/xbs-lukeshu/cron-jobs/integrity-check b/extra/xbs-lukeshu/cron-jobs/integrity-check
new file mode 100755
index 0000000..7459380
--- /dev/null
+++ b/extra/xbs-lukeshu/cron-jobs/integrity-check
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+dirname="$(dirname "$(readlink -e "$0")")"
+
+. "${dirname}/../config"
+. "${dirname}/../db-functions"
+
+script_lock
+
+if [ $# -ne 1 ]; then
+ die "usage: %s <mailto>" "${0##*/}"
+fi
+mailto=$1
+
+check() {
+ "${dirname}"/check_archlinux/check_packages.py \
+ --repos="${repos}" \
+ --abs-tree="/srv/abs/rsync/${arch},/srv/abs/rsync/any" \
+ --repo-dir="${FTP_BASE}" \
+ --arch="${arch}" \
+ 2>&1 | "${dirname}"/devlist-mailer "Integrity Check ${arch}: ${repos}" "${mailto}"
+}
+
+repos='core,extra,community'
+arch='i686'
+check
+
+repos='core,extra,community,multilib'
+arch='x86_64'
+check
+
+script_unlock
diff --git a/extra/xbs-lukeshu/cron-jobs/make_repo_torrents b/extra/xbs-lukeshu/cron-jobs/make_repo_torrents
new file mode 100755
index 0000000..2eb0978
--- /dev/null
+++ b/extra/xbs-lukeshu/cron-jobs/make_repo_torrents
@@ -0,0 +1,70 @@
+#!/bin/bash
+# Copyright (C) 2014 Joseph Graham <joseph@t67.eu>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This script finds any updated packages and calls
+# `make_indivudual_torrent' for each of them.
+
+username=$( id -un )
+
+case "${username}" in
+ repo | root )
+ true
+ ;;
+ * )
+ echo "This script must be run as repo user or root user."
+ echo "ByeBye!"
+ exit 1
+ ;;
+esac
+
+# pacman doesn't support multiple different packages of the same name,
+# so it's OK to just stuff all the torrents into a single directory.
+script_directory="$(dirname "$(readlink -e "$0")")/.."
+. "$(dirname "$(readlink -e "$0")")/../config"
+public_location="$FTP_BASE/"
+torrent_location="$FTP_BASE/torrents/"
+
+cd "${torrent_location}"
+
+# Find any directories that might have packages in then
+find "${public_location}" -name 'os' -type 'd' |
+while read dir
+do
+ # Find any packages
+ find "${dir}" -name '*\.pkg\.tar\.xz' |
+ while read pkg
+ do
+ pkg_name="${pkg##*/}"
+
+ if [[ -h "${pkg}" ]] # check if it's a symbolic link
+ then
+ # We get the target of the symlink
+ pkg=$( readlink -f "${pkg}" )
+ fi
+
+ # If a .torrent file does not already exist for this package, we call
+ # `make_individual_torrent' to make it.
+ if ! [[ -f "${torrent_location}${pkg_name}.torrent" ]]
+ then
+ "$script_directory/make_individual_torrent" "${pkg}" "${public_location}"
+ fi
+ done
+done
+
+if [[ "${username}" == root ]]
+then
+ chown repo *
+fi
diff --git a/extra/xbs-lukeshu/cron-jobs/sourceballs b/extra/xbs-lukeshu/cron-jobs/sourceballs
new file mode 100755
index 0000000..c02912a
--- /dev/null
+++ b/extra/xbs-lukeshu/cron-jobs/sourceballs
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+dirname="$(dirname "$(readlink -e "$0")")"
+. "${dirname}/../config"
+. "${dirname}/../db-functions"
+pushd "${WORKDIR}" >/dev/null
+
+script_lock
+
+for repo in "${PKGREPOS[@]}"; do
+ for arch in "${ARCHES[@]}"; do
+ repo_lock "${repo}" "${arch}" || exit 1
+ done
+done
+
+#adjust the nice level to run at a lower priority
+renice +10 -p $$ > /dev/null
+
+# Create a readable file for each repo with the following format
+# <pkgbase|pkgname> <pkgver>-<pkgrel> <arch> <license>[ <license>]
+for repo in "${PKGREPOS[@]}"; do
+ for arch in "${ARCHES[@]}"; do
+ # Repo does not exist; skip it
+ if [ ! -f "${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT}" ]; then
+ continue
+ fi
+ bsdtar -xOf "${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT}" \
+ | awk '/^%NAME%/ { getline b };
+ /^%BASE%/ { getline b };
+ /^%VERSION%/ { getline v };
+ /^%LICENSE%/,/^$/ {
+ if ( !/^%LICENSE%/ ) { l=l" "$0 }
+ };
+ /^%ARCH%/ {
+ getline a;
+ printf "%s %s %s %s\n", b, v, a, l;
+ l="";
+ }'
+ done | sort -u > "${WORKDIR}/db-${repo}"
+done
+
+for repo in "${PKGREPOS[@]}"; do
+ for arch in "${ARCHES[@]}"; do
+ repo_unlock "${repo}" "${arch}"
+ done
+done
+
+# Create a list of all available source package file names
+find "${FTP_BASE}/${SRCPOOL}" -xtype f -name "*${SRCEXT}" -printf '%f\n' | sort -u > "${WORKDIR}/available-src-pkgs"
+
+# Check for all packages if we need to build a source package
+for repo in "${PKGREPOS[@]}"; do
+ newpkgs=()
+ failedpkgs=()
+ while read line; do
+ pkginfo=("${line}")
+ pkgbase=${pkginfo[0]}
+ pkgver=${pkginfo[1]}
+ pkgarch=${pkginfo[2]}
+ pkglicense=("${pkginfo[@]:3}")
+
+ # Should this package be skipped?
+ if grep -Fqx "${pkgbase}" "${dirname}/sourceballs.skip"; then
+ continue
+ fi
+ # Check if the license or .force file does not enforce creating a source package
+ 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"
+
+ # Build the source package if its not already there
+ if ! grep -Fqx "${pkgbase}-${pkgver}${SRCEXT}" "${WORKDIR}/available-src-pkgs"; then
+ # Check if we had failed before
+ if in_array "${pkgbase}-${pkgver}${SRCEXT}" "${failedpkgs[@]}"; then
+ continue
+ fi
+
+ # Get the sources from xbs
+ mkdir -p -m0770 "${WORKDIR}/pkgbuilds/${repo}-${pkgarch}"
+ cp -a "$(xbs releasepath "${pkgbase}" "${repo}" "${pkgarch}")" \
+ "${WORKDIR}/pkgbuilds/${repo}-${pkgarch}/${pkgbase}" >/dev/null 2>&1
+ if [ $? -ge 1 ]; then
+ failedpkgs+=("${pkgbase}-${pkgver}${SRCEXT}")
+ continue
+ fi
+
+ # Build the actual source package
+ pushd "${WORKDIR}/pkgbuilds/${repo}-${pkgarch}/${pkgbase}" >/dev/null
+ SRCPKGDEST=. makepkg --nocolor --allsource --ignorearch --skippgpcheck >"${WORKDIR}/${pkgbase}.log" 2>&1
+ if [ $? -eq 0 ] && [ -f "${pkgbase}-${pkgver}${SRCEXT}" ]; then
+ mv_acl "${pkgbase}-${pkgver}${SRCEXT}" "${FTP_BASE}/${SRCPOOL}/${pkgbase}-${pkgver}${SRCEXT}"
+ # Avoid creating the same source package for every arch
+ echo "${pkgbase}-${pkgver}${SRCEXT}" >> "${WORKDIR}/available-src-pkgs"
+ newpkgs+=("${pkgbase}-${pkgver}${SRCEXT}")
+ else
+ failedpkgs+=("${pkgbase}-${pkgver}${SRCEXT}")
+ cat "${WORKDIR}/${pkgbase}.log" >> "${WORKDIR}/makepkg-fail.log"
+ fi
+ popd >/dev/null
+ fi
+ done < "${WORKDIR}/db-${repo}"
+
+ if [ ${#newpkgs[@]} -ge 1 ]; then
+ msg "Adding source packages for [%s]..." "${repo}"
+ for new_pkg in "${newpkgs[@]}"; do
+ msg2 '%s' "${new_pkg}"
+ done
+ fi
+ if [ ${#failedpkgs[@]} -ge 1 ]; then
+ msg "Failed to create source packages for [%s]..." "${repo}"
+ for failed_pkg in "${failedpkgs[@]}"; do
+ msg2 '%s' "${failed_pkg}"
+ done
+ fi
+done
+
+# Cleanup old source packages
+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
+ msg "Removing old source packages..."
+ "${SOURCE_CLEANUP_DRYRUN}" && warning 'dry run mode is active'
+ for old_pkg in "${old_pkgs[@]}"; do
+ msg2 '%s' "${old_pkg}"
+ if ! "${SOURCE_CLEANUP_DRYRUN}"; then
+ mv_acl "$FTP_BASE/${SRCPOOL}/${old_pkg}" "${SOURCE_CLEANUP_DESTDIR}/${old_pkg}"
+ touch "${SOURCE_CLEANUP_DESTDIR}/${old_pkg}"
+ fi
+ done
+fi
+
+old_pkgs=($(find "${SOURCE_CLEANUP_DESTDIR}" -type f -name "*${SRCEXT}" -mtime +"${SOURCE_CLEANUP_KEEP}" -printf '%f\n'))
+if [ ${#old_pkgs[@]} -ge 1 ]; then
+ msg "Removing old source packages from the cleanup directory..."
+ for old_pkg in "${old_pkgs[@]}"; do
+ msg2 '%s' "${old_pkg}"
+ "${SOURCE_CLEANUP_DRYRUN}" || rm -f "${SOURCE_CLEANUP_DESTDIR}/${old_pkg}"
+ done
+fi
+
+if [ -f "${WORKDIR}/makepkg-fail.log" ]; then
+ msg "Log of failed packages"
+ cat "${WORKDIR}/makepkg-fail.log"
+fi
+
+script_unlock
diff --git a/extra/xbs-lukeshu/cron-jobs/sourceballs.force b/extra/xbs-lukeshu/cron-jobs/sourceballs.force
new file mode 100644
index 0000000..badf15d
--- /dev/null
+++ b/extra/xbs-lukeshu/cron-jobs/sourceballs.force
@@ -0,0 +1,4 @@
+faad2
+wxgtk
+wxpython
+glhack
diff --git a/extra/xbs-lukeshu/cron-jobs/sourceballs.skip b/extra/xbs-lukeshu/cron-jobs/sourceballs.skip
new file mode 100644
index 0000000..0e1731c
--- /dev/null
+++ b/extra/xbs-lukeshu/cron-jobs/sourceballs.skip
@@ -0,0 +1,43 @@
+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
+vdrift-data
+warmux-data
+wesnoth-data
+widelands-data
+xonotic-data
+texlive-bibtexextra
+texlive-bin
+texlive-core
+texlive-fontsextra
+texlive-formatsextra
+texlive-games
+texlive-genericextra
+texlive-htmlxml
+texlive-humanities
+texlive-langcjk
+texlive-langcyrillic
+texlive-langextra
+texlive-langgreek
+texlive-latexextra
+texlive-music
+texlive-pictures
+texlive-plainextra
+texlive-pstricks
+texlive-publishers
+texlive-science
diff --git a/extra/xbs-lukeshu/db-functions b/extra/xbs-lukeshu/db-functions
new file mode 100644
index 0000000..62260bb
--- /dev/null
+++ b/extra/xbs-lukeshu/db-functions
@@ -0,0 +1,456 @@
+#!/hint/bash
+
+# Some PKGBUILDs need CARCH to be set
+CARCH=$(. "$(librelib conf.sh)"; load_files makepkg; echo "$CARCH")
+
+# Useful functions
+UMASK=""
+set_umask () {
+ [ "$UMASK" == "" ] && UMASK="$(umask)"
+ export UMASK
+ umask 002
+}
+
+restore_umask () {
+ umask "$UMASK" >/dev/null
+}
+
+# just like mv -f, but we touch the file and then copy the content so
+# default ACLs in the target dir will be applied
+mv_acl() {
+ rm -f "$2"
+ touch "$2"
+ cat "$1" >"$2" || return 1
+ rm -f "$1"
+}
+
+# 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
+
+# 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##*/}"
+ if ! mkdir "$LOCKDIR" >/dev/null 2>&1 ; then
+ local _owner="$(/usr/bin/stat -c %U "$LOCKDIR")"
+ error "Script %s is already locked by %s." "${0##*/}" "$_owner"
+ exit 1
+ else
+ set_umask
+ return 0
+ fi
+}
+
+script_unlock() {
+ local LOCKDIR="$TMPDIR/.scriptlock.${0##*/}"
+ if [ ! -d "$LOCKDIR" ]; then
+ warning "Script %s was not locked!" "${0##*/}"
+ restore_umask
+ return 1
+ else
+ rmdir "$LOCKDIR"
+ restore_umask
+ return 0
+ fi
+}
+
+cleanup() {
+ local l
+ local repo
+ local arch
+
+ trap - EXIT INT QUIT TERM
+ for l in "${LOCKS[@]}"; do
+ repo=${l%.*}
+ arch=${l#*.}
+ if [ -d "$TMPDIR/.repolock.$repo.$arch" ]; then
+ msg "Removing left over lock from [%s] (%s)" "${repo}" "${arch}"
+ repo_unlock "$repo" "$arch"
+ fi
+ done
+ if [ -d "$TMPDIR/.scriptlock.${0##*/}" ]; then
+ msg "Removing left over lock from %s" "${0##*/}"
+ script_unlock
+ fi
+ rm -rf "$WORKDIR"
+
+ if (( REPO_MODIFIED )); then
+ date +%s > "${FTP_BASE}/lastupdate"
+ fi
+
+ [ "$1" ] && exit "$1"
+}
+
+trap abort INT QUIT TERM HUP
+trap cleanup EXIT
+
+
+#repo_lock <repo-name> <arch> [timeout]
+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
+ local _lockblock
+ local _owner
+
+ # This is the lock file used by repo-add and repo-remove
+ if [ -f "${DBLOCKFILE}" ]; then
+ 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
+ _trial=0
+ elif [ $# -eq 3 ]; then
+ _lockblock=false
+ _timeout=$3
+ let _trial=$_timeout/$LOCK_DELAY
+ fi
+
+ _count=0
+ while [ "$_count" -le "$_trial" ] || "$_lockblock" ; do
+ if ! mkdir "$LOCKDIR" >/dev/null 2>&1 ; then
+ _owner="$(/usr/bin/stat -c %U "$LOCKDIR")"
+ warning "Repo [%s] (%s) is already locked by %s." "${1}" "${2}" "$_owner"
+ msg2 "Retrying in %d seconds..." "$LOCK_DELAY"
+ else
+ LOCKS+=("$1.$2")
+ set_umask
+ return 0
+ fi
+ sleep "$LOCK_DELAY"
+ let _count=$_count+1
+ done
+
+ error "Repo [%s] (%s) is already locked by %s. Giving up!" "${1}" "${2}" "$_owner"
+ return 1
+}
+
+repo_unlock () { #repo_unlock <repo-name> <arch>
+ local LOCKDIR="$TMPDIR/.repolock.$1.$2"
+ if [ ! -d "$LOCKDIR" ]; then
+ warning "Repo lock [%s] (%s) was not locked!" "${1}" "${2}"
+ restore_umask
+ return 1
+ else
+ rmdir "$LOCKDIR"
+ restore_umask
+ return 0
+ fi
+}
+
+# usage: _grep_pkginfo pkgfile pattern
+_grep_pkginfo() {
+ local _ret
+
+ _ret="$(/usr/bin/bsdtar -xOqf "$1" .PKGINFO | grep -m 1 "^${2} = ")"
+ echo "${_ret#${2} = }"
+}
+
+
+# Get the package base or name as fallback
+getpkgbase() {
+ local _base
+
+ _base="$(_grep_pkginfo "$1" "pkgbase")"
+ if [ -z "$_base" ]; then
+ getpkgname "$1"
+ else
+ echo "$_base"
+ fi
+}
+
+issplitpkg() {
+ local _base
+
+ _base="$(_grep_pkginfo "$1" "pkgbase")"
+ if [ -z "$_base" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+# Get the package name
+getpkgname() {
+ local _name
+
+ _name="$(_grep_pkginfo "$1" "pkgname")"
+ if [ -z "$_name" ]; then
+ error "Package '%s' has no pkgname in the PKGINFO. Fail!" "$1"
+ exit 1
+ fi
+
+ echo "$_name"
+}
+
+# Get the pkgver-pkgrel of this package
+getpkgver() {
+ local _ver
+
+ _ver="$(_grep_pkginfo "$1" "pkgver")"
+ if [ -z "$_ver" ]; then
+ error "Package '%s' has no pkgver in the PKGINFO. Fail!" "$1"
+ exit 1
+ fi
+
+ echo "$_ver"
+}
+
+getpkgarch() {
+ local _ver
+
+ _ver="$(_grep_pkginfo "$1" "arch")"
+ if [ -z "$_ver" ]; then
+ error "Package '%s' has no arch in the PKGINFO. Fail!" "$1"
+ exit 1
+ fi
+
+ echo "$_ver"
+}
+
+check_packager() {
+ local _packager
+
+ _packager=$(_grep_pkginfo "$1" "packager")
+ [[ $_packager && $_packager != 'Unknown Packager' ]]
+}
+
+getpkgfile() {
+ if [[ ${#} -ne 1 ]]; then
+ error 'No canonical package found!'
+ exit 1
+ elif [ ! -f "${1}" ]; then
+ error "Package %s not found!" "${1}"
+ exit 1
+ elif "${REQUIRE_SIGNATURE}" && [ ! -f "${1}.sig" ]; then
+ error "Package signature %s not found!" "${1}.sig"
+ exit 1
+ fi
+
+ echo "${1}"
+}
+
+getpkgfiles() {
+ local f
+ if [ ! -z "$(printf '%s\n' "${@%\.*}" | sort | uniq -D)" ]; then
+ error 'Duplicate packages found!'
+ exit 1
+ fi
+
+ for f in "${@}"; do
+ if [ ! -f "${f}" ]; then
+ error "Package %s not found!" "${f}"
+ exit 1
+ elif "${REQUIRE_SIGNATURE}" && [ ! -f "${f}.sig" ]; then
+ error "Package signature %s not found!" "${f}.sig"
+ exit 1
+ fi
+ done
+
+ echo "${@}"
+}
+
+check_pkgfile() {
+ local pkgfile=$1
+
+ local pkgname="$(getpkgname "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+ local pkgver="$(getpkgver "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+ local pkgarch="$(getpkgarch "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+
+ in_array "${pkgarch}" "${ARCHES[@]}" 'any' || return 1
+
+ if echo "${pkgfile##*/}" | grep -q "${pkgname}-${pkgver}-${pkgarch}"; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+check_pkgxbs() {
+ local pkgfile="${1}"
+ local _pkgbase="$(getpkgbase "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+ local _pkgname="$(getpkgname "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+ local _pkgver="$(getpkgver "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+ local _pkgarch="$(getpkgarch "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+ local repo="${2}"
+
+ in_array "${repo}" "${PKGREPOS[@]}" || return 1
+
+ local xbsver="$(. "$(xbs releasepath "${_pkgbase}" "${repo}" "${_pkgarch}")/PKGBUILD"; get_full_version "${_pkgname}")"
+ [ "${xbsver}" == "${_pkgver}" ] || return 1
+
+ local xbsnames=($(. "$(xbs releasepath "${_pkgbase}" "${repo}" "${_pkgarch}")/PKGBUILD"; echo "${pkgname[@]}"))
+ in_array "${_pkgname}" "${xbsnames[@]}" || return 1
+
+ return 0
+}
+
+check_splitpkgs() {
+ local repo="${1}"
+ shift
+ local pkgfiles=("${@}")
+ local pkgfile
+ local pkgdir
+ local xbsname
+
+ mkdir -p "${WORKDIR}/check_splitpkgs/"
+ pushd "${WORKDIR}/check_splitpkgs" >/dev/null
+
+ for pkgfile in "${pkgfiles[@]}"; do
+ issplitpkg "${pkgfile}" || continue
+ local _pkgbase="$(getpkgbase "${pkgfile}")"
+ local _pkgname="$(getpkgname "${pkgfile}")"
+ local _pkgarch="$(getpkgarch "${pkgfile}")"
+ mkdir -p "${repo}/${_pkgarch}/${_pkgbase}"
+ echo "${_pkgname}" >> "${repo}/${_pkgarch}/${_pkgbase}/staging"
+
+ local xbsnames=($(. "$(xbs releasepath "${_pkgbase}" "${repo}" "${_pkgarch}")/PKGBUILD"; echo "${pkgname[@]}"))
+ printf '%s\n' "${xbsnames[@]}" >> "${repo}/${_pkgarch}/${_pkgbase}/xbs"
+ done
+ popd >/dev/null
+
+ for pkgdir in "${WORKDIR}/check_splitpkgs/${repo}"/*/*; do
+ [ ! -d "${pkgdir}" ] && continue
+ sort -u "${pkgdir}/staging" -o "${pkgdir}/staging"
+ sort -u "${pkgdir}/xbs" -o "${pkgdir}/xbs"
+ if [ ! -z "$(comm -13 "${pkgdir}/staging" "${pkgdir}/xbs")" ]; then
+ return 1
+ fi
+ done
+
+ return 0
+}
+
+check_pkgrepos() {
+ local pkgfile=$1
+
+ local pkgname="$(getpkgname "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+ local pkgver="$(getpkgver "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+ local pkgarch="$(getpkgarch "${pkgfile}")"
+ [ $? -ge 1 ] && return 1
+
+ [ -f "${FTP_BASE}/${PKGPOOL}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT} ] && return 1
+ [ -f "${FTP_BASE}/${PKGPOOL}/${pkgname}-${pkgver}-${pkgarch}"${PKGEXT}.sig ] && return 1
+ [ -f "${FTP_BASE}/${PKGPOOL}/${pkgfile##*/}" ] && return 1
+ [ -f "${FTP_BASE}/${PKGPOOL}/${pkgfile##*/}.sig" ] && return 1
+
+ return 0
+}
+
+#usage: chk_license ${license[@]}"
+chk_license() {
+ local l
+ for l in "${@}"; do
+ in_array "${l}" "${ALLOWED_LICENSES[@]}" && return 0
+ done
+
+ return 1
+}
+
+check_repo_permission() {
+ local repo=$1
+
+ [ ${#PKGREPOS[@]} -eq 0 ] && return 1
+ [ -z "${PKGPOOL}" ] && return 1
+
+ in_array "${repo}" "${PKGREPOS[@]}" || return 1
+
+ [ -w "$FTP_BASE/${PKGPOOL}" ] || return 1
+
+ local arch
+ for arch in "${ARCHES[@]}"; do
+ local dir="${FTP_BASE}/${repo}/os/${arch}/"
+ [ -w "${dir}" ] || return 1
+ [ -f "${dir}${repo}"${DBEXT} -a ! -w "${dir}${repo}"${DBEXT} ] && return 1
+ [ -f "${dir}${repo}"${FILESEXT} -a ! -w "${dir}${repo}"${FILESEXT} ] && return 1
+ done
+
+ return 0
+}
+
+set_repo_permission() {
+ local repo=$1
+ local arch=$2
+ local dbfile="${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT}"
+ local filesfile="${FTP_BASE}/${repo}/os/${arch}/${repo}${FILESEXT}"
+
+ if [ -w "${dbfile}" ]; then
+ local group=$(/usr/bin/stat --printf='%G' "$(dirname "${dbfile}")")
+ chgrp "$group" "${dbfile}" || error "Could not change group of %s to %s" "${dbfile}" "$group"
+ chgrp "$group" "${filesfile}" || error "Could not change group of %s to %s" "${filesfile}" "$group"
+ chmod g+w "${dbfile}" || error "Could not set write permission for group %s to %s" "$group" "${dbfile}"
+ chmod g+w "${filesfile}" || error "Could not set write permission for group %s to %s" "$group" "${filesfile}"
+ else
+ error "You don't have permission to change %s" "${dbfile}"
+ fi
+}
+
+arch_repo_add() {
+ local repo=$1
+ local arch=$2
+ local pkgs=("${@:3}")
+
+ 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[@]}" \
+ || 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}"
+
+ REPO_MODIFIED=1
+}
+
+arch_repo_remove() {
+ local repo=$1
+ 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[@]}" \
+ || 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
+}
diff --git a/extra/xbs-lukeshu/db-import b/extra/xbs-lukeshu/db-import
new file mode 100755
index 0000000..a8a073d
--- /dev/null
+++ b/extra/xbs-lukeshu/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/extra/xbs-lukeshu/db-import.conf b/extra/xbs-lukeshu/db-import.conf
new file mode 100644
index 0000000..aaf7b0f
--- /dev/null
+++ b/extra/xbs-lukeshu/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-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
diff --git a/extra/xbs-lukeshu/db-init b/extra/xbs-lukeshu/db-init
new file mode 100755
index 0000000..e25dbff
--- /dev/null
+++ b/extra/xbs-lukeshu/db-init
@@ -0,0 +1,6 @@
+#!/bin/bash
+# Creates the repo structure defined in config
+
+source "$(dirname "$(readlink -e "$0")")/config"
+
+mkdir -p -- "${FTP_BASE}"/{"${PKGPOOL}","${SRCPOOL}"} "${CLEANUP_DESTDIR}" "${SOURCE_CLEANUP_DESTDIR}" "${STAGING}"
diff --git a/extra/xbs-lukeshu/db-list-nonfree.py b/extra/xbs-lukeshu/db-list-nonfree.py
new file mode 100755
index 0000000..a486fa5
--- /dev/null
+++ b/extra/xbs-lukeshu/db-list-nonfree.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python2
+#-*- encoding: utf-8 -*-
+from filter import *
+import argparse
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(
+ prog="nonfree_in_db",
+ description="Cleans nonfree files on repo",)
+
+ parser.add_argument("-k", "--blacklist-file", type=str,
+ help="File containing blacklisted names",
+ required=True,)
+
+ parser.add_argument("-b", "--database", type=str,
+ help="dabatase to clean",
+ required=True,)
+
+ args=parser.parse_args()
+
+ if not (args.blacklist_file and args.database):
+ parser.print_help()
+ exit(1)
+
+ blacklist=listado(args.blacklist_file)
+ pkgs=get_pkginfo_from_db(args.database)
+
+ print(" ".join([pkg["name"] for pkg in pkgs if pkg["name"] in blacklist]))
diff --git a/extra/xbs-lukeshu/db-list-unsigned-packages b/extra/xbs-lukeshu/db-list-unsigned-packages
new file mode 100755
index 0000000..095e1e6
--- /dev/null
+++ b/extra/xbs-lukeshu/db-list-unsigned-packages
@@ -0,0 +1,38 @@
+#!/bin/bash
+# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -e
+
+# Output a list of repo/package-name-and-version pairs representing
+# unsigned packages available for architecture $1 and specified for
+# architecture $2 (usually $1 or any, default is to list all).
+
+. "$(dirname "$(readlink -e "$0")")/config"
+. "$(dirname "$(readlink -e "$0")")/db-functions"
+
+if [ $# -lt 1 ]; then
+ msg "usage: %s <architecture>" "${0##*/}"
+ exit 1
+fi
+
+arch=$1
+shift
+
+for repo in "${PKGREPOS[@]}"
+do
+ db="${FTP_BASE}/${repo}/os/${arch}/${repo}.db"
+ [ -f "$db" ] && "$(dirname "$(readlink -e "$0")")/db-list-unsigned-packages.py" "$repo" "$@" < "$db"
+done
diff --git a/extra/xbs-lukeshu/db-list-unsigned-packages.py b/extra/xbs-lukeshu/db-list-unsigned-packages.py
new file mode 100755
index 0000000..80cff51
--- /dev/null
+++ b/extra/xbs-lukeshu/db-list-unsigned-packages.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""
+Output a list of repo/package-name-and-version pairs representing
+unsigned packages in the database at standard input of repo named in
+the first argument and specified for architectures listed in the
+following arguments (usually the one of the database or any, default
+is to list all).
+
+If the --keyset argument is passed, print the key fingerprint of every
+signed package.
+"""
+
+
+import base64
+import subprocess
+import sys
+import tarfile
+
+
+def main():
+ """Do the job."""
+ check_keys = False
+ if "--keyset" in sys.argv:
+ sys.argv.remove("--keyset")
+ check_keys = True
+ repo = sys.argv[1]
+ pkgarches = frozenset(name.encode("utf-8") for name in sys.argv[2:])
+ packages = []
+ keys = []
+ with tarfile.open(fileobj=sys.stdin.buffer) as archive:
+ for entry in archive:
+ if entry.name.endswith("/desc"):
+ content = archive.extractfile(entry)
+ skip = False
+ is_arch = False
+ key = None
+ for line in content:
+ if is_arch:
+ is_arch = False
+ if pkgarches and line.strip() not in pkgarches:
+ skip = True # different architecture
+ break
+ if line == b"%PGPSIG%\n":
+ skip = True # signed
+ key = b""
+ if check_keys:
+ continue
+ else:
+ break
+ if line == b"%ARCH%\n":
+ is_arch = True
+ continue
+ if key is not None:
+ if line.strip():
+ key += line.strip()
+ else:
+ break
+ if check_keys and key:
+ key_binary = base64.b64decode(key)
+ keys.append(key_binary)
+ packages.append(repo + "/" + entry.name[:-5])
+ if skip:
+ continue
+ print(repo + "/" + entry.name[:-5])
+ if check_keys and keys:
+ # We have collected all signed package names in packages and
+ # all keys in keys. Let's now ask gpg to list all signatures
+ # and find which keys made them.
+ packets = subprocess.check_output(("gpg", "--list-packets"),
+ input=b"".join(keys))
+ i = 0
+ for line in packets.decode("latin1").split("\n"):
+ if line.startswith(":signature packet:"):
+ keyid = line[line.index("keyid ") + len("keyid "):]
+ print(packages[i], keyid)
+ i += 1
+
+
+if __name__ == "__main__":
+ main()
diff --git a/extra/xbs-lukeshu/db-move b/extra/xbs-lukeshu/db-move
new file mode 100755
index 0000000..89a0ad6
--- /dev/null
+++ b/extra/xbs-lukeshu/db-move
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+. "$(dirname "$(readlink -e "$0")")/config"
+. "$(dirname "$(readlink -e "$0")")/db-functions"
+
+if [ $# -lt 3 ]; then
+ msg "usage: %s <repo-from> <repo-to> <pkgname|pkgbase> ..." "${0##*/}"
+ exit 1
+fi
+
+args=("${@}")
+repo_from="${args[0]}"
+repo_to="${args[1]}"
+ftppath_from="${FTP_BASE}/${repo_from}/os/"
+ftppath_to="${FTP_BASE}/${repo_to}/os/"
+
+if ! check_repo_permission "$repo_to" || ! check_repo_permission "$repo_from"; then
+ die "You don't have permission to move packages from %s to %s" "${repo_from}" "${repo_to}"
+fi
+
+# TODO: this might lock too much (architectures)
+for pkgarch in "${ARCHES[@]}"; do
+ repo_lock "${repo_to}" "${pkgarch}" || exit 1
+ repo_lock "${repo_from}" "${pkgarch}" || exit 1
+done
+
+# First loop is to check that all necessary files exist
+for pkgbase in "${args[@]:2}"; do
+ for pkgarch in "${ARCHES[@]}" 'any'; do
+ xbsrepo_from="$(xbs releasepath "${pkgbase}" "${repo_from}" "${pkgarch}")"
+ if [ -r "${xbsrepo_from}/PKGBUILD" ]; then
+ pkgnames=($(. "${xbsrepo_from}/PKGBUILD"; echo "${pkgname[@]}"))
+ if [ ${#pkgnames[@]} -lt 1 ]; then
+ die "Could not read pkgname"
+ fi
+
+ if [ "${pkgarch}" == 'any' ]; then
+ tarches=("${ARCHES[@]}")
+ else
+ tarches=("${pkgarch}")
+ 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
+ done
+ continue 2
+ fi
+ done
+ die "%s not found in %s" "${pkgbase}" "${repo_from}"
+done
+
+msg "Moving packages from [%s] to [%s]..." "${repo_from}" "${repo_to}"
+
+declare -A add_pkgs
+declare -A remove_pkgs
+for pkgbase in "${args[@]:2}"; do
+ # move the package in xbs
+ arches=($(xbs move "${repo_from}" "${repo_to}" "${pkgbase}"))
+ # move the package in ftp
+ for pkgarch in "${arches[@]}"; do
+ xbsrepo_to="$(xbs releasepath "$pkgbase" "$repo_to" "$pkgarch")"
+ if true; then # to add an indent level to make merging easier
+ if [ "${pkgarch}" == 'any' ]; then
+ tarches=("${ARCHES[@]}")
+ else
+ tarches=("${pkgarch}")
+ fi
+ msg2 "%s (%s)" "${pkgbase}" "${tarches[*]}"
+ pkgnames=($(. "${xbsrepo_to}/PKGBUILD"; echo "${pkgname[@]}"))
+
+ 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##*/}"
+
+ ln -s "../../../${PKGPOOL}/${pkgfile}" "${ftppath_to}/${tarch}/"
+ if [ -f "${FTP_BASE}/${PKGPOOL}/${pkgfile}.sig" ]; then
+ ln -s "../../../${PKGPOOL}/${pkgfile}.sig" "${ftppath_to}/${tarch}/"
+ fi
+ add_pkgs[${tarch}]+="${FTP_BASE}/${PKGPOOL}/${pkgfile} "
+ remove_pkgs[${tarch}]+="${pkgname} "
+ done
+ done
+ fi
+ done
+done
+
+for tarch in "${ARCHES[@]}"; do
+ if [ -n "${add_pkgs[${tarch}]}" ]; then
+ arch_repo_add "${repo_to}" "${tarch}" ${add_pkgs[${tarch}]}
+ arch_repo_remove "${repo_from}" "${tarch}" ${remove_pkgs[${tarch}]}
+ fi
+done
+
+for pkgarch in "${ARCHES[@]}"; do
+ repo_unlock "${repo_from}" "${pkgarch}"
+ repo_unlock "${repo_to}" "${pkgarch}"
+done
diff --git a/extra/xbs-lukeshu/db-pick-mirror b/extra/xbs-lukeshu/db-pick-mirror
new file mode 100755
index 0000000..9474ed7
--- /dev/null
+++ b/extra/xbs-lukeshu/db-pick-mirror
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+
+require 'json'
+require 'rest_client'
+
+protocol = ARGV[0]
+jsonurl = ARGV[1]
+
+data = JSON::parse(RestClient.get(jsonurl))
+
+if data["version"] != 3
+ print "Data format version != 3"
+ exit 1
+end
+
+urls = data["urls"]
+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/extra/xbs-lukeshu/db-remove b/extra/xbs-lukeshu/db-remove
new file mode 100755
index 0000000..dcbe4b4
--- /dev/null
+++ b/extra/xbs-lukeshu/db-remove
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+. "$(dirname "$(readlink -e "$0")")/config"
+. "$(dirname "$(readlink -e "$0")")/db-functions"
+
+if [ $# -lt 3 ]; then
+ msg "usage: %s <repo> <arch> <pkgname|pkgbase> ..." "${0##*/}"
+ exit 1
+fi
+
+repo="$1"
+arch="$2"
+pkgbases=("${@:3}")
+
+if ! check_repo_permission "$repo"; then
+ die "You don't have permission to remove packages from %s" "${repo}"
+fi
+
+if [ "$arch" == "any" ]; then
+ tarches=("${ARCHES[@]}")
+else
+ tarches=("$arch")
+fi
+
+for tarch in "${tarches[@]}"; do
+ repo_lock "$repo" "$tarch" || exit 1
+done
+
+remove_pkgs=()
+for pkgbase in "${pkgbases[@]}"; do
+ msg "Removing %s from [%s]..." "$pkgbase" "$repo"
+
+ path="$(xbs releasepath "$pkgbase" "$repo" "$arch")"
+ if [ -d "$path" ]; then
+ remove_pkgs+=($(. "$path/PKGBUILD"; echo "${pkgname[@]}"))
+ xbs unrelease "$pkgbase" "$repo" "$arch"
+ else
+ warning "%s not found in %s for %s" \
+ "$pkgbase" "$(xbs name)" "$repo-$arch"
+ warning "Removing only %s from the repo" "$pkgbase"
+ warning "If it was a split package you have to remove the others yourself!"
+ remove_pkgs+=("$pkgbase")
+ fi
+done
+
+for tarch in "${tarches[@]}"; do
+ arch_repo_remove "${repo}" "${tarch}" "${remove_pkgs[@]}"
+ repo_unlock "$repo" "$tarch"
+done
diff --git a/extra/xbs-lukeshu/db-repo-add b/extra/xbs-lukeshu/db-repo-add
new file mode 100755
index 0000000..4611bdf
--- /dev/null
+++ b/extra/xbs-lukeshu/db-repo-add
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+. "$(dirname "$(readlink -e "$0")")/config"
+. "$(dirname "$(readlink -e "$0")")/db-functions"
+
+if [ $# -lt 3 ]; then
+ msg "usage: %s <repo> <arch> <pkgfile> ..." "${0##*/}"
+ exit 1
+fi
+
+repo="$1"
+arch="$2"
+pkgfiles=("${@:3}")
+
+ftppath="$FTP_BASE/$repo/os"
+
+if ! check_repo_permission "$repo"; then
+ die "You don't have permission to add packages to %s" "${repo}"
+fi
+
+if [ "$arch" == "any" ]; then
+ tarches=("${ARCHES[@]}")
+else
+ tarches=("$arch")
+fi
+
+for tarch in "${tarches[@]}"; do
+ repo_lock "$repo" "$tarch" || exit 1
+done
+
+for tarch in "${tarches[@]}"; do
+ for pkgfile in "${pkgfiles[@]}"; do
+ if [[ ! -f "${FTP_BASE}/${repo}/os/${arch}/${pkgfile##*/}" ]]; then
+ die "Package file %s not found in %s" "${pkgfile##*/}" "${FTP_BASE}/${repo}/os/${arch}/"
+ else
+ msg "Adding %s to [%s]..." "$pkgfile" "$repo"
+ fi
+ done
+ arch_repo_add "${repo}" "${tarch}" "${pkgfiles[@]}"
+ repo_unlock "$repo" "$tarch"
+done
diff --git a/extra/xbs-lukeshu/db-repo-remove b/extra/xbs-lukeshu/db-repo-remove
new file mode 100755
index 0000000..aadc4ce
--- /dev/null
+++ b/extra/xbs-lukeshu/db-repo-remove
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+. "$(dirname "$(readlink -e "$0")")/config"
+. "$(dirname "$(readlink -e "$0")")/db-functions"
+
+if [ $# -lt 3 ]; then
+ msg "usage: %s <repo> <arch> <pkgname> ..." "${0##*/}"
+ exit 1
+fi
+
+repo="$1"
+arch="$2"
+pkgnames=("${@:3}")
+
+ftppath="$FTP_BASE/$repo/os"
+
+if ! check_repo_permission "$repo"; then
+ die "You don't have permission to remove packages from %s" "${repo}"
+fi
+
+if [ "$arch" == "any" ]; then
+ tarches=("${ARCHES[@]}")
+else
+ tarches=("$arch")
+fi
+
+for tarch in "${tarches[@]}"; do
+ repo_lock "$repo" "$tarch" || exit 1
+done
+
+for tarch in "${tarches[@]}"; do
+ for pkgname in "${pkgnames[@]}"; do
+ msg "Removing %s from [%s]..." "$pkgname" "$repo"
+ done
+ arch_repo_remove "${repo}" "${tarch}" "${pkgnames[@]}"
+ repo_unlock "$repo" "$tarch"
+done
diff --git a/extra/xbs-lukeshu/db-update b/extra/xbs-lukeshu/db-update
new file mode 100755
index 0000000..559ef3f
--- /dev/null
+++ b/extra/xbs-lukeshu/db-update
@@ -0,0 +1,126 @@
+#!/bin/bash
+
+. "$(dirname "$(readlink -e "$0")")/config"
+. "$(dirname "$(readlink -e "$0")")/db-functions"
+
+if [ $# -ge 1 ]; then
+ warning "Calling %s with a specific repository is no longer supported" "${0##*/}"
+ exit 1
+fi
+
+# Find repos with packages to release
+staging_repos=($(find "${STAGING}" -mindepth 1 -type f -name "*${PKGEXT}" -printf '%h\n' | sort -u))
+if [ $? -ge 1 ]; then
+ die "Could not read %s" "${STAGING}"
+fi
+
+repos=()
+for staging_repo in "${staging_repos[@]##*/}"; do
+ if in_array "${staging_repo}" "${PKGREPOS[@]}"; then
+ repos+=("${staging_repo}")
+ fi
+done
+
+# TODO: this might lock too much (architectures)
+for repo in "${repos[@]}"; do
+ for pkgarch in "${ARCHES[@]}"; do
+ repo_lock "${repo}" "${pkgarch}" || exit 1
+ done
+done
+
+# check if packages are valid
+for repo in "${repos[@]}"; do
+ if ! check_repo_permission "${repo}"; then
+ die "You don't have permission to update packages in %s" "${repo}"
+ fi
+ pkgs=($(getpkgfiles "${STAGING}/${repo}/"*${PKGEXT}))
+ if [ $? -eq 0 ]; then
+ for pkg in "${pkgs[@]}"; do
+ if [ -h "${pkg}" ]; then
+ die "Package %s is a symbolic link" "${repo}/${pkg##*/}"
+ fi
+ 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
+ done
+ if ! check_splitpkgs "${repo}" "${pkgs[@]}"; then
+ die "Missing split packages for %s" "${repo}"
+ fi
+ else
+ die "Could not read %s" "${STAGING}"
+ fi
+done
+
+dirs=()
+for repo in "${repos[@]}"; do
+ msg "Updating [%s]..." "${repo}"
+ any_pkgs=($(getpkgfiles "${STAGING}/${repo}/"*-any${PKGEXT} 2>/dev/null))
+ for pkgarch in "${ARCHES[@]}"; do
+ add_dirs=()
+ add_pkgs=()
+ arch_pkgs=($(getpkgfiles "${STAGING}/${repo}/"*-"${pkgarch}"${PKGEXT} 2>/dev/null))
+ for pkg in "${arch_pkgs[@]}" "${any_pkgs[@]}"; do
+ pkgfile="${pkg##*/}"
+ msg2 "%s (%s)" "${pkgfile}" "${pkgarch}"
+ # any packages might have been moved by the previous run
+ if [ -f "${pkg}" ]; then
+ mv "${pkg}" "$FTP_BASE/${PKGPOOL}"
+ fi
+ ln -s "../../../${PKGPOOL}/${pkgfile}" "$FTP_BASE/$repo/os/${pkgarch}"
+ # also move signatures
+ if [ -f "${pkg}.sig" ]; then
+ mv "${pkg}.sig" "$FTP_BASE/${PKGPOOL}"
+ fi
+ if [ -f "$FTP_BASE/${PKGPOOL}/${pkgfile}.sig" ]; then
+ ln -s "../../../${PKGPOOL}/${pkgfile}.sig" "$FTP_BASE/$repo/os/${pkgarch}"
+ fi
+ add_dirs+=("${STAGING}/abslibre/$(getpkgarch "$FTP_BASE/$PKGPOOL/$pkgfile")/$repo/$(getpkgbase "$FTP_BASE/$PKGPOOL/$pkgfile")")
+ add_pkgs+=("${pkgfile}")
+ done
+ for add_dir in "${add_dirs[@]}"; do
+ (cd "${add_dir}" && xbs release-server "${repo}" "${pkgarch}") ||
+ error 'cd %q && xbs release-server %q %q' "${add_dir}" "${repo}" "${pkgarch}"
+ done
+ if [ ${#add_pkgs[@]} -ge 1 ]; then
+ arch_repo_add "${repo}" "${pkgarch}" "${add_pkgs[@]}"
+ fi
+ dirs+=("${add_dirs[@]}")
+ done
+done
+
+for repo in "${repos[@]}"; do
+ for pkgarch in "${ARCHES[@]}"; do
+ repo_unlock "${repo}" "${pkgarch}"
+ done
+done
+
+cd "${STAGING}"
+
+# Remove left over XBS files
+rm -rf -- "${dirs[@]}"
+dirname -z -- "${dirs[@]}" |
+ xargs -0 realpath -zm --relative-to="${STAGING}" -- |
+ xargs -0 rmdir -p -- 2>/dev/null
+
+# Stage generated source files
+while read -r file; do
+ pub="${FTP_BASE}/${file}"
+ if [[ -f "$pub" ]]; then
+ warning "file already exists: %s" "${file}"
+ else
+ mkdir -p -- "${pub%/*}"
+ mv -vn "$file" "$pub"
+ fi
+done < <(find other sources -type f 2>/dev/null)
diff --git a/extra/xbs-lukeshu/make_individual_torrent b/extra/xbs-lukeshu/make_individual_torrent
new file mode 100755
index 0000000..0a7e778
--- /dev/null
+++ b/extra/xbs-lukeshu/make_individual_torrent
@@ -0,0 +1,52 @@
+#!/bin/bash
+# Copyright (C) 2014 Joseph Graham <joseph@t67.eu>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This script is called by `make_repo_torrents' to make a torrent. It
+# depends on `mktorrent'. It takes the following args:
+# $1 - path of package
+# $2 - public location
+
+# Comma seperated list of trackers, no spaces
+# t67.eu is run by Xylon, hackcoop by fauno & friends
+trackers='http://t67.eu:6969/announce,http://tracker.hackcoop.com.ar/announce'
+
+# This mirror is put as a webseed. Which mirror we use for a webseed
+# doesn't really matter since it's re-written on the client machine by
+# pacman2pacman so it won't normally be used anyway.
+seed_url='http://repo.parabolagnulinux.org/'
+
+if [[ -z "${1}" ]]
+then
+ echo "Error. First arg must be the path of the package."
+ echo 1
+fi
+
+if [[ -z "${2}" ]]
+then
+ echo "Error. Second arg must be the public location."
+ echo 1
+fi
+
+pkg="${1}"
+public_location="${2}"
+
+pkg_name="${pkg##*/}"
+
+# URL of the actual package for the webseed
+webseed="${seed_url}${pkg#${public_location}}"
+
+mktorrent -a "${trackers}" "${pkg}" -w "${webseed}" >/dev/null ||
+echo "Error making torrent for \"${pkg}\""
diff --git a/extra/xbs-lukeshu/test/__init__.py b/extra/xbs-lukeshu/test/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extra/xbs-lukeshu/test/__init__.py
diff --git a/extra/xbs-lukeshu/test/blacklist_sample b/extra/xbs-lukeshu/test/blacklist_sample
new file mode 100644
index 0000000..2a02af6
--- /dev/null
+++ b/extra/xbs-lukeshu/test/blacklist_sample
@@ -0,0 +1,2 @@
+alex:alex-libre: Aquí va un comentario
+gmime22 ::Non free dependencies \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/core.db.tar.gz b/extra/xbs-lukeshu/test/core.db.tar.gz
new file mode 100644
index 0000000..5eb2081
--- /dev/null
+++ b/extra/xbs-lukeshu/test/core.db.tar.gz
Binary files differ
diff --git a/extra/xbs-lukeshu/test/depends b/extra/xbs-lukeshu/test/depends
new file mode 100644
index 0000000..7ff3ad4
--- /dev/null
+++ b/extra/xbs-lukeshu/test/depends
@@ -0,0 +1,4 @@
+%DEPENDS%
+glibc>=2.13
+zlib
+
diff --git a/extra/xbs-lukeshu/test/desc b/extra/xbs-lukeshu/test/desc
new file mode 100644
index 0000000..abba644
--- /dev/null
+++ b/extra/xbs-lukeshu/test/desc
@@ -0,0 +1,39 @@
+%FILENAME%
+binutils-2.21-4-x86_64.pkg.tar.xz
+
+%NAME%
+binutils
+
+%VERSION%
+2.21-4
+
+%DESC%
+A set of programs to assemble and manipulate binary and object files
+
+%GROUPS%
+base
+
+%CSIZE%
+3412892
+
+%ISIZE%
+17571840
+
+%MD5SUM%
+4e666f87c78998f4839f33dc06d2043a
+
+%URL%
+http://www.gnu.org/software/binutils/
+
+%LICENSE%
+GPL
+
+%ARCH%
+x86_64
+
+%BUILDDATE%
+1297240369
+
+%PACKAGER%
+Allan McRae <allan@archlinux.org>
+
diff --git a/extra/xbs-lukeshu/test/lib/common.inc b/extra/xbs-lukeshu/test/lib/common.inc
new file mode 100644
index 0000000..b9a2f9d
--- /dev/null
+++ b/extra/xbs-lukeshu/test/lib/common.inc
@@ -0,0 +1,321 @@
+set -E
+
+# override the default TMPDIR
+init_tmpdir() {
+ [[ -n $MASTER_TMPDIR ]] || export MASTER_TMPDIR="$(mktemp -dqt ${0##*/}.XXXXXXXXXX)"
+ export TMPDIR=$MASTER_TMPDIR
+}
+
+_TMPDIR=$TMPDIR
+. "$(dirname ${BASH_SOURCE[0]})/../../config"
+TMPDIR=$_TMPDIR
+init_tmpdir
+
+. "$(dirname ${BASH_SOURCE[0]})/../../db-functions"
+
+arches() (
+ . $(librelib conf.sh)
+ load_files libretools
+ printf '%s\n' "${ARCHES[*]}"
+)
+
+arch_svn() {
+ /usr/bin/svn "$@"
+}
+
+signpkg() {
+ if [[ -r '/etc/makepkg.conf' ]]; then
+ source '/etc/makepkg.conf'
+ else
+ die '/etc/makepkg.conf not found!'
+ fi
+ if [[ -r ~/.makepkg.conf ]]; then
+ . ~/.makepkg.conf
+ fi
+ if [[ -n $GPGKEY ]]; then
+ SIGNWITHKEY=(-u "${GPGKEY}")
+ fi
+ gpg --detach-sign --use-agent "${SIGNWITHKEY[@]}" "${@}" || die
+}
+
+oneTimeSetUp() {
+ local p
+ local d
+ local a
+ local arches=($(arches))
+ local pkgname
+ local pkgarch
+ local pkgversion
+ local build
+ pkgdir="$(mktemp -dt "${0##*/}.XXXXXXXXXX")"
+ cp -Lr "$(dirname "${BASH_SOURCE[0]}")"/../packages/* "${pkgdir}"
+ msg 'Building packages...'
+ for d in "${pkgdir}"/*; do
+ pushd $d >/dev/null
+ pkgname=($(. PKGBUILD; echo "${pkgname[@]}"))
+ pkgarch=($(. PKGBUILD; echo "${arch[@]}"))
+ pkgversion=$(. PKGBUILD; get_full_version)
+
+ build=true
+ for a in "${pkgarch[@]}"; do
+ for p in "${pkgname[@]}"; do
+ [ ! -f "${p}-${pkgversion}-${a}"${PKGEXT} ] && build=false
+ done
+ done
+
+ if ! "${build}"; then
+ if [ "${pkgarch[0]}" == 'any' ]; then
+ sudo libremakepkg || die 'libremakepkg failed'
+ else
+ for a in "${pkgarch[@]}"; do
+ if in_array "$a" "${arches[@]}"; then
+ 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
+ fi
+ popd >/dev/null
+ done
+}
+
+oneTimeTearDown() {
+ rm -rf "${pkgdir}"
+}
+
+setUp() {
+ local p
+ local pkg
+ local r
+ local a
+
+ [ -f "$(dirname "${BASH_SOURCE[0]}")/../../config.local" ] && die "$(dirname "${BASH_SOURCE[0]}")/../../config.local exists"
+ init_tmpdir
+ TMP="$(mktemp -dt "${0##*/}.XXXXXXXXXX")"
+ #msg "Using ${TMP}"
+
+ 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
+ mkdir -p "${TMP}/staging/${r}"
+ for a in "${ARCHES[@]}"; do
+ mkdir -p "${TMP}/ftp/${r}/os/${a}"
+ done
+ done
+ mkdir -p "${TMP}/ftp/${PKGPOOL}"
+ mkdir -p "${TMP}/ftp/${SRCPOOL}"
+
+ msg 'Creating svn repository...'
+ svnadmin create "${TMP}/svn-packages-repo"
+ arch_svn checkout -q "file://${TMP}/svn-packages-repo" "${TMP}/svn-packages-copy"
+
+ for p in "${pkgdir}"/*; do
+ pkg=${p##*/}
+ mkdir -p "${TMP}/svn-packages-copy/${pkg}"/{trunk,repos}
+ cp "${p}"/* "${TMP}/svn-packages-copy/${pkg}/trunk/"
+ arch_svn add -q "${TMP}/svn-packages-copy/${pkg}"
+ 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=($(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="${TMP}/svn-packages-copy"
+ PKGREPOS=("${PKGREPOS[@]}")
+ PKGPOOL="${PKGPOOL}"
+ SRCPOOL="${SRCPOOL}"
+ TESTING_REPO='testing'
+ STABLE_REPOS=('core' 'extra')
+ CLEANUP_DESTDIR="${TMP}/package-cleanup"
+ SOURCE_CLEANUP_DESTDIR="${TMP}/source-cleanup"
+ STAGING="${TMP}/staging"
+ TMPDIR="${TMP}/tmp"
+ CLEANUP_DRYRUN=false
+ SOURCE_CLEANUP_DRYRUN=false
+ REQUIRE_SIGNATURE=true
+eot
+ . "$(dirname "${BASH_SOURCE[0]}")/../../config"
+}
+
+tearDown() {
+ rm -rf "${TMP}"
+ rm -f "$(dirname "${BASH_SOURCE[0]}")/../../config.local"
+ echo
+}
+
+releasePackage() {
+ local repo=$1
+ local pkgbase=$2
+ local arch=$3
+ local a
+ local p
+ local pkgver
+ local pkgname
+
+ pushd "${TMP}/svn-packages-copy/${pkgbase}/trunk/" >/dev/null
+ xbs release "${repo}" "${arch}" >/dev/null 2>&1
+ pkgver=$(. PKGBUILD; get_full_version)
+ pkgname=($(. PKGBUILD; echo "${pkgname[@]}"))
+ popd >/dev/null
+ cp "${pkgdir}/${pkgbase}"/*-"${pkgver}-${arch}"${PKGEXT} "${STAGING}/${repo}/"
+
+ if "${REQUIRE_SIGNATURE}"; then
+ for a in "${arch[@]}"; do
+ for p in "${pkgname[@]}"; do
+ signpkg "${STAGING}/${repo}/${p}-${pkgver}-${a}"${PKGEXT}
+ done
+ done
+ fi
+}
+
+checkAnyPackageDB() {
+ local repo=$1
+ local pkg=$2
+ local arch
+ local db
+
+ [ -r "${FTP_BASE}/${PKGPOOL}/${pkg}" ] || fail "${PKGPOOL}/${pkg} not found"
+ if "${REQUIRE_SIGNATURE}"; then
+ [ -r "${FTP_BASE}/${PKGPOOL}/${pkg}.sig" ] || fail "${PKGPOOL}/${pkg}.sig not found"
+ fi
+
+ for arch in $(arches); 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}")" == "$(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")" == "$(readlink -e "${FTP_BASE}/${PKGPOOL}/${pkg}.sig")" ] \
+ || fail "${repo}/os/${arch}/${pkg}.sig does not link to ${PKGPOOL}/${pkg}.sig"
+ fi
+
+ for db in ${DBEXT} ${FILESEXT}; do
+ ( [ -r "${FTP_BASE}/${repo}/os/${arch}/${repo}${db%.tar.*}" ] \
+ && bsdtar -xf "${FTP_BASE}/${repo}/os/${arch}/${repo}${db%.tar.*}" -O | grep ${pkg} &>/dev/null) \
+ || fail "${pkg} not in ${repo}/os/${arch}/${repo}${db%.tar.*}"
+ done
+ done
+ [ -r "${STAGING}"/${repo}/${pkg} ] && fail "${repo}/${pkg} found in staging dir"
+ [ -r "${STAGING}"/${repo}/${pkg}.sig ] && fail "${repo}/${pkg}.sig found in staging dir"
+}
+
+checkAnyPackage() {
+ local repo=$1
+ local pkg=$2
+
+ checkAnyPackageDB $repo $pkg
+
+ local pkgbase=$(getpkgbase "${FTP_BASE}/${PKGPOOL}/${pkg}")
+ arch_svn up -q "${TMP}/svn-packages-copy/${pkgbase}"
+ [ -d "${TMP}/svn-packages-copy/${pkgbase}/repos/${repo}-any" ] \
+ || fail "svn-packages-copy/${pkgbase}/repos/${repo}-any does not exist"
+}
+
+checkPackageDB() {
+ local repo=$1
+ local pkg=$2
+ local arch=$3
+ local db
+
+ [ -r "${FTP_BASE}/${PKGPOOL}/${pkg}" ] || fail "${PKGPOOL}/${pkg} not found"
+ [ -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}")" == "$(readlink -e "${FTP_BASE}/${PKGPOOL}/${pkg}")" ] \
+ || fail "${repo}/os/${arch}/${pkg} does not link to ${PKGPOOL}/${pkg}"
+
+ if ${REQUIRE_SIGNATURE}; then
+ [ -r "${FTP_BASE}/${PKGPOOL}/${pkg}.sig" ] || fail "${PKGPOOL}/${pkg}.sig not found"
+ [ -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")" == "$(readlink -e "${FTP_BASE}/${PKGPOOL}/${pkg}.sig")" ] \
+ || fail "${repo}/os/${arch}/${pkg}.sig does not link to ${PKGPOOL}/${pkg}.sig"
+ fi
+
+ for db in ${DBEXT} ${FILESEXT}; do
+ ( [ -r "${FTP_BASE}/${repo}/os/${arch}/${repo}${db%.tar.*}" ] \
+ && bsdtar -xf "${FTP_BASE}/${repo}/os/${arch}/${repo}${db%.tar.*}" -O | grep ${pkg} &>/dev/null) \
+ || fail "${pkg} not in ${repo}/os/${arch}/${repo}${db%.tar.*}"
+ done
+}
+
+checkPackage() {
+ local repo=$1
+ local pkg=$2
+ local arch=$3
+
+ checkPackageDB $repo $pkg $arch
+
+ local pkgbase=$(getpkgbase "${FTP_BASE}/${PKGPOOL}/${pkg}")
+ arch_svn up -q "${TMP}/svn-packages-copy/${pkgbase}"
+ [ -d "${TMP}/svn-packages-copy/${pkgbase}/repos/${repo}-${arch}" ] \
+ || fail "svn-packages-copy/${pkgbase}/repos/${repo}-${arch} does not exist"
+}
+
+checkRemovedPackageDB() {
+ local repo=$1
+ local pkgbase=$2
+ local arch=$3
+ local db
+
+ for db in ${DBEXT} ${FILESEXT}; do
+ ( [ -r "${FTP_BASE}/${repo}/os/${arch}/${repo}${db%.tar.*}" ] \
+ && bsdtar -xf "${FTP_BASE}/${repo}/os/${arch}/${repo}${db%.tar.*}" -O | grep ${pkgbase} &>/dev/null) \
+ && fail "${pkgbase} should not be in ${repo}/os/${arch}/${repo}${db%.tar.*}"
+ done
+}
+
+checkRemovedPackage() {
+ local repo=$1
+ local pkgbase=$2
+ local arch=$3
+
+ checkRemovedPackageDB $repo $pkgbase $arch
+
+ arch_svn up -q "${TMP}/svn-packages-copy/${pkgbase}"
+ [ -d "${TMP}/svn-packages-copy/${pkgbase}/repos/${repo}-${arch}" ] \
+ && fail "svn-packages-copy/${pkgbase}/repos/${repo}-${arch} should not exist"
+}
+
+checkRemovedAnyPackageDB() {
+ local repo=$1
+ local pkgbase=$2
+ local arch
+ local db
+
+ for db in ${DBEXT} ${FILESEXT}; do
+ for arch in $(arches); do
+ ( [ -r "${FTP_BASE}/${repo}/os/${arch}/${repo}${db%.tar.*}" ] \
+ && bsdtar -xf "${FTP_BASE}/${repo}/os/${arch}/${repo}${db%.tar.*}" -O | grep ${pkgbase} &>/dev/null) \
+ && fail "${pkgbase} should not be in ${repo}/os/${arch}/${repo}${db%.tar.*}"
+ done
+ done
+}
+
+checkRemovedAnyPackage() {
+ local repo=$1
+ local pkgbase=$2
+
+ checkRemovedAnyPackageDB $repo $pkgbase
+
+ arch_svn up -q "${TMP}/svn-packages-copy/${pkgbase}"
+ [ -d "${TMP}/svn-packages-copy/${pkgbase}/repos/${repo}-any" ] \
+ && fail "svn-packages-copy/${pkgbase}/repos/${repo}-any should not exist"
+}
diff --git a/extra/xbs-lukeshu/test/lib/shunit2 b/extra/xbs-lukeshu/test/lib/shunit2
new file mode 100755
index 0000000..8862ffd
--- /dev/null
+++ b/extra/xbs-lukeshu/test/lib/shunit2
@@ -0,0 +1,1048 @@
+#! /bin/sh
+# $Id: shunit2 335 2011-05-01 20:10:33Z kate.ward@forestent.com $
+# vim:et:ft=sh:sts=2:sw=2
+#
+# Copyright 2008 Kate Ward. All Rights Reserved.
+# Released under the LGPL (GNU Lesser General Public License)
+#
+# shUnit2 -- Unit testing framework for Unix shell scripts.
+# http://code.google.com/p/shunit2/
+#
+# Author: kate.ward@forestent.com (Kate Ward)
+#
+# shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is
+# based on the popular JUnit unit testing framework for Java.
+
+# return if shunit already loaded
+[ -n "${SHUNIT_VERSION:-}" ] && exit 0
+
+SHUNIT_VERSION='2.1.6'
+
+SHUNIT_TRUE=0
+SHUNIT_FALSE=1
+SHUNIT_ERROR=2
+
+# enable strict mode by default
+SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}}
+
+_shunit_warn() { echo "shunit2:WARN $@" >&2; }
+_shunit_error() { echo "shunit2:ERROR $@" >&2; }
+_shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; }
+
+# specific shell checks
+if [ -n "${ZSH_VERSION:-}" ]; then
+ setopt |grep "^shwordsplit$" >/dev/null
+ if [ $? -ne ${SHUNIT_TRUE} ]; then
+ _shunit_fatal 'zsh shwordsplit option is required for proper operation'
+ fi
+ if [ -z "${SHUNIT_PARENT:-}" ]; then
+ _shunit_fatal "zsh does not pass \$0 through properly. please declare \
+\"SHUNIT_PARENT=\$0\" before calling shUnit2"
+ fi
+fi
+
+#
+# constants
+#
+
+__SHUNIT_ASSERT_MSG_PREFIX='ASSERT:'
+__SHUNIT_MODE_SOURCED='sourced'
+__SHUNIT_MODE_STANDALONE='standalone'
+__SHUNIT_PARENT=${SHUNIT_PARENT:-$0}
+
+# set the constants readonly
+shunit_constants_=`set |grep '^__SHUNIT_' |cut -d= -f1`
+echo "${shunit_constants_}" |grep '^Binary file' >/dev/null && \
+ shunit_constants_=`set |grep -a '^__SHUNIT_' |cut -d= -f1`
+for shunit_constant_ in ${shunit_constants_}; do
+ shunit_ro_opts_=''
+ case ${ZSH_VERSION:-} in
+ '') ;; # this isn't zsh
+ [123].*) ;; # early versions (1.x, 2.x, 3.x)
+ *) shunit_ro_opts_='-g' ;; # all later versions. declare readonly globally
+ esac
+ readonly ${shunit_ro_opts_} ${shunit_constant_}
+done
+unset shunit_constant_ shunit_constants_ shunit_ro_opts_
+
+# variables
+__shunit_lineno='' # line number of executed test
+__shunit_mode=${__SHUNIT_MODE_SOURCED} # operating mode
+__shunit_reportGenerated=${SHUNIT_FALSE} # is report generated
+__shunit_script='' # filename of unittest script (standalone mode)
+__shunit_skip=${SHUNIT_FALSE} # is skipping enabled
+__shunit_suite='' # suite of tests to execute
+
+# counts of tests
+__shunit_testSuccess=${SHUNIT_TRUE}
+__shunit_testsTotal=0
+__shunit_testsPassed=0
+__shunit_testsFailed=0
+
+# counts of asserts
+__shunit_assertsTotal=0
+__shunit_assertsPassed=0
+__shunit_assertsFailed=0
+__shunit_assertsSkipped=0
+
+# macros
+_SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi'
+
+#-----------------------------------------------------------------------------
+# assert functions
+#
+
+# Assert that two values are equal to one another.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertEquals()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertEquals() requires two or three arguments; $# given"
+ _shunit_error "1: ${1:+$1} 2: ${2:+$2} 3: ${3:+$3}${4:+ 4: $4}"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_expected_=$1
+ shunit_actual_=$2
+
+ shunit_return=${SHUNIT_TRUE}
+ if [ "${shunit_expected_}" = "${shunit_actual_}" ]; then
+ _shunit_assertPass
+ else
+ failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}"
+ shunit_return=${SHUNIT_FALSE}
+ fi
+
+ unset shunit_message_ shunit_expected_ shunit_actual_
+ return ${shunit_return}
+}
+_ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"'
+
+# Assert that two values are not equal to one another.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotEquals()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertNotEquals() requires two or three arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_expected_=$1
+ shunit_actual_=$2
+
+ shunit_return=${SHUNIT_TRUE}
+ if [ "${shunit_expected_}" != "${shunit_actual_}" ]; then
+ _shunit_assertPass
+ else
+ failSame "${shunit_message_}" "$@"
+ shunit_return=${SHUNIT_FALSE}
+ fi
+
+ unset shunit_message_ shunit_expected_ shunit_actual_
+ return ${shunit_return}
+}
+_ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"'
+
+# Assert that a value is null (i.e. an empty string)
+#
+# Args:
+# message: string: failure message [optional]
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNull()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "assertNull() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ assertTrue "${shunit_message_}" "[ -z '$1' ]"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+_ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"'
+
+# Assert that a value is not null (i.e. a non-empty string)
+#
+# Args:
+# message: string: failure message [optional]
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotNull()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null
+ _shunit_error "assertNotNull() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"`
+ test -n "${shunit_actual_}"
+ assertTrue "${shunit_message_}" $?
+ shunit_return=$?
+
+ unset shunit_actual_ shunit_message_
+ return ${shunit_return}
+}
+_ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"'
+
+# Assert that two values are the same (i.e. equal to one another).
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertSame()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertSame() requires two or three arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ assertEquals "${shunit_message_}" "$1" "$2"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+_ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"'
+
+# Assert that two values are not the same (i.e. not equal to one another).
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertNotSame()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "assertNotSame() requires two or three arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_:-}$1"
+ shift
+ fi
+ assertNotEquals "${shunit_message_}" "$1" "$2"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+_ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"'
+
+# Assert that a value or shell test condition is true.
+#
+# In shell, a value of 0 is true and a non-zero value is false. Any integer
+# value passed can thereby be tested.
+#
+# Shell supports much more complicated tests though, and a means to support
+# them was needed. As such, this function tests that conditions are true or
+# false through evaluation rather than just looking for a true or false.
+#
+# The following test will succeed:
+# assertTrue 0
+# assertTrue "[ 34 -gt 23 ]"
+# The folloing test will fail with a message:
+# assertTrue 123
+# assertTrue "test failed" "[ -r '/non/existant/file' ]"
+#
+# Args:
+# message: string: failure message [optional]
+# condition: string: integer value or shell conditional statement
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertTrue()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -gt 2 ]; then
+ _shunit_error "assertTrue() takes one two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_condition_=$1
+
+ # see if condition is an integer, i.e. a return value
+ shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
+ shunit_return=${SHUNIT_TRUE}
+ if [ -z "${shunit_condition_}" ]; then
+ # null condition
+ shunit_return=${SHUNIT_FALSE}
+ elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
+ then
+ # possible return value. treating 0 as true, and non-zero as false.
+ [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE}
+ else
+ # (hopefully) a condition
+ ( eval ${shunit_condition_} ) >/dev/null 2>&1
+ [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE}
+ fi
+
+ # record the test
+ if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
+ _shunit_assertPass
+ else
+ _shunit_assertFail "${shunit_message_}"
+ fi
+
+ unset shunit_message_ shunit_condition_ shunit_match_
+ return ${shunit_return}
+}
+_ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"'
+
+# Assert that a value or shell test condition is false.
+#
+# In shell, a value of 0 is true and a non-zero value is false. Any integer
+# value passed can thereby be tested.
+#
+# Shell supports much more complicated tests though, and a means to support
+# them was needed. As such, this function tests that conditions are true or
+# false through evaluation rather than just looking for a true or false.
+#
+# The following test will succeed:
+# assertFalse 1
+# assertFalse "[ 'apples' = 'oranges' ]"
+# The folloing test will fail with a message:
+# assertFalse 0
+# assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]"
+#
+# Args:
+# message: string: failure message [optional]
+# condition: string: integer value or shell conditional statement
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+assertFalse()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -lt 1 -o $# -gt 2 ]; then
+ _shunit_error "assertFalse() quires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 2 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_condition_=$1
+
+ # see if condition is an integer, i.e. a return value
+ shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'`
+ shunit_return=${SHUNIT_TRUE}
+ if [ -z "${shunit_condition_}" ]; then
+ # null condition
+ shunit_return=${SHUNIT_FALSE}
+ elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ]
+ then
+ # possible return value. treating 0 as true, and non-zero as false.
+ [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE}
+ else
+ # (hopefully) a condition
+ ( eval ${shunit_condition_} ) >/dev/null 2>&1
+ [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE}
+ fi
+
+ # record the test
+ if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then
+ _shunit_assertPass
+ else
+ _shunit_assertFail "${shunit_message_}"
+ fi
+
+ unset shunit_message_ shunit_condition_ shunit_match_
+ return ${shunit_return}
+}
+_ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"'
+
+#-----------------------------------------------------------------------------
+# failure functions
+#
+
+# Records a test failure.
+#
+# Args:
+# message: string: failure message [optional]
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+fail()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -gt 1 ]; then
+ _shunit_error "fail() requires zero or one arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 1 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+
+ _shunit_assertFail "${shunit_message_}"
+
+ unset shunit_message_
+ return ${SHUNIT_FALSE}
+}
+_FAIL_='eval fail --lineno "${LINENO:-}"'
+
+# Records a test failure, stating two values were not equal.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failNotEquals()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "failNotEquals() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ shunit_expected_=$1
+ shunit_actual_=$2
+
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>"
+
+ unset shunit_message_ shunit_expected_ shunit_actual_
+ return ${SHUNIT_FALSE}
+}
+_FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"'
+
+# Records a test failure, stating two values should have been the same.
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failSame()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "failSame() requires two or three arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+
+ _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same"
+
+ unset shunit_message_
+ return ${SHUNIT_FALSE}
+}
+_FAIL_SAME_='eval failSame --lineno "${LINENO:-}"'
+
+# Records a test failure, stating two values were not equal.
+#
+# This is functionally equivalent to calling failNotEquals().
+#
+# Args:
+# message: string: failure message [optional]
+# expected: string: expected value
+# actual: string: actual value
+# Returns:
+# integer: success (TRUE/FALSE/ERROR constant)
+failNotSame()
+{
+ ${_SHUNIT_LINENO_}
+ if [ $# -lt 2 -o $# -gt 3 ]; then
+ _shunit_error "failNotEquals() requires one or two arguments; $# given"
+ return ${SHUNIT_ERROR}
+ fi
+ _shunit_shouldSkip && return ${SHUNIT_TRUE}
+
+ shunit_message_=${__shunit_lineno}
+ if [ $# -eq 3 ]; then
+ shunit_message_="${shunit_message_}$1"
+ shift
+ fi
+ failNotEquals "${shunit_message_}" "$1" "$2"
+ shunit_return=$?
+
+ unset shunit_message_
+ return ${shunit_return}
+}
+_FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"'
+
+#-----------------------------------------------------------------------------
+# skipping functions
+#
+
+# Force remaining assert and fail functions to be "skipped".
+#
+# This function forces the remaining assert and fail functions to be "skipped",
+# i.e. they will have no effect. Each function skipped will be recorded so that
+# the total of asserts and fails will not be altered.
+#
+# Args:
+# None
+startSkipping()
+{
+ __shunit_skip=${SHUNIT_TRUE}
+}
+
+# Resume the normal recording behavior of assert and fail calls.
+#
+# Args:
+# None
+endSkipping()
+{
+ __shunit_skip=${SHUNIT_FALSE}
+}
+
+# Returns the state of assert and fail call skipping.
+#
+# Args:
+# None
+# Returns:
+# boolean: (TRUE/FALSE constant)
+isSkipping()
+{
+ return ${__shunit_skip}
+}
+
+#-----------------------------------------------------------------------------
+# suite functions
+#
+
+# Stub. This function should contains all unit test calls to be made.
+#
+# DEPRECATED (as of 2.1.0)
+#
+# This function can be optionally overridden by the user in their test suite.
+#
+# If this function exists, it will be called when shunit2 is sourced. If it
+# does not exist, shunit2 will search the parent script for all functions
+# beginning with the word 'test', and they will be added dynamically to the
+# test suite.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Adds a function name to the list of tests schedule for execution.
+#
+# This function should only be called from within the suite() function.
+#
+# Args:
+# function: string: name of a function to add to current unit test suite
+suite_addTest()
+{
+ shunit_func_=${1:-}
+
+ __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}"
+ __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1`
+
+ unset shunit_func_
+}
+
+# Stub. This function will be called once before any tests are run.
+#
+# Common one-time environment preparation tasks shared by all tests can be
+# defined here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Stub. This function will be called once after all tests are finished.
+#
+# Common one-time environment cleanup tasks shared by all tests can be defined
+# here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+# Stub. This function will be called before each test is run.
+#
+# Common environment preparation tasks shared by all tests can be defined here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#setUp() { :; }
+
+# Note: see _shunit_mktempFunc() for actual implementation
+# Stub. This function will be called after each test is run.
+#
+# Common environment cleanup tasks shared by all tests can be defined here.
+#
+# This function should be overridden by the user in their unit test suite.
+# Note: see _shunit_mktempFunc() for actual implementation
+#
+# Args:
+# None
+#tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION
+
+#------------------------------------------------------------------------------
+# internal shUnit2 functions
+#
+
+# Create a temporary directory to store various run-time files in.
+#
+# This function is a cross-platform temporary directory creation tool. Not all
+# OSes have the mktemp function, so one is included here.
+#
+# Args:
+# None
+# Outputs:
+# string: the temporary directory that was created
+_shunit_mktempDir()
+{
+ # try the standard mktemp function
+ ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return
+
+ # the standard mktemp didn't work. doing our own.
+ if [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then
+ _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 </dev/urandom \
+ |sed 's/^[^0-9a-f]*//'`
+ elif [ -n "${RANDOM:-}" ]; then
+ # $RANDOM works
+ _shunit_random_=${RANDOM}${RANDOM}${RANDOM}$$
+ else
+ # $RANDOM doesn't work
+ _shunit_date_=`date '+%Y%m%d%H%M%S'`
+ _shunit_random_=`expr ${_shunit_date_} / $$`
+ fi
+
+ _shunit_tmpDir_="${TMPDIR:-/tmp}/shunit.${_shunit_random_}"
+ ( umask 077 && mkdir "${_shunit_tmpDir_}" ) || \
+ _shunit_fatal 'could not create temporary directory! exiting'
+
+ echo ${_shunit_tmpDir_}
+ unset _shunit_date_ _shunit_random_ _shunit_tmpDir_
+}
+
+# This function is here to work around issues in Cygwin.
+#
+# Args:
+# None
+_shunit_mktempFunc()
+{
+ for _shunit_func_ in oneTimeSetUp oneTimeTearDown setUp tearDown suite noexec
+ do
+ _shunit_file_="${__shunit_tmpDir}/${_shunit_func_}"
+ cat <<EOF >"${_shunit_file_}"
+#! /bin/sh
+exit ${SHUNIT_TRUE}
+EOF
+ chmod +x "${_shunit_file_}"
+ done
+
+ unset _shunit_file_
+}
+
+# Final cleanup function to leave things as we found them.
+#
+# Besides removing the temporary directory, this function is in charge of the
+# final exit code of the unit test. The exit code is based on how the script
+# was ended (e.g. normal exit, or via Ctrl-C).
+#
+# Args:
+# name: string: name of the trap called (specified when trap defined)
+_shunit_cleanup()
+{
+ _shunit_name_=$1
+
+ case ${_shunit_name_} in
+ EXIT) _shunit_signal_=0 ;;
+ INT) _shunit_signal_=2 ;;
+ TERM) _shunit_signal_=15 ;;
+ *)
+ _shunit_warn "unrecognized trap value (${_shunit_name_})"
+ _shunit_signal_=0
+ ;;
+ esac
+
+ # do our work
+ rm -fr "${__shunit_tmpDir}"
+
+ # exit for all non-EXIT signals
+ if [ ${_shunit_name_} != 'EXIT' ]; then
+ _shunit_warn "trapped and now handling the (${_shunit_name_}) signal"
+ # disable EXIT trap
+ trap 0
+ # add 128 to signal and exit
+ exit `expr ${_shunit_signal_} + 128`
+ elif [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ] ; then
+ _shunit_assertFail 'Unknown failure encountered running a test'
+ _shunit_generateReport
+ exit ${SHUNIT_ERROR}
+ fi
+
+ unset _shunit_name_ _shunit_signal_
+}
+
+# The actual running of the tests happens here.
+#
+# Args:
+# None
+_shunit_execSuite()
+{
+ for _shunit_test_ in ${__shunit_suite}; do
+ __shunit_testSuccess=${SHUNIT_TRUE}
+
+ # disable skipping
+ endSkipping
+
+ # execute the per-test setup function
+ setUp
+
+ # execute the test
+ echo "${_shunit_test_}"
+ eval ${_shunit_test_}
+
+ # execute the per-test tear-down function
+ tearDown
+
+ # update stats
+ if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then
+ __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1`
+ else
+ __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1`
+ fi
+ done
+
+ unset _shunit_test_
+}
+
+# Generates the user friendly report with appropriate OK/FAILED message.
+#
+# Args:
+# None
+# Output:
+# string: the report of successful and failed tests, as well as totals.
+_shunit_generateReport()
+{
+ _shunit_ok_=${SHUNIT_TRUE}
+
+ # if no exit code was provided one, determine an appropriate one
+ [ ${__shunit_testsFailed} -gt 0 \
+ -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \
+ && _shunit_ok_=${SHUNIT_FALSE}
+
+ echo
+ if [ ${__shunit_testsTotal} -eq 1 ]; then
+ echo "Ran ${__shunit_testsTotal} test."
+ else
+ echo "Ran ${__shunit_testsTotal} tests."
+ fi
+
+ _shunit_failures_=''
+ _shunit_skipped_=''
+ [ ${__shunit_assertsFailed} -gt 0 ] \
+ && _shunit_failures_="failures=${__shunit_assertsFailed}"
+ [ ${__shunit_assertsSkipped} -gt 0 ] \
+ && _shunit_skipped_="skipped=${__shunit_assertsSkipped}"
+
+ if [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then
+ _shunit_msg_='OK'
+ [ -n "${_shunit_skipped_}" ] \
+ && _shunit_msg_="${_shunit_msg_} (${_shunit_skipped_})"
+ else
+ _shunit_msg_="FAILED (${_shunit_failures_}"
+ [ -n "${_shunit_skipped_}" ] \
+ && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}"
+ _shunit_msg_="${_shunit_msg_})"
+ fi
+
+ echo
+ echo ${_shunit_msg_}
+ __shunit_reportGenerated=${SHUNIT_TRUE}
+
+ unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_
+}
+
+# Test for whether a function should be skipped.
+#
+# Args:
+# None
+# Returns:
+# boolean: whether the test should be skipped (TRUE/FALSE constant)
+_shunit_shouldSkip()
+{
+ [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE}
+ _shunit_assertSkip
+}
+
+# Records a successful test.
+#
+# Args:
+# None
+_shunit_assertPass()
+{
+ __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1`
+ __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
+}
+
+# Records a test failure.
+#
+# Args:
+# message: string: failure message to provide user
+_shunit_assertFail()
+{
+ _shunit_msg_=$1
+
+ __shunit_testSuccess=${SHUNIT_FALSE}
+ __shunit_assertsFailed=`expr ${__shunit_assertsFailed} + 1`
+ __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
+ echo "${__SHUNIT_ASSERT_MSG_PREFIX}${_shunit_msg_}"
+
+ unset _shunit_msg_
+}
+
+# Records a skipped test.
+#
+# Args:
+# None
+_shunit_assertSkip()
+{
+ __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1`
+ __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1`
+}
+
+# Prepare a script filename for sourcing.
+#
+# Args:
+# script: string: path to a script to source
+# Returns:
+# string: filename prefixed with ./ (if necessary)
+_shunit_prepForSourcing()
+{
+ _shunit_script_=$1
+ case "${_shunit_script_}" in
+ /*|./*) echo "${_shunit_script_}" ;;
+ *) echo "./${_shunit_script_}" ;;
+ esac
+ unset _shunit_script_
+}
+
+# Escape a character in a string.
+#
+# Args:
+# c: string: unescaped character
+# s: string: to escape character in
+# Returns:
+# string: with escaped character(s)
+_shunit_escapeCharInStr()
+{
+ [ -n "$2" ] || return # no point in doing work on an empty string
+
+ # Note: using shorter variable names to prevent conflicts with
+ # _shunit_escapeCharactersInString().
+ _shunit_c_=$1
+ _shunit_s_=$2
+
+
+ # escape the character
+ echo ''${_shunit_s_}'' |sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g'
+
+ unset _shunit_c_ _shunit_s_
+}
+
+# Escape a character in a string.
+#
+# Args:
+# str: string: to escape characters in
+# Returns:
+# string: with escaped character(s)
+_shunit_escapeCharactersInString()
+{
+ [ -n "$1" ] || return # no point in doing work on an empty string
+
+ _shunit_str_=$1
+
+ # Note: using longer variable names to prevent conflicts with
+ # _shunit_escapeCharInStr().
+ for _shunit_char_ in '"' '$' "'" '`'; do
+ _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"`
+ done
+
+ echo "${_shunit_str_}"
+ unset _shunit_char_ _shunit_str_
+}
+
+# Extract list of functions to run tests against.
+#
+# Args:
+# script: string: name of script to extract functions from
+# Returns:
+# string: of function names
+_shunit_extractTestFunctions()
+{
+ _shunit_script_=$1
+
+ # extract the lines with test function names, strip of anything besides the
+ # function name, and output everything on a single line.
+ _shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)'
+ egrep "${_shunit_regex_}" "${_shunit_script_}" \
+ |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \
+ |xargs
+
+ unset _shunit_regex_ _shunit_script_
+}
+
+#------------------------------------------------------------------------------
+# main
+#
+
+# determine the operating mode
+if [ $# -eq 0 ]; then
+ __shunit_script=${__SHUNIT_PARENT}
+ __shunit_mode=${__SHUNIT_MODE_SOURCED}
+else
+ __shunit_script=$1
+ [ -r "${__shunit_script}" ] || \
+ _shunit_fatal "unable to read from ${__shunit_script}"
+ __shunit_mode=${__SHUNIT_MODE_STANDALONE}
+fi
+
+# create a temporary storage location
+__shunit_tmpDir=`_shunit_mktempDir`
+
+# provide a public temporary directory for unit test scripts
+# TODO(kward): document this
+SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp"
+mkdir "${SHUNIT_TMPDIR}"
+
+# setup traps to clean up after ourselves
+trap '_shunit_cleanup EXIT' 0
+trap '_shunit_cleanup INT' 2
+trap '_shunit_cleanup TERM' 15
+
+# create phantom functions to work around issues with Cygwin
+_shunit_mktempFunc
+PATH="${__shunit_tmpDir}:${PATH}"
+
+# make sure phantom functions are executable. this will bite if /tmp (or the
+# current $TMPDIR) points to a path on a partition that was mounted with the
+# 'noexec' option. the noexec command was created with _shunit_mktempFunc().
+noexec 2>/dev/null || _shunit_fatal \
+ 'please declare TMPDIR with path on partition with exec permission'
+
+# we must manually source the tests in standalone mode
+if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then
+ . "`_shunit_prepForSourcing \"${__shunit_script}\"`"
+fi
+
+# execute the oneTimeSetUp function (if it exists)
+oneTimeSetUp
+
+# execute the suite function defined in the parent test script
+# deprecated as of 2.1.0
+suite
+
+# if no suite function was defined, dynamically build a list of functions
+if [ -z "${__shunit_suite}" ]; then
+ shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"`
+ for shunit_func_ in ${shunit_funcs_}; do
+ suite_addTest ${shunit_func_}
+ done
+fi
+unset shunit_func_ shunit_funcs_
+
+# execute the tests
+_shunit_execSuite
+
+# execute the oneTimeTearDown function (if it exists)
+oneTimeTearDown
+
+# generate the report
+_shunit_generateReport
+
+# that's it folks
+[ ${__shunit_testsFailed} -eq 0 ]
+exit $?
diff --git a/extra/xbs-lukeshu/test/packages/pkg-any-a/PKGBUILD b/extra/xbs-lukeshu/test/packages/pkg-any-a/PKGBUILD
new file mode 100644
index 0000000..8749a35
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-any-a/PKGBUILD
@@ -0,0 +1,12 @@
+pkgname=pkg-any-a
+pkgver=1
+pkgrel=1
+pkgdesc="A package called ${pkgname}"
+arch=('any')
+url='http://www.archlinux.org/'
+license=('GPL')
+
+package() {
+ install -d -m755 ${pkgdir}/usr/share/${pkgname}
+ echo 'test' > ${pkgdir}/usr/share/${pkgname}/test
+}
diff --git a/extra/xbs-lukeshu/test/packages/pkg-any-b/PKGBUILD b/extra/xbs-lukeshu/test/packages/pkg-any-b/PKGBUILD
new file mode 100644
index 0000000..e6a0498
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-any-b/PKGBUILD
@@ -0,0 +1,12 @@
+pkgname=pkg-any-b
+pkgver=1
+pkgrel=1
+pkgdesc="A package called ${pkgname}"
+arch=('any')
+url='http://www.archlinux.org/'
+license=('GPL')
+
+package() {
+ install -d -m755 ${pkgdir}/usr/share/${pkgname}
+ echo 'test' > ${pkgdir}/usr/share/${pkgname}/test
+}
diff --git a/extra/xbs-lukeshu/test/packages/pkg-simple-a/Makefile b/extra/xbs-lukeshu/test/packages/pkg-simple-a/Makefile
new file mode 120000
index 0000000..50be211
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-simple-a/Makefile
@@ -0,0 +1 @@
+../../src/Makefile \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/packages/pkg-simple-a/PKGBUILD b/extra/xbs-lukeshu/test/packages/pkg-simple-a/PKGBUILD
new file mode 100644
index 0000000..953ecfa
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-simple-a/PKGBUILD
@@ -0,0 +1,22 @@
+pkgname=pkg-simple-a
+pkgver=1
+pkgrel=1
+pkgdesc="A package called ${pkgname}"
+arch=('i686' 'x86_64')
+url='http://www.archlinux.org/'
+license=('GPL')
+depends=('glibc')
+makedepends=('gcc')
+source=('Makefile' 'test.c')
+md5sums=('c6cb8dcc86253355fed559416d0c8dcf'
+ '3c1e4279feb678fd9cabaccdb28e40d0')
+
+build() {
+ cd ${srcdir}
+ make
+}
+
+package() {
+ cd ${srcdir}
+ make install DESTDIR=${pkgdir} DESTBIN=${pkgname}
+}
diff --git a/extra/xbs-lukeshu/test/packages/pkg-simple-a/test.c b/extra/xbs-lukeshu/test/packages/pkg-simple-a/test.c
new file mode 120000
index 0000000..ed5b5ac
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-simple-a/test.c
@@ -0,0 +1 @@
+../../src/test.c \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/packages/pkg-simple-b/Makefile b/extra/xbs-lukeshu/test/packages/pkg-simple-b/Makefile
new file mode 120000
index 0000000..50be211
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-simple-b/Makefile
@@ -0,0 +1 @@
+../../src/Makefile \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/packages/pkg-simple-b/PKGBUILD b/extra/xbs-lukeshu/test/packages/pkg-simple-b/PKGBUILD
new file mode 100644
index 0000000..95ffd09
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-simple-b/PKGBUILD
@@ -0,0 +1,22 @@
+pkgname=pkg-simple-b
+pkgver=1
+pkgrel=1
+pkgdesc="A package called ${pkgname}"
+arch=('i686' 'x86_64')
+url='http://www.archlinux.org/'
+license=('GPL')
+depends=('glibc')
+makedepends=('gcc')
+source=('Makefile' 'test.c')
+md5sums=('c6cb8dcc86253355fed559416d0c8dcf'
+ '3c1e4279feb678fd9cabaccdb28e40d0')
+
+build() {
+ cd ${srcdir}
+ make
+}
+
+package() {
+ cd ${srcdir}
+ make install DESTDIR=${pkgdir} DESTBIN=${pkgname}
+}
diff --git a/extra/xbs-lukeshu/test/packages/pkg-simple-b/test.c b/extra/xbs-lukeshu/test/packages/pkg-simple-b/test.c
new file mode 120000
index 0000000..ed5b5ac
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-simple-b/test.c
@@ -0,0 +1 @@
+../../src/test.c \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/packages/pkg-simple-epoch/Makefile b/extra/xbs-lukeshu/test/packages/pkg-simple-epoch/Makefile
new file mode 120000
index 0000000..50be211
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-simple-epoch/Makefile
@@ -0,0 +1 @@
+../../src/Makefile \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/packages/pkg-simple-epoch/PKGBUILD b/extra/xbs-lukeshu/test/packages/pkg-simple-epoch/PKGBUILD
new file mode 100644
index 0000000..eebe2bd
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-simple-epoch/PKGBUILD
@@ -0,0 +1,23 @@
+pkgname=pkg-simple-epoch
+pkgver=1
+pkgrel=1
+epoch=1
+pkgdesc="A package called ${pkgname}"
+arch=('i686' 'x86_64')
+url='http://www.archlinux.org/'
+license=('GPL')
+depends=('glibc')
+makedepends=('gcc')
+source=('Makefile' 'test.c')
+md5sums=('c6cb8dcc86253355fed559416d0c8dcf'
+ '3c1e4279feb678fd9cabaccdb28e40d0')
+
+build() {
+ cd ${srcdir}
+ make
+}
+
+package() {
+ cd ${srcdir}
+ make install DESTDIR=${pkgdir} DESTBIN=${pkgname}
+}
diff --git a/extra/xbs-lukeshu/test/packages/pkg-simple-epoch/test.c b/extra/xbs-lukeshu/test/packages/pkg-simple-epoch/test.c
new file mode 120000
index 0000000..ed5b5ac
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-simple-epoch/test.c
@@ -0,0 +1 @@
+../../src/test.c \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/packages/pkg-split-a/Makefile b/extra/xbs-lukeshu/test/packages/pkg-split-a/Makefile
new file mode 120000
index 0000000..50be211
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-split-a/Makefile
@@ -0,0 +1 @@
+../../src/Makefile \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/packages/pkg-split-a/PKGBUILD b/extra/xbs-lukeshu/test/packages/pkg-split-a/PKGBUILD
new file mode 100644
index 0000000..e941976
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-split-a/PKGBUILD
@@ -0,0 +1,28 @@
+pkgbase=pkg-split-a
+pkgname=('pkg-split-a1' 'pkg-split-a2')
+pkgver=1
+pkgrel=1
+pkgdesc="A split package called ${pkgbase}"
+arch=('i686' 'x86_64')
+url='http://www.archlinux.org/'
+license=('GPL')
+depends=('glibc')
+makedepends=('gcc')
+source=('Makefile' 'test.c')
+md5sums=('c6cb8dcc86253355fed559416d0c8dcf'
+ '3c1e4279feb678fd9cabaccdb28e40d0')
+
+build() {
+ cd ${srcdir}
+ make
+}
+
+package_pkg-split-a1() {
+ cd ${srcdir}
+ make install DESTDIR=${pkgdir} DESTBIN=${pkgname[0]}
+}
+
+package_pkg-split-a2() {
+ cd ${srcdir}
+ make install DESTDIR=${pkgdir} DESTBIN=${pkgname[1]}
+}
diff --git a/extra/xbs-lukeshu/test/packages/pkg-split-a/test.c b/extra/xbs-lukeshu/test/packages/pkg-split-a/test.c
new file mode 120000
index 0000000..ed5b5ac
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-split-a/test.c
@@ -0,0 +1 @@
+../../src/test.c \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/packages/pkg-split-b/Makefile b/extra/xbs-lukeshu/test/packages/pkg-split-b/Makefile
new file mode 120000
index 0000000..50be211
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-split-b/Makefile
@@ -0,0 +1 @@
+../../src/Makefile \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/packages/pkg-split-b/PKGBUILD b/extra/xbs-lukeshu/test/packages/pkg-split-b/PKGBUILD
new file mode 100644
index 0000000..6ddbc45
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-split-b/PKGBUILD
@@ -0,0 +1,29 @@
+pkgbase=pkg-split-b
+pkgname=('pkg-split-b1' 'pkg-split-b2')
+pkgver=1
+pkgrel=1
+pkgdesc="A split package called ${pkgbase}"
+arch=('i686' 'x86_64')
+url='http://www.archlinux.org/'
+license=('GPL')
+
+depends=('glibc')
+makedepends=('gcc')
+source=('Makefile' 'test.c')
+md5sums=('c6cb8dcc86253355fed559416d0c8dcf'
+ '3c1e4279feb678fd9cabaccdb28e40d0')
+
+build() {
+ cd ${srcdir}
+ make
+}
+
+package_pkg-split-b1() {
+ cd ${srcdir}
+ make install DESTDIR=${pkgdir} DESTBIN=${pkgname[0]}
+}
+
+package_pkg-split-b2() {
+ cd ${srcdir}
+ make install DESTDIR=${pkgdir} DESTBIN=${pkgname[1]}
+}
diff --git a/extra/xbs-lukeshu/test/packages/pkg-split-b/test.c b/extra/xbs-lukeshu/test/packages/pkg-split-b/test.c
new file mode 120000
index 0000000..ed5b5ac
--- /dev/null
+++ b/extra/xbs-lukeshu/test/packages/pkg-split-b/test.c
@@ -0,0 +1 @@
+../../src/test.c \ No newline at end of file
diff --git a/extra/xbs-lukeshu/test/rsync_output_sample b/extra/xbs-lukeshu/test/rsync_output_sample
new file mode 100644
index 0000000..72d9cd0
--- /dev/null
+++ b/extra/xbs-lukeshu/test/rsync_output_sample
@@ -0,0 +1,14 @@
+dr-xr-sr-x 4096 2010/09/11 11:37:10 .
+-rw-r--r-- 11 2011/02/08 00:00:01 lastsync
+drwxrwxr-x 15 2010/09/11 11:28:50 community-staging
+drwxrwxr-x 30 2010/09/11 11:28:50 community-staging/os
+drwxrwxr-x 8192 2011/02/07 17:00:01 community-staging/os/i686
+lrwxrwxrwx 52 2010/12/23 16:51:01 community-staging/os/i686/alex-2.3.4-1-i686.pkg.tar.xz -> ../../../pool/community/alex-2.3.4-1-i686.pkg.tar.xz
+lrwxrwxrwx 27 2011/02/07 14:02:54 community-staging/os/i686/community-staging.db -> community-staging.db.tar.gz
+-rw-rw-r-- 2237 2011/02/07 14:02:54 community-staging/os/i686/community-staging.db.tar.gz
+-rw-rw-r-- 3209 2011/02/07 14:00:13 community-staging/os/i686/community-staging.db.tar.gz.old
+drwxrwxr-x 15 2009/07/22 15:07:56 community
+drwxrwxr-x 40 2009/08/04 15:57:42 community/os
+drwxrwsr-x 36864 2011/02/03 05:00:01 community/os/any
+-rw-rw-r-- 303336 2010/07/16 10:06:28 community/os/any/any2dvd-0.34-4-any.pkg.tar.xz
+-rw-rw-r-- 221664 2010/03/28 15:55:48 community/os/x86_64/gmime22-2.2.26-1-x86_64.pkg.tar.xz
diff --git a/extra/xbs-lukeshu/test/runTest b/extra/xbs-lukeshu/test/runTest
new file mode 100755
index 0000000..b8713d8
--- /dev/null
+++ b/extra/xbs-lukeshu/test/runTest
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+. "$(dirname ${BASH_SOURCE[0]})/lib/common.inc"
+
+for t in "$(dirname ${BASH_SOURCE[0]})/test.d/"*.sh; do
+ l=$(basename ${t} .sh)
+ if [ -x ${t} ]; then
+ msg "Running test '${l}'"
+ ${t}
+ [ $? -ne 0 ] && die "Test '${l}' failed"
+ echo -e "\n\n\n"
+ else
+ warning "Skipping test ${l}"
+ fi
+done
diff --git a/extra/xbs-lukeshu/test/src/Makefile b/extra/xbs-lukeshu/test/src/Makefile
new file mode 100644
index 0000000..105b730
--- /dev/null
+++ b/extra/xbs-lukeshu/test/src/Makefile
@@ -0,0 +1,5 @@
+all:
+ gcc $(CFLAGS) -o test test.c
+
+install:
+ install -D -m755 test $(DESTDIR)/usr/bin/$(DESTBIN)
diff --git a/extra/xbs-lukeshu/test/src/test.c b/extra/xbs-lukeshu/test/src/test.c
new file mode 100644
index 0000000..a661689
--- /dev/null
+++ b/extra/xbs-lukeshu/test/src/test.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ printf("Arch is the best!\n");
+ return EXIT_SUCCESS;
+}
diff --git a/extra/xbs-lukeshu/test/test.d/create-filelists.sh b/extra/xbs-lukeshu/test/test.d/create-filelists.sh
new file mode 100755
index 0000000..b5ec5c8
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/create-filelists.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+curdir="$(readlink -e "$(dirname "$0")")"
+. "${curdir}/../lib/common.inc"
+
+testCreateSimpleFileLists() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b' 'pkg-simple-epoch')
+ local pkgbase
+ local arch
+
+ for pkgbase in "${pkgs[@]}"; do
+ for arch in "${arches[@]}"; do
+ releasePackage extra "${pkgbase}" "${arch}"
+ done
+ done
+ ../db-update
+
+ for pkgbase in "${pkgs[@]}"; do
+ for arch in "${arches[@]}"; do
+ if ! bsdtar -xOf "${FTP_BASE}/extra/os/${arch}/extra${FILESEXT}" | grep "usr/bin/${pkgbase}" &>/dev/null; then
+ fail "usr/bin/${pkgbase} not found in ${arch}/extra${FILESEXT}"
+ fi
+ done
+ done
+}
+
+testCreateAnyFileLists() {
+ local arches=(`arches`)
+ local pkgs=('pkg-any-a' 'pkg-any-b')
+ local pkgbase
+ local arch
+
+ for pkgbase in "${pkgs[@]}"; do
+ releasePackage extra "${pkgbase}" any
+ done
+ ../db-update
+
+ for pkgbase in "${pkgs[@]}"; do
+ for arch in "${arches[@]}"; do
+ if ! bsdtar -xOf "${FTP_BASE}/extra/os/${arch}/extra${FILESEXT}" | grep "usr/share/${pkgbase}/test" &>/dev/null; then
+ fail "usr/share/${pkgbase}/test not found in ${arch}/extra${FILESEXT}"
+ fi
+ done
+ done
+}
+
+testCreateSplitFileLists() {
+ local arches=(`arches`)
+ local pkgs=('pkg-split-a' 'pkg-split-b')
+ local pkg
+ local pkgbase
+ local pkgname
+ local pkgnames
+ local arch
+
+ for pkgbase in "${pkgs[@]}"; do
+ for arch in "${arches[@]}"; do
+ releasePackage extra "${pkgbase}" "${arch}"
+ done
+ done
+ ../db-update
+
+ for pkgbase in "${pkgs[@]}"; do
+ pkgnames=($(source "${TMP}/svn-packages-copy/${pkgbase}/trunk/PKGBUILD"; echo "${pkgname[@]}"))
+ for pkgname in "${pkgnames[@]}"; do
+ for arch in "${arches[@]}"; do
+ if ! bsdtar -xOf "${FTP_BASE}/extra/os/${arch}/extra${FILESEXT}" | grep "usr/bin/${pkgname}" &>/dev/null; then
+ fail "usr/bin/${pkgname} not found in ${arch}/extra${FILESEXT}"
+ fi
+ done
+ done
+ done
+}
+
+
+testCleanupFileLists() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+ ../db-update
+
+ for arch in ${arches[@]}; do
+ ../db-remove extra ${arch} pkg-simple-a
+ done
+
+ for arch in ${arches[@]}; do
+ if ! bsdtar -xOf "${FTP_BASE}/extra/os/${arch}/extra${FILESEXT}" | grep "usr/bin/pkg-simple-b" &>/dev/null; then
+ fail "usr/bin/pkg-simple-b not found in ${arch}/extra${FILESEXT}"
+ fi
+ if bsdtar -xOf "${FTP_BASE}/extra/os/${arch}/extra${FILESEXT}" | grep "usr/bin/pkg-simple-a" &>/dev/null; then
+ fail "usr/bin/pkg-simple-a still found in ${arch}/extra${FILESEXT}"
+ fi
+ done
+
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/db-move.sh b/extra/xbs-lukeshu/test/test.d/db-move.sh
new file mode 100755
index 0000000..3cf355b
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/db-move.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testMoveSimplePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage testing ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ ../db-move testing extra pkg-simple-a
+
+ for arch in ${arches[@]}; do
+ checkPackage extra pkg-simple-a-1-1-${arch}.pkg.tar.xz ${arch}
+ checkRemovedPackage testing pkg-simple-a-1-1-${arch}.pkg.tar.xz ${arch}
+
+ checkPackage testing pkg-simple-b-1-1-${arch}.pkg.tar.xz ${arch}
+ done
+}
+
+testMoveMultiplePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage testing ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ ../db-move testing extra pkg-simple-a pkg-simple-b
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ checkPackage extra ${pkgbase}-1-1-${arch}.pkg.tar.xz ${arch}
+ checkRemovedPackage testing ${pkgbase}-1-1-${arch}.pkg.tar.xz ${arch}
+ done
+ done
+}
+
+testMoveEpochPackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-epoch')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage testing ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ ../db-move testing extra pkg-simple-epoch
+
+ for arch in ${arches[@]}; do
+ checkPackage extra pkg-simple-epoch-1:1-1-${arch}.pkg.tar.xz ${arch}
+ checkRemovedPackage testing pkg-simple-epoch-1:1-1-${arch}.pkg.tar.xz ${arch}
+ done
+}
+
+testMoveAnyPackages() {
+ local pkgs=('pkg-any-a' 'pkg-any-b')
+ local pkgbase
+
+ for pkgbase in ${pkgs[@]}; do
+ releasePackage testing ${pkgbase} any
+ done
+
+ ../db-update
+ ../db-move testing extra pkg-any-a
+
+ checkAnyPackage extra pkg-any-a-1-1-any.pkg.tar.xz
+ checkRemovedAnyPackage testing pkg-any-a
+ checkAnyPackage testing pkg-any-b-1-1-any.pkg.tar.xz
+}
+
+testMoveSplitPackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-split-a' 'pkg-split-b')
+ local pkg
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage testing ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+ ../db-move testing extra pkg-split-a
+
+ for arch in ${arches[@]}; do
+ for pkg in "${pkgdir}/pkg-split-a"/*-${arch}${PKGEXT}; do
+ checkPackage extra ${pkg##*/} ${arch}
+ done
+ done
+ for arch in ${arches[@]}; do
+ for pkg in "${pkgdir}/pkg-split-b"/*-${arch}${PKGEXT}; do
+ checkPackage testing ${pkg##*/} ${arch}
+ done
+ done
+
+ checkRemovedAnyPackage testing pkg-split-a
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/db-remove.sh b/extra/xbs-lukeshu/test/test.d/db-remove.sh
new file mode 100755
index 0000000..d79605e
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/db-remove.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testRemovePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b' 'pkg-split-a' 'pkg-split-b' 'pkg-simple-epoch')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ ../db-remove extra ${arch} ${pkgbase}
+ done
+ done
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ checkRemovedPackage extra ${pkgbase} ${arch}
+ done
+ done
+}
+
+testRemoveMultiplePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b' 'pkg-split-a' 'pkg-split-b' 'pkg-simple-epoch')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ for arch in ${arches[@]}; do
+ ../db-remove extra ${arch} ${pkgs[@]}
+ done
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ checkRemovedPackage extra ${pkgbase} ${arch}
+ done
+ done
+}
+
+testRemoveAnyPackages() {
+ local pkgs=('pkg-any-a' 'pkg-any-b')
+ local pkgbase
+
+ for pkgbase in ${pkgs[@]}; do
+ releasePackage extra ${pkgbase} any
+ done
+
+ ../db-update
+
+ for pkgbase in ${pkgs[@]}; do
+ ../db-remove extra any ${pkgbase}
+ done
+
+ for pkgbase in ${pkgs[@]}; do
+ checkRemovedAnyPackage extra ${pkgbase}
+ done
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/db-repo-add.sh b/extra/xbs-lukeshu/test/test.d/db-repo-add.sh
new file mode 100755
index 0000000..09fc52f
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/db-repo-add.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testAddSimplePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ cp "${pkgdir}/${pkgbase}/${pkgbase}-1-1-${arch}.pkg.tar.xz" "${FTP_BASE}/${PKGPOOL}/"
+ touch "${FTP_BASE}/${PKGPOOL}/${pkgbase}-1-1-${arch}.pkg.tar.xz.sig"
+ ln -s "${FTP_BASE}/${PKGPOOL}/${pkgbase}-1-1-${arch}.pkg.tar.xz" "${FTP_BASE}/extra/os/${arch}/"
+ ln -s "${FTP_BASE}/${PKGPOOL}/${pkgbase}-1-1-${arch}.pkg.tar.xz.sig" "${FTP_BASE}/extra/os/${arch}/"
+ ../db-repo-add extra ${arch} ${pkgbase}-1-1-${arch}.pkg.tar.xz
+ done
+ done
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ checkPackageDB extra ${pkgbase}-1-1-${arch}.pkg.tar.xz ${arch}
+ done
+ done
+}
+
+testAddMultiplePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b')
+ local pkgbase
+ local arch
+
+ for arch in ${arches[@]}; do
+ add_pkgs=()
+ for pkgbase in ${pkgs[@]}; do
+ cp "${pkgdir}/${pkgbase}/${pkgbase}-1-1-${arch}.pkg.tar.xz" "${FTP_BASE}/${PKGPOOL}/"
+ touch "${FTP_BASE}/${PKGPOOL}/${pkgbase}-1-1-${arch}.pkg.tar.xz.sig"
+ ln -s "${FTP_BASE}/${PKGPOOL}/${pkgbase}-1-1-${arch}.pkg.tar.xz" "${FTP_BASE}/extra/os/${arch}/"
+ ln -s "${FTP_BASE}/${PKGPOOL}/${pkgbase}-1-1-${arch}.pkg.tar.xz.sig" "${FTP_BASE}/extra/os/${arch}/"
+ add_pkgs[${#add_pkgs[*]}]=${pkgbase}-1-1-${arch}.pkg.tar.xz
+ done
+ ../db-repo-add extra ${arch} ${add_pkgs[@]}
+ done
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ checkPackageDB extra ${pkgbase}-1-1-${arch}.pkg.tar.xz ${arch}
+ done
+ done
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/db-repo-remove.sh b/extra/xbs-lukeshu/test/test.d/db-repo-remove.sh
new file mode 100755
index 0000000..eec0109
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/db-repo-remove.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testRemovePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b' 'pkg-simple-epoch')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ ../db-repo-remove extra ${arch} ${pkgbase}
+ done
+ done
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ checkRemovedPackageDB extra ${pkgbase} ${arch}
+ done
+ done
+}
+
+testRemoveMultiplePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b' 'pkg-simple-epoch')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ for arch in ${arches[@]}; do
+ ../db-repo-remove extra ${arch} ${pkgs[@]}
+ done
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ checkRemovedPackageDB extra ${pkgbase} ${arch}
+ done
+ done
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/db-update.sh b/extra/xbs-lukeshu/test/test.d/db-update.sh
new file mode 100755
index 0000000..7f1874b
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/db-update.sh
@@ -0,0 +1,175 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testAddSimplePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ checkPackage extra ${pkgbase}-1-1-${arch}.pkg.tar.xz ${arch}
+ done
+ done
+}
+
+testAddSingleSimplePackage() {
+ releasePackage extra 'pkg-simple-a' 'i686'
+ ../db-update
+ checkPackage extra 'pkg-simple-a-1-1-i686.pkg.tar.xz' 'i686'
+}
+
+testAddSingleEpochPackage() {
+ releasePackage extra 'pkg-simple-epoch' 'i686'
+ ../db-update
+ checkPackage extra 'pkg-simple-epoch-1:1-1-i686.pkg.tar.xz' 'i686'
+}
+
+testAddAnyPackages() {
+ local pkgs=('pkg-any-a' 'pkg-any-b')
+ local pkgbase
+
+ for pkgbase in ${pkgs[@]}; do
+ releasePackage extra ${pkgbase} any
+ done
+
+ ../db-update
+
+ for pkgbase in ${pkgs[@]}; do
+ checkAnyPackage extra ${pkgbase}-1-1-any.pkg.tar.xz
+ done
+}
+
+testAddSplitPackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-split-a' 'pkg-split-b')
+ local pkg
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ for pkg in "${pkgdir}/${pkgbase}"/*-${arch}${PKGEXT}; do
+ checkPackage extra ${pkg##*/} ${arch}
+ done
+ done
+ done
+}
+
+testUpdateAnyPackage() {
+ releasePackage extra pkg-any-a any
+ ../db-update
+
+ 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 libremakepkg
+ mv pkg-any-a-1-2-any.pkg.tar.xz "${pkgdir}/pkg-any-a/"
+ popd >/dev/null
+
+ releasePackage extra pkg-any-a any
+ ../db-update
+
+ checkAnyPackage extra pkg-any-a-1-2-any.pkg.tar.xz any
+
+ rm -f "${pkgdir}/pkg-any-a/pkg-any-a-1-2-any.pkg.tar.xz"
+}
+
+testUpdateAnyPackageToDifferentRepositoriesAtOnce() {
+ releasePackage extra pkg-any-a any
+
+ 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 libremakepkg
+ mv pkg-any-a-1-2-any.pkg.tar.xz "${pkgdir}/pkg-any-a/"
+ popd >/dev/null
+
+ releasePackage testing pkg-any-a any
+
+ ../db-update
+
+ checkAnyPackage extra pkg-any-a-1-1-any.pkg.tar.xz any
+ checkAnyPackage testing pkg-any-a-1-2-any.pkg.tar.xz any
+
+ rm -f "${pkgdir}/pkg-any-a/pkg-any-a-1-2-any.pkg.tar.xz"
+}
+
+testUpdateSameAnyPackageToSameRepository() {
+ releasePackage extra pkg-any-a any
+ ../db-update
+ checkAnyPackage extra pkg-any-a-1-1-any.pkg.tar.xz any
+
+ releasePackage extra pkg-any-a any
+ ../db-update >/dev/null 2>&1 && (fail 'Adding an existing package to the same repository should fail'; return 1)
+}
+
+testUpdateSameAnyPackageToDifferentRepositories() {
+ releasePackage extra pkg-any-a any
+ ../db-update
+ checkAnyPackage extra pkg-any-a-1-1-any.pkg.tar.xz any
+
+ releasePackage testing pkg-any-a any
+ ../db-update >/dev/null 2>&1 && (fail 'Adding an existing package to another repository should fail'; return 1)
+
+ local arch
+ for arch in $(arches); 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.*}"
+ done
+}
+
+
+testAddIncompleteSplitPackage() {
+ local arches=(`arches`)
+ local repo='extra'
+ local pkgbase='pkg-split-a'
+ local arch
+
+ for arch in ${arches[@]}; do
+ releasePackage ${repo} ${pkgbase} ${arch}
+ done
+
+ # remove a split package to make db-update fail
+ rm "${STAGING}"/extra/${pkgbase}1-*
+
+ ../db-update >/dev/null 2>&1 && fail "db-update should fail when a split package is missing!"
+
+ for arch in ${arches[@]}; do
+ ( [ -r "${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT%.tar.*}" ] \
+ && bsdtar -xf "${FTP_BASE}/${repo}/os/${arch}/${repo}${DBEXT%.tar.*}" -O | grep ${pkgbase} &>/dev/null) \
+ && fail "${pkgbase} should not be in ${repo}/os/${arch}/${repo}${DBEXT%.tar.*}"
+ done
+}
+
+testUnknownRepo() {
+ mkdir "${STAGING}/unknown/"
+ releasePackage extra 'pkg-simple-a' 'i686'
+ releasePackage unknown 'pkg-simple-b' 'i686'
+ ../db-update
+ checkPackage extra 'pkg-simple-a-1-1-i686.pkg.tar.xz' 'i686'
+ [ -e "${FTP_BASE}/unknown" ] && fail "db-update pushed a package into an unknown repository"
+ rm -rf "${STAGING}/unknown/"
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/ftpdir-cleanup.sh b/extra/xbs-lukeshu/test/test.d/ftpdir-cleanup.sh
new file mode 100755
index 0000000..630b88f
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/ftpdir-cleanup.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testCleanupSimplePackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ for arch in ${arches[@]}; do
+ ../db-remove extra ${arch} pkg-simple-a
+ done
+
+ ../cron-jobs/ftpdir-cleanup >/dev/null
+
+ for arch in ${arches[@]}; do
+ local pkg1="pkg-simple-a-1-1-${arch}.pkg.tar.xz"
+ checkRemovedPackage extra 'pkg-simple-a' ${arch}
+ [ -f "${FTP_BASE}/${PKGPOOL}/${pkg1}" ] && fail "${PKGPOOL}/${pkg1} found"
+ [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkg1}" ] && fail "${repo}/os/${arch}/${pkg1} found"
+
+ local pkg2="pkg-simple-b-1-1-${arch}.pkg.tar.xz"
+ checkPackage extra ${pkg2} ${arch}
+ done
+}
+
+testCleanupEpochPackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-epoch')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ for arch in ${arches[@]}; do
+ ../db-remove extra ${arch} pkg-simple-epoch
+ done
+
+ ../cron-jobs/ftpdir-cleanup >/dev/null
+
+ for arch in ${arches[@]}; do
+ local pkg1="pkg-simple-epoch-1:1-1-${arch}.pkg.tar.xz"
+ checkRemovedPackage extra 'pkg-simple-epoch' ${arch}
+ [ -f "${FTP_BASE}/${PKGPOOL}/${pkg1}" ] && fail "${PKGPOOL}/${pkg1} found"
+ [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkg1}" ] && fail "${repo}/os/${arch}/${pkg1} found"
+ done
+}
+
+testCleanupAnyPackages() {
+ local pkgs=('pkg-any-a' 'pkg-any-b')
+ local pkgbase
+ local arch='any'
+
+ for pkgbase in ${pkgs[@]}; do
+ releasePackage extra ${pkgbase} any
+ done
+
+ ../db-update
+ ../db-remove extra any pkg-any-a
+ ../cron-jobs/ftpdir-cleanup >/dev/null
+
+ local pkg1='pkg-any-a-1-1-any.pkg.tar.xz'
+ checkRemovedAnyPackage extra 'pkg-any-a'
+ [ -f "${FTP_BASE}/${PKGPOOL}/${pkg1}" ] && fail "${PKGPOOL}/${pkg1} found"
+ [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkg1}" ] && fail "${repo}/os/${arch}/${pkg1} found"
+
+ local pkg2="pkg-any-b-1-1-${arch}.pkg.tar.xz"
+ checkAnyPackage extra ${pkg2}
+}
+
+testCleanupSplitPackages() {
+ local arches=(`arches`)
+ local pkgs=('pkg-split-a' 'pkg-split-b')
+ local pkg
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ for arch in ${arches[@]}; do
+ ../db-remove extra ${arch} ${pkgs[0]}
+ done
+
+ ../cron-jobs/ftpdir-cleanup >/dev/null
+
+ for arch in ${arches[@]}; do
+ for pkg in "${pkgdir}/${pkgs[0]}"/*-${arch}${PKGEXT}; do
+ checkRemovedPackage extra ${pkgs[0]} ${arch}
+ [ -f "${FTP_BASE}/${PKGPOOL}/${pkg}" ] && fail "${PKGPOOL}/${pkg} found"
+ [ -f "${FTP_BASE}/${repo}/os/${arch}/${pkg}" ] && fail "${repo}/os/${arch}/${pkg} found"
+ done
+
+ for pkg in "${pkgdir}/${pkgs[1]}"/*-${arch}${PKGEXT}; do
+ checkPackage extra ${pkg##*/} ${arch}
+ done
+ done
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/packages.sh b/extra/xbs-lukeshu/test/test.d/packages.sh
new file mode 100755
index 0000000..488cb15
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/packages.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testPackages() {
+ # TODO: namcap -r sodepends fails with i686 packages
+ find "${pkgdir}" -name "*${PKGEXT}" -exec namcap -e sodepends,pkgnameindesc {} + || fail 'namcap failed'
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/signed-packages.sh b/extra/xbs-lukeshu/test/test.d/signed-packages.sh
new file mode 100755
index 0000000..03566ef
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/signed-packages.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testAddSignedPackage() {
+ releasePackage extra 'pkg-simple-a' 'i686'
+ ../db-update || fail "db-update failed!"
+}
+
+testAddUnsignedPackage() {
+ releasePackage extra 'pkg-simple-a' 'i686'
+ rm "${STAGING}"/extra/*.sig
+ ../db-update >/dev/null 2>&1 && fail "db-update should fail when a signature is missing!"
+}
+
+testAddInvalidSignedPackage() {
+ local p
+ releasePackage extra 'pkg-simple-a' 'i686'
+ for p in "${STAGING}"/extra/*${PKGEXT}; do
+ unxz $p
+ xz -0 ${p%%.xz}
+ done
+ ../db-update >/dev/null 2>&1 && fail "db-update should fail when a signature is invalid!"
+}
+
+testAddBrokenSignature() {
+ local s
+ releasePackage extra 'pkg-simple-a' 'i686'
+ for s in "${STAGING}"/extra/*.sig; do
+ echo 0 > $s
+ done
+ ../db-update >/dev/null 2>&1 && fail "db-update should fail when a signature is broken!"
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/sourceballs.sh b/extra/xbs-lukeshu/test/test.d/sourceballs.sh
new file mode 100755
index 0000000..472cb30
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/sourceballs.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testSourceballs() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b' 'pkg-simple-epoch')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+ ../db-update
+
+ ../cron-jobs/sourceballs
+ for pkgbase in ${pkgs[@]}; do
+ [ ! -r ${FTP_BASE}/${SRCPOOL}/${pkgbase}-*${SRCEXT} ] && fail "source package not found!"
+ done
+}
+
+testAnySourceballs() {
+ local pkgs=('pkg-any-a' 'pkg-any-b')
+ local pkgbase
+
+ for pkgbase in ${pkgs[@]}; do
+ releasePackage extra ${pkgbase} any
+ done
+ ../db-update
+
+ ../cron-jobs/sourceballs
+ for pkgbase in ${pkgs[@]}; do
+ [ ! -r ${FTP_BASE}/${SRCPOOL}/${pkgbase}-*${SRCEXT} ] && fail "source package not found!"
+ done
+}
+
+testSplitSourceballs() {
+ local arches=(`arches`)
+ local pkgs=('pkg-split-a' 'pkg-split-b')
+ local pkg
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+
+ ../db-update
+
+ ../cron-jobs/sourceballs
+ for pkgbase in ${pkgs[@]}; do
+ [ ! -r ${FTP_BASE}/${SRCPOOL}/${pkgbase}-*${SRCEXT} ] && fail "source package not found!"
+ done
+}
+
+testSourceballsCleanup() {
+ local arches=(`arches`)
+ local pkgs=('pkg-simple-a' 'pkg-simple-b')
+ local pkgbase
+ local arch
+
+ for pkgbase in ${pkgs[@]}; do
+ for arch in ${arches[@]}; do
+ releasePackage extra ${pkgbase} ${arch}
+ done
+ done
+ ../db-update
+ ../cron-jobs/sourceballs
+
+ for arch in ${arches[@]}; do
+ ../db-remove extra ${arch} pkg-simple-a
+ done
+
+ ../cron-jobs/sourceballs
+ [ -r ${FTP_BASE}/${SRCPOOL}/pkg-simple-a-*${SRCEXT} ] && fail "source package was not removed!"
+ [ ! -r ${FTP_BASE}/${SRCPOOL}/pkg-simple-b-*${SRCEXT} ] && fail "source package not found!"
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test.d/testing2x.sh b/extra/xbs-lukeshu/test/test.d/testing2x.sh
new file mode 100755
index 0000000..0c2fa83
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test.d/testing2x.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+curdir=$(readlink -e $(dirname $0))
+. "${curdir}/../lib/common.inc"
+
+testTesting2xAnyPackage() {
+ releasePackage core pkg-any-a any
+ ../db-update
+
+ 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 libremakepkg
+ mv pkg-any-a-1-2-any.pkg.tar.xz "${pkgdir}/pkg-any-a/"
+ popd >/dev/null
+
+ releasePackage testing pkg-any-a any
+ ../db-update
+ rm -f "${pkgdir}/pkg-any-a/pkg-any-a-1-2-any.pkg.tar.xz"
+
+ ../testing2x pkg-any-a
+
+ checkAnyPackage core pkg-any-a-1-2-any.pkg.tar.xz any
+ checkRemovedAnyPackage testing pkg-any-a
+}
+
+. "${curdir}/../lib/shunit2"
diff --git a/extra/xbs-lukeshu/test/test_filter.py b/extra/xbs-lukeshu/test/test_filter.py
new file mode 100644
index 0000000..d8006f9
--- /dev/null
+++ b/extra/xbs-lukeshu/test/test_filter.py
@@ -0,0 +1,196 @@
+# -*- encoding: utf-8 -*-
+""" """
+
+__author__ = "Joshua Ismael Haase Hernández <hahj87@gmail.com>"
+__version__ = "$Revision: 1.1 $"
+__date__ = "$Date: 2011/02/08 $"
+__copyright__ = "Copyright (c) 2011 Joshua Ismael Haase Hernández"
+__license__ = "GPL3+"
+
+from repm.config import *
+from repm.filter import *
+import unittest
+
+class pkginfo_from_file_KnownValues(unittest.TestCase):
+ # (filename, name, version, release, arch)
+ # filename is location
+ known=(
+ ("community-testing/os/i686/inputattach-1.24-3-i686.pkg.tar.xz","inputattach","1.24","3","i686"),
+ ("community-testing/os/i686/ngspice-22-1-i686.pkg.tar.xz","ngspice","22","1","i686"),
+ ("community-testing/os/i686/tmux-1.4-2-i686.pkg.tar.xz","tmux","1.4","2","i686"),
+ ("community-testing/os/i686/tor-0.2.1.29-2-i686.pkg.tar.xz","tor","0.2.1.29","2","i686"),
+ ("../../../pool/community/tor-0.2.1.29-2-i686.pkg.tar.xz","tor","0.2.1.29","2","i686"),
+ ("community-testing/os/x86_64/inputattach-1.24-3-x86_64.pkg.tar.xz","inputattach","1.24","3","x86_64"),
+ ("../../../pool/community/inputattach-1.24-3-x86_64.pkg.tar.xz","inputattach","1.24","3","x86_64"),
+ ("tor-0.2.1.29-2-x86_64.pkg.tar.xz","tor","0.2.1.29","2","x86_64"),
+ )
+
+ def generate_results(self, example_tuple, attr):
+ location, name, version, release, arch = example_tuple
+ return pkginfo_from_filename(location)[attr], locals()[attr]
+
+ def testReturnPackageObject(self):
+ for i in self.known:
+ location, name, version, release, arch = i
+ self.assertIsInstance(pkginfo_from_filename(location),Package)
+
+ def testNames(self):
+ for i in self.known:
+ k,v = self.generate_results(example_tuple=i,attr="name")
+ self.assertEqual(k, v)
+
+ def testVersions(self):
+ for i in self.known:
+ k,v = self.generate_results(example_tuple=i,attr="version")
+ self.assertEqual(k, v)
+
+ def testArchs(self):
+ for i in self.known:
+ k,v = self.generate_results(example_tuple=i,attr="arch")
+ self.assertEqual(k, v)
+
+ def testReleases(self):
+ for i in self.known:
+ k,v = self.generate_results(example_tuple=i,attr="release")
+ self.assertEqual(k, v)
+
+ def testLocations(self):
+ for i in self.known:
+ k,v = self.generate_results(example_tuple=i,attr="location")
+ self.assertEqual(k, v)
+
+class pkginfo_from_file_BadInput(unittest.TestCase):
+ bad=("community-testing/os/i686/community-testing.db",
+ "community-testing/os/i686/community-testing.db.tar.gz",
+ "community-testing/os/i686/community-testing.db.tar.gz.old",
+ "community-testing/os/i686/community-testing.files",
+ "community-testing/os/i686/community-testing.files.tar.gz",
+ "community-testing/os/x86_64")
+
+ def testBadInput(self):
+ for i in self.bad:
+ self.assertRaises(NonValidFile,pkginfo_from_filename,i)
+
+class pkginfoFromRsyncOutput(unittest.TestCase):
+ example_package_list=(Package(),Package(),Package())
+ example_package_list[0].package_info={ "name" : "alex",
+ "version" : "2.3.4",
+ "release" : "1",
+ "arch" : "i686",
+ "license" : False,
+ "location": "community-staging/os/i686/alex-2.3.4-1-i686.pkg.tar.xz",
+ "depends" : False,}
+ example_package_list[1].package_info={ "name" : "any2dvd",
+ "version" : "0.34",
+ "release" : "4",
+ "arch" : "any",
+ "license" : False,
+ "location": "community/os/any/any2dvd-0.34-4-any.pkg.tar.xz",
+ "depends" : False,}
+ example_package_list[2].package_info={ "name" : "gmime22",
+ "version" : "2.2.26",
+ "release" : "1",
+ "arch" : "x86_64",
+ "license" : False,
+ "location": "community/os/x86_64/gmime22-2.2.26-1-x86_64.pkg.tar.xz",
+ "depends" : False,}
+
+ try:
+ output_file = open("rsync_output_sample")
+ rsync_out= output_file.read()
+ output_file.close()
+ except IOError: print("There is no rsync_output_sample file")
+
+ pkglist = pkginfo_from_rsync_output(rsync_out)
+
+ def testOutputArePackages(self):
+ if not self.pkglist:
+ self.fail("not pkglist:" + str(self.pkglist))
+ for pkg in self.pkglist:
+ self.assertIsInstance(pkg,Package)
+
+ def testPackageInfo(self):
+ if not self.pkglist:
+ self.fail("Pkglist doesn't exist: " + str(self.pkglist))
+ self.assertEqual(self.pkglist,self.example_package_list)
+
+class generateRsyncBlacklist(unittest.TestCase):
+ example_package_list=(Package(),Package(),Package())
+ example_package_list[0].package_info={ "name" : "alex",
+ "version" : "2.3.4",
+ "release" : "1",
+ "arch" : "i686",
+ "license" : False,
+ "location": "community-staging/os/i686/alex-2.3.4-1-i686.pkg.tar.xz",
+ "depends" : False,}
+ example_package_list[1].package_info={ "name" : "any2dvd",
+ "version" : "0.34",
+ "release" : "4",
+ "arch" : "any",
+ "license" : False,
+ "location": "community/os/any/any2dvd-0.34-4-any.pkg.tar.xz",
+ "depends" : False,}
+ example_package_list[2].package_info={ "name" : "gmime22",
+ "version" : "2.2.26",
+ "release" : "1",
+ "arch" : "x86_64",
+ "license" : False,
+ "location": "community/os/x86_64/gmime22-2.2.26-1-x86_64.pkg.tar.xz",
+ "depends" : False,}
+
+ def testListado(self):
+ self.assertEqual(listado("blacklist_sample"),["alex","gmime22"])
+
+ def testExcludeFiles(self):
+ a=rsyncBlacklist_from_blacklist(self.example_package_list,
+ listado("blacklist_sample"),
+ False)
+ b=[self.example_package_list[0]["location"],self.example_package_list[2]["location"]]
+ self.assertEqual(a,b)
+
+class pkginfo_from_descKnownValues(unittest.TestCase):
+ pkgsample=Package()
+ pkgsample.package_info={"name" : "binutils",
+ "version" : "2.21",
+ "release" : "4",
+ "arch" : "x86_64",
+ "license" : "GPL",
+ "location": "binutils-2.21-4-x86_64.pkg.tar.xz",
+ "depends" : False,}
+ fsock=open("desc")
+ pkggen=pkginfo_from_desc(fsock.read())
+ fsock.close()
+ def testPkginfoFromDesc(self):
+ if self.pkggen is None:
+ self.fail("return value is None")
+ self.assertEqual(self.pkgsample,self.pkggen)
+
+class pkginfo_from_db(unittest.TestCase):
+ archdb = os.path.join("./workdir")
+ example_package_list=(Package(),Package(),Package())
+ example_package_list[0].package_info={ "name" : "acl",
+ "version" : "2.2.49",
+ "release" : "2",
+ "arch" : "x86_64",
+ "license" : ("LGPL",),
+ "location": "acl-2.2.49-2-x86_64.pkg.tar.xz",
+ "depends" : ("attr>=2.4.41"),}
+ example_package_list[1].package_info={ "name" : "glibc",
+ "version" : "2.13",
+ "release" : "4",
+ "arch" : "x86_64",
+ "license" : ("GPL","LGPL"),
+ "location": "glibc-2.13-4-x86_64.pkg.tar.xz",
+ "depends" : ("linux-api-headers>=2.6.37","tzdata",),}
+ example_package_list[2].package_info={ "name" : "",
+ "version" : "2.2.26",
+ "release" : "1",
+ "arch" : "x86_64",
+ "license" : False,
+ "location": "",
+ "depends" : False,}
+
+
+if __name__ == "__main__":
+ unittest.main()
+