public inbox for cygwin-apps-cvs@sourceware.org
help / color / mirror / Atom feed
* [calm - Cygwin server-side packaging maintenance script] branch master, updated. 20230209-96-g693426a
@ 2024-04-17 19:03 Jon Turney
  0 siblings, 0 replies; only message in thread
From: Jon Turney @ 2024-04-17 19:03 UTC (permalink / raw)
  To: cygwin-apps-cvs

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 54458 bytes --]




https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/calm.git;h=693426a56fcf26a85a1aeb202fccace35469e344

commit 693426a56fcf26a85a1aeb202fccace35469e344
Author: Jon Turney <jon.turney@dronecode.org.uk>
Date:   Wed Apr 17 17:16:58 2024 +0100

    Add a test of upload auth failure

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/calm.git;h=cddf1ab4c03e78382278390089dd4c7cbc8ef3d4

commit cddf1ab4c03e78382278390089dd4c7cbc8ef3d4
Author: Jon Turney <jon.turney@dronecode.org.uk>
Date:   Fri Apr 12 00:02:12 2024 +0100

    Add the ability to ingest arch-tagged package filenames
    
    Change to deriving the name of the listing file from package name and
    version, rather than from the tar archive filename, because that would
    include the arch tag.
    
    That exposes an interesting anomaly: We are ignoring any version:
    override when generating the listing filename. Since it's taken into
    account where the link is made to that file on the package summary page,
    that seems like a bug which this change accidentally fixes.
    
    Adjust the test data to reflect this fix.

https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/calm.git;h=1e7c58f03b970cb13eee490fe0e043a490c7b7b5

commit 1e7c58f03b970cb13eee490fe0e043a490c7b7b5
Author: Jon Turney <jon.turney@dronecode.org.uk>
Date:   Fri Sep 16 16:38:33 2022 +0100

    Separate collect and read package files steps
    
    This is necessary when different versions of a src package might exist
    in both arch/ and src/ paths.
    
    Update test data for change to Package() repr, as we no longer have
    pkgpath, just then package name
    
    This is preparatory work for allowing a couple of things: putting new
    source packages in src/ (without moving the existing ones under arch/),
    re-structuring the data model so that arch is inside package, rather
    than containing a collection of them.
    
    This improves package authorization so it's possible for a package can
    replace parts of another package (without needing rearrangement of the
    package locations), by requiring authorization for *all* paths at which
    a package exists.
    
    Change to using a class rather than named tuple to hold scan results, so
    it can be mutable in uploads.auth_check()
    
    Also: drop no longer needed use of a defaultdict for holding packages in
    uploads.scan()


Diff:
---
 TODO                                               |   2 -
 calm/calm.py                                       |   4 +
 calm/package.py                                    | 247 ++++++++++++---------
 calm/pkg2html.py                                   |  18 +-
 calm/uploads.py                                    |  74 +++++-
 test/test_calm.py                                  |  37 +++
 .../testpackage/cygwin/cygwin-3.0.0-1-src.hint     |   5 +
 .../testpackage/cygwin/cygwin-3.0.0-1-src.tar.xz   | Bin 0 -> 228 bytes
 .../release/testpackage/cygwin/cygwin-3.0.0-1.hint |   4 +
 .../testpackage/cygwin/cygwin-3.0.0-1.tar.xz       | Bin 0 -> 228 bytes
 test/testdata/htdocs.expected/dirtree.expected     |   4 +-
 .../{test-d-1.0-1-src => test-d-1.0.42590-1-src}   |   0
 .../test-d/{test-d-1.0-1 => test-d-1.0.42590-1}    |   0
 .../{test-d-1.0-1-src => test-d-1.0.42590-1-src}   |   0
 test/testdata/process_arch/htdocs.expected         |   4 +-
 test/testdata/upload_bad_auth/homedir.expected     |   9 +
 test/testdata/upload_bad_auth/htdocs.expected      |   1 +
 test/testdata/upload_bad_auth/rel_area.expected    | 182 +++++++++++++++
 test/testdata/upload_bad_auth/stagingdir.expected  |   1 +
 test/testdata/upload_bad_auth/vault.expected       |  15 ++
 test/testdata/uploads/pkglist.expected             |   8 +-
 21 files changed, 485 insertions(+), 130 deletions(-)

diff --git a/TODO b/TODO
index c4daa81..5e09902 100644
--- a/TODO
+++ b/TODO
@@ -3,10 +3,8 @@
 * use ./setup.hint inside the tar file, avoiding all the hint/tar coherence problems
 * mksetupini should write to stdout if --inifile not specified
 * check packages for path collisions
-* don't do upload authorization by path, then remove unique path constraint
 * mksetupini should be able to verify requires: contains valid package names using a provided list of packages (or a cygwin-pkg-maint file?)
 * make override.hint (optionally?) apply recursively?
 * atomically update .ini/.sig (rename of containing directory, if we put release/ was somewhere else?)
 * report changes in override.hint like we used to for setup.hint
-* maintainers.py should only re-read cygwin-pkg-maint if it's changed
 * empty install packages should only come in two variants: no dependencies and in _obsolete category, or with dependencies and in 'meta' category
diff --git a/calm/calm.py b/calm/calm.py
index b13d78c..ac662a2 100755
--- a/calm/calm.py
+++ b/calm/calm.py
@@ -194,6 +194,10 @@ def process_maintainer_uploads(args, state, all_packages, m, basedir, desc, scru
         # remove triggers
         uploads.remove(args, scan_result[arch].remove_always)
 
+        # check upload is authorized
+        if not scan_result[arch].error:
+            uploads.auth_check(args, m, scan_result[arch], state.packages)
+
         if scan_result[arch].error:
             logging.error("error while reading uploaded arch %s packages from maintainer %s" % (arch, m.name))
             success = False
diff --git a/calm/package.py b/calm/package.py
index 6220191..d03989a 100755
--- a/calm/package.py
+++ b/calm/package.py
@@ -73,6 +73,11 @@ class RepoPath():
         self.path = _path
         self.fn = _fn
 
+    def __eq__(self, other):
+        return (self.arch == other.arch and
+                self.path == other.path and
+                self.fn == other.fn)
+
     # convert to a path, absolute if given a base directory
     def abspath(self, basedir=None):
         pc = [self.arch, 'release', self.path, self.fn]
@@ -91,17 +96,17 @@ class RepoPath():
 # information we keep about a package
 class Package(object):
     def __init__(self):
-        self.pkgpath = ''  # path to package, relative to arch
         self.tarfiles = {}
         self.hints = {}
         self.is_used_by = set()
         self.version_hints = {}
         self.override_hints = {}
         self.not_for_output = False
+        self.auth_path = set()
 
     def __repr__(self):
         return "Package('%s', %s, %s, %s, %s)" % (
-            self.pkgpath,
+            self.name,
             pprint.pformat(self.tarfiles),
             pprint.pformat(self.version_hints),
             pprint.pformat(self.override_hints),
@@ -154,22 +159,32 @@ class Hint(object):
 # read a packages from a directory hierarchy
 #
 def read_packages(rel_area, arch):
-    error = False
-    packages = {}
+    result = False
 
+    # first collect all package files
     # <arch>/ noarch/ and src/ directories are considered
+    collected = {}
     for root in ['noarch', 'src', arch]:
-        packages[root] = {}
-
         releasedir = os.path.join(rel_area, root)
-        logging.debug('reading packages from %s' % releasedir)
 
         for (dirpath, _subdirs, files) in os.walk(releasedir, followlinks=True):
-            error = read_package_dir(packages[root], rel_area, dirpath, files) or error
+            result = collect_files_package_dir(collected, rel_area, dirpath, files) or result
+
+    # then read each package
+    logging.debug('reading packages from %s' % rel_area)
 
-        logging.debug("%d packages read from %s" % (len(packages[root]), releasedir))
+    packages = {}
+    for p in collected:
+        fl = collected[p]
+        for kind in Kind:
+            if not fl[kind]:
+                continue
 
-    return (merge({}, *packages.values()), error)
+            result = read_one_package(packages, p, rel_area, fl[kind] + fl['all'], kind, strict=False) or result
+
+    logging.debug("%d packages read from %s" % (len(packages), rel_area))
+
+    return (packages, result)
 
 
 # helper function to compute sha512 for a particular file
@@ -319,15 +334,22 @@ def clean_hints(p, hints, warnings):
 
 
 #
-# read a single package directory
+# collect package files from a single package directory
 #
 # (may contain at most one source package and one binary package)
-# (return True if problems, False otherwise)
+# (updates collected, and returns True if problems, False otherwise)
 #
 
-def read_package_dir(packages, basedir, dirpath, files, upload=False):
+def collect_files_package_dir(collected, basedir, dirpath, files):
     relpath = os.path.relpath(dirpath, basedir)
 
+    # skip over arch/release/ directories
+    if relpath.count(os.sep) < 2:
+        return
+
+    (arch, release, pkgpath) = relpath.split(os.sep, 2)
+    assert release == 'release'
+
     # the package name is always the directory name
     p = os.path.basename(dirpath)
 
@@ -345,48 +367,45 @@ def read_package_dir(packages, basedir, dirpath, files, upload=False):
 
         return False
 
-    # classify files for which kind of package they belong to
-    fl = {}
-    for kind in list(Kind) + ['all']:
-        fl[kind] = []
+    # initialize package in collected
+    if p not in collected:
+        collected[p] = {}
+        for kind in list(Kind) + ['all']:
+            collected[p][kind] = []
 
+    fl = collected[p]
+
+    # classify files for which kind of package they belong to
     for f in files[:]:
         if f == 'override.hint':
-            fl['all'].append(f)
+            fl['all'].append(RepoPath(arch, pkgpath, f))
             files.remove(f)
         elif re.match(r'^' + re.escape(p) + r'.*\.hint$', f):
             if f.endswith('-src.hint'):
-                fl[Kind.source].append(f)
+                fl[Kind.source].append(RepoPath(arch, pkgpath, f))
             else:
-                fl[Kind.binary].append(f)
+                fl[Kind.binary].append(RepoPath(arch, pkgpath, f))
             files.remove(f)
         elif re.match(r'^' + re.escape(p) + r'.*\.tar' + common_constants.PACKAGE_COMPRESSIONS_RE + r'$', f):
             if '-src.tar' in f:
-                fl[Kind.source].append(f)
+                fl[Kind.source].append(RepoPath(arch, pkgpath, f))
             else:
-                fl[Kind.binary].append(f)
+                fl[Kind.binary].append(RepoPath(arch, pkgpath, f))
             files.remove(f)
 
-    # read package
-    result = False
-    for kind in Kind:
-        # only create a package if there's archives for it to contain
-        if fl[kind]:
-            result = read_one_package(packages, p, relpath, dirpath, fl[kind] + fl['all'], kind, upload) or result
-
     # warn about unexpected files, including tarfiles which don't match the
     # package name
     if files:
         logging.error("unexpected files in %s: %s" % (p, ', '.join(files)))
-        result = True
+        return True
 
-    return result
+    return False
 
 
 #
 # read a single package
 #
-def read_one_package(packages, p, relpath, dirpath, files, kind, strict):
+def read_one_package(packages, p, basedir, files, kind, strict):
     warnings = False
 
     if not re.match(r'^[\w\-._+]*$', p):
@@ -405,44 +424,69 @@ def read_one_package(packages, p, relpath, dirpath, files, kind, strict):
         logging.error("package '%s' name ends with '-src'" % p)
         return True
 
-    # check for duplicate package names at different paths
-    (arch, release, pkgpath) = relpath.split(os.sep, 2)
-    assert release == 'release'
     pn = p + ('-src' if kind == Kind.source else '')
 
     if pn in packages:
-        logging.error("duplicate package name at paths %s and %s" %
-                      (relpath, packages[p].pkgpath))
+        logging.error("duplicate package name %s" % (pn))
         return True
 
-    if 'override.hint' in files:
-        # read override.hint
-        override_hints = read_hints(p, os.path.join(dirpath, 'override.hint'), hint.override)
-        if override_hints is None:
-            logging.error("error parsing %s" % (os.path.join(dirpath, 'override.hint')))
-            return True
-        files.remove('override.hint')
-    else:
-        override_hints = {}
+    # collecting multiple copies of any filename is an error
+    # (we could easily do this by accident with override.hint)
+    duplicates = set()
+    seen = set()
+    for f in files:
+        if f.fn in seen:
+            duplicates.add(f.fn)
+        seen.add(f.fn)
+
+    if duplicates:
+        for d in duplicates:
+            logging.error("Multiple %s files for package '%s'.  I can't handle that!" % (d, p))
+        return True
+
+    # read any override.hint
+    override_hints = {}
+    for rp in list(files):
+        if rp.fn == 'override.hint':
+            override_hints = read_hints(p, rp.abspath(basedir), hint.override)
+            if override_hints is None:
+                logging.error("error parsing %s" % (rp.fn))
+                return True
+            files.remove(rp)
+            break
 
     # build a list of version-releases (since replacement pvr.hint files are
     # allowed to be uploaded, we must consider both .tar and .hint files for
     # that), and collect the attributes for each tar file
     tars = {}
+    hint_files = {}
     vr_list = set()
+    auth_path = set()
+
+    for rp in list(files):
+        f = rp.fn
+
+        if kind == Kind.source:
+            arch_re = r'(-src)'
+        else:
+            # XXX: we might also need to handle ARCHIVED_ARCHES, so this works
+            # for mksetupini invoked on an x86 repo with new packages made by
+            # future versions of cygport which generate arch-tagged packages (it
+            # might be better if we had an idea what the valid arches are here)
+            arch_re = r'(-' + '|'.join(common_constants.ARCHES) + r'|)'
 
-    for f in list(files):
         # warn if filename doesn't follow P-V-R naming convention
         #
         # P must match the package name, V can contain anything, R must
         # start with a number and can't include a hyphen
-        match = re.match(r'^' + re.escape(p) + r'-(.+)-(\d[0-9a-zA-Z._+]*)(-src|)\.(tar' + common_constants.PACKAGE_COMPRESSIONS_RE + r'|hint)$', f)
+        match = re.match(r'^' + re.escape(p) + r'-(.+)-(\d[0-9a-zA-Z._+]*)' + arch_re + r'\.(tar' + common_constants.PACKAGE_COMPRESSIONS_RE + r'|hint)$', f)
         if not match:
-            logging.error("file '%s' in package '%s' doesn't follow naming convention" % (f, p))
+            logging.error("file '%s' in package '%s' doesn't follow naming convention" % (rp.fn, p))
             return True
         else:
             v = match.group(1)
             r = match.group(2)
+            arch_tag = match.group(3)
 
             # historically, V can contain a '-' (since we can use the fact
             # we already know P to split unambiguously), but this is a bad
@@ -491,33 +535,46 @@ def read_one_package(packages, p, relpath, dirpath, files, kind, strict):
 
             # collect the attributes for each tar file
             t = Tar()
-            t.repopath.arch = arch
-            t.repopath.path = pkgpath
-            t.repopath.fn = f
-            t.size = os.path.getsize(os.path.join(dirpath, f))
-            t.is_empty = tarfile_is_empty(os.path.join(dirpath, f))
-            t.mtime = os.path.getmtime(os.path.join(dirpath, f))
-            t.sha512 = sha512_file(os.path.join(dirpath, f))
+            t.repopath = rp
+            t.size = os.path.getsize(rp.abspath(basedir))
+            t.is_empty = tarfile_is_empty(rp.abspath(basedir))
+            t.mtime = os.path.getmtime(rp.abspath(basedir))
+            t.sha512 = sha512_file(rp.abspath(basedir))
 
             tars[vr] = t
 
+            # it's an error to not have a corresponding pvr.hint in the same directory
+            hint_fn = '%s-%s%s.hint' % (p, vr, arch_tag)
+            hrp = RepoPath(rp.arch, rp.path, hint_fn)
+            if hrp not in files:
+                logging.error("package %s has packages for version %s, but no %s" % (p, vr, hint_fn))
+                return True
+
+        else:
+            # for each version, a package can contain at most one hint
+            # file. Warn if we have too many (for e.g. both with and without an
+            # archtag).
+            if vr in hint_files:
+                logging.error("package '%s' has more than one hint file for version '%s'" % (p, vr))
+                return True
+            hint_files[vr] = rp
+
+        # collect the superpackage names for authorization purposes
+        auth_path.add(rp.path.split(os.sep, 1)[0])
+
     # determine hints for each version we've encountered
     version_hints = {}
     hints = {}
     actual_tars = {}
     for vr in vr_list:
-        hint_fn = '%s-%s%s.hint' % (p, vr, '-src' if kind == Kind.source else '')
-        if hint_fn in files:
-            # is there a PVR.hint file?
-            pvr_hint = read_hints(p, os.path.join(dirpath, hint_fn), hint.pvr if kind == Kind.binary else hint.spvr, strict)
-            if not pvr_hint:
-                logging.error("error parsing %s" % (os.path.join(dirpath, hint_fn)))
-                return True
-            warnings = clean_hints(p, pvr_hint, warnings)
-        else:
-            # it's an error to not have a pvr.hint
-            logging.error("package %s has packages for version %s, but no %s" % (p, vr, hint_fn))
+        rp = hint_files[vr]
+
+        # is there a PVR.hint file?
+        pvr_hint = read_hints(p, rp.abspath(basedir), hint.pvr if kind == Kind.binary else hint.spvr, strict)
+        if not pvr_hint:
+            logging.error("error parsing %s" % (rp.fn))
             return True
+        warnings = clean_hints(p, pvr_hint, warnings)
 
         # apply a version override
         if 'version' in pvr_hint:
@@ -532,9 +589,7 @@ def read_one_package(packages, p, relpath, dirpath, files, kind, strict):
             pvr_hint['external-source'] += '-src'
 
         hintobj = Hint()
-        hintobj.repopath.arch = arch
-        hintobj.repopath.path = pkgpath
-        hintobj.repopath.fn = hint_fn
+        hintobj.repopath = rp
         hintobj.hints = pvr_hint
 
         version_hints[ovr] = pvr_hint
@@ -548,7 +603,7 @@ def read_one_package(packages, p, relpath, dirpath, files, kind, strict):
     packages[pn].override_hints = override_hints
     packages[pn].tarfiles = actual_tars
     packages[pn].hints = hints
-    packages[pn].pkgpath = pkgpath
+    packages[pn].auth_path = auth_path
     packages[pn].kind = kind
     # since we are kind of inventing the source package names, and don't
     # want to report them, keep track of the real name
@@ -692,6 +747,7 @@ def validate_packages(args, packages, valid_provides_extra=None, missing_obsolet
     error = False
 
     if packages is None:
+        logging.error("package set is empty")
         return False
 
     if missing_obsolete_extra is None:
@@ -1146,10 +1202,11 @@ def validate_package_maintainers(args, packages):
             continue
         if any(['_obsolete' in packages[p].version_hints[vr].get('category', '') for vr in packages[p].version_hints]):
             continue
-        # validate that the package is in a path which starts with something in the package list
-        if not is_in_package_list(packages[p].pkgpath, all_packages):
-            logging.error("package '%s' on path '%s', which doesn't start with a package in the package list" % (p, packages[p].pkgpath))
-            error = True
+        # validate that the package's auth_paths are all in the package list
+        for a in packages[p].auth_path:
+            if a not in all_packages:
+                logging.error("package '%s' exists in '%s', which isn't in the package list" % (p, a))
+                error = True
         # source which is superseded by a different package, but retained due to
         # old install versions can be unmaintained and non-obsolete
         if packages[p].kind == Kind.source:
@@ -1504,11 +1561,7 @@ def merge(a, *l):
                     logging.error("package '%s' is of more than one kind" % (p))
                     return None
 
-                # package must exist at same relative path
-                if c[p].pkgpath != b[p].pkgpath:
-                    logging.error("package '%s' is at paths %s and %s" % (p, c[p].pkgpath, b[p].pkgpath))
-                    return None
-                else:
+                if True:
                     for vr in b[p].tarfiles:
                         if vr in c[p].tarfiles:
                             logging.error("package '%s' has duplicate tarfile for version %s" % (p, vr))
@@ -1534,6 +1587,9 @@ def merge(a, *l):
                     # merge hint file lists
                     c[p].hints.update(b[p].hints)
 
+                    # merge auth_path sets
+                    c[p].auth_path.update(b[p].auth_path)
+
     return c
 
 
@@ -1542,11 +1598,16 @@ def merge(a, *l):
 #
 
 def delete(packages, path, fn):
+    logging.debug("removing: %s/%s" % (path, fn))
+
     ex_packages = []
-    (_, _, pkgpath) = path.split(os.sep, 2)
+
+    # a clunky way of determining the package which owns these files
+    # (which still doesn't know if it's the source or binary package)
+    pn = path.split(os.sep)[-1]
 
     for p in packages:
-        if packages[p].pkgpath == pkgpath:
+        if packages[p].orig_name == pn:
             for vr in packages[p].tarfiles:
                 if packages[p].tarfiles[vr].repopath.fn == fn:
                     del packages[p].tarfiles[vr]
@@ -1568,31 +1629,9 @@ def delete(packages, path, fn):
         del packages[p]
 
 
-#
-# verify that the package path starts with a package in the list of packages
-#
-# (This means that a maintainer can upload a package with any name, provided the
-# path contains one allowed for that maintainer)
-#
-# This avoids the need to have to explicitly list foo, foo_autorebase,
-# foo-devel, foo-doc, foo-debuginfo, libfoo0, girepository-foo, etc. instead of
-# just foo in the package list
-#
-# But means only the rule that a package can't exist in multiple paths prevents
-# arbitrary package upload.
-#
-
-
-def is_in_package_list(ppath, plist):
-    superpackage = ppath.split(os.sep, 1)[0]
-    return superpackage in plist
-
-
 #
 # helper function to mark a package version as fresh (not stale)
 #
-
-
 @unique
 class Freshness(IntEnum):
     # ordered most dominant first
diff --git a/calm/pkg2html.py b/calm/pkg2html.py
index c619bb9..86a41df 100755
--- a/calm/pkg2html.py
+++ b/calm/pkg2html.py
@@ -506,10 +506,14 @@ def write_arch_listing(args, packages, arch):
         else:
             listings = []
 
-        for to in packages[p].tarfiles.values():
-            tn = to.repopath.fn
-            fver = re.sub(r'\.tar.*$', '', tn)
-            listing = os.path.join(dirpath, fver)
+        for v in packages[p].versions():
+            to = packages[p].tar(v)
+
+            # the way this filename is built is pretty arbitrary, but is linked
+            # to from the summary page, and package-grep relies on its knowledge
+            # of the scheme when producing its output
+            fn = packages[p].orig_name + '-' + v + ('-src' if packages[p].kind == package.Kind.source else '')
+            listing = os.path.join(dirpath, fn)
 
             # ... if it doesn't already exist, or --force --force
             if not os.path.exists(listing) or (args.force > 1):
@@ -522,7 +526,7 @@ def write_arch_listing(args, packages, arch):
                         bv = packages[p].best_version
                         desc = sdesc(packages[p], bv)
 
-                        if fver.endswith('-src'):
+                        if packages[p].kind == package.Kind.source:
                             desc = desc + " (source)"
 
                         print(textwrap.dedent('''\
@@ -569,8 +573,8 @@ def write_arch_listing(args, packages, arch):
             if listing in toremove:
                 toremove.remove(listing)
 
-            if fver in listings:
-                listings.remove(fver)
+            if fn in listings:
+                listings.remove(fn)
 
         # some versions remain on toremove list, and will be removed, so summary
         # needs updating
diff --git a/calm/uploads.py b/calm/uploads.py
index fcb23cd..a58ac80 100644
--- a/calm/uploads.py
+++ b/calm/uploads.py
@@ -31,7 +31,6 @@ import os
 import re
 import shutil
 import time
-from collections import defaultdict, namedtuple
 
 import xtarfile
 
@@ -45,8 +44,16 @@ REMINDER_INTERVAL = 60 * 60 * 24 * 7
 # reminders don't start to be issued until an hour after upload
 REMINDER_GRACE = 60 * 60
 
-# a named tuple type to hold the result of scan
-ScanResult = namedtuple('ScanResult', 'error,packages,to_relarea,to_vault,remove_always,remove_success')
+
+# a type to hold the result of scan
+class ScanResult:
+    def __init__(self, error, packages, move, vault, remove, remove_success):
+        self.error = error
+        self.packages = packages
+        self.to_relarea = move
+        self.to_vault = vault
+        self.remove_always = remove
+        self.remove_success = remove_success
 
 
 #
@@ -57,7 +64,7 @@ def scan(scandir, m, all_packages, arch, args):
     homedir = os.path.join(scandir, m.name)
     basedir = os.path.join(homedir, arch)
 
-    packages = defaultdict(package.Package)
+    packages = {}
     move = MoveList(homedir)
     vault = MoveList()
     remove = []
@@ -137,15 +144,21 @@ def scan(scandir, m, all_packages, arch, args):
         if not files:
             continue
 
+        # two quick checks if the upload is allowed
+        #
+        # (removal is only controlled by these checks, uploads are more complex,
+        # with the full auth check in auth_check() below)
+
         # package doesn't appear in package list at all
         (_, _, pkgpath) = relpath.split(os.sep, 2)
-        if not package.is_in_package_list(pkgpath, all_packages):
-            logging.error("package '%s' is not in the package list" % relpath)
+        superpackage = pkgpath.split(os.sep, 1)[0]
+        if superpackage not in all_packages:
+            logging.error("package '%s' is not in the package list" % superpackage)
             continue
 
         # only process packages for which we are listed as a maintainer, or we are a trusted maintainer
-        if not (package.is_in_package_list(pkgpath, m.pkgs) or (m.name in args.trustedmaint.split('/'))):
-            logging.warning("package '%s' is not in the package list for maintainer '%s'" % (relpath, m.name))
+        if not ((superpackage in m.pkgs) or (m.name in args.trustedmaint.split('/'))):
+            logging.warning("package '%s' is not in the package list for maintainer '%s'" % (superpackage, m.name))
             continue
 
         # see if we can fix-up any setup.hint files
@@ -261,6 +274,7 @@ def scan(scandir, m, all_packages, arch, args):
                 continue
 
             # does file already exist in release area?
+            # XXX: this needs to be done later to be multipath aware
             dest = os.path.join(args.rel_area, relpath, f)
             if os.path.isfile(dest):
                 if not f.endswith('.hint'):
@@ -285,7 +299,17 @@ def scan(scandir, m, all_packages, arch, args):
 
         # read and validate package
         if files:
-            if package.read_package_dir(packages, homedir, dirpath, files, upload=True):
+            collected = {}
+            result = package.collect_files_package_dir(collected, homedir, dirpath, files)
+            for p in collected:
+                fl = collected[p]
+                for kind in package.Kind:
+                    if not fl[kind]:
+                        continue
+
+                    result = package.read_one_package(packages, p, homedir, fl[kind] + fl['all'], kind, strict=True) or result
+
+            if result:
                 error = True
 
     # always consider timestamp as checked during a dry-run, so it is never
@@ -316,3 +340,35 @@ def remove(args, remove):
                 os.unlink(f)
             except FileNotFoundError:
                 logging.error("%s can't be deleted as it doesn't exist" % (f))
+
+
+#
+# upload authorization check
+#
+# We want to avoid the need to have to explicitly list foo, foo_autorebase,
+# foo-devel, foo-doc, foo-debuginfo, libfoo0, girepository-foo, etc. instead of
+# just foo in the package list
+#
+# To prevent arbitrary package upload, we need to check that for all packages
+# uploaded, the uploader is authorized for all the existing places (auth_paths)
+# the package exists as well...
+#
+def auth_check(args, m, scan_result, packages):
+    # if uploader is a trusted maintainer, it's ok
+    if m.name in args.trustedmaint.split('/'):
+        return
+
+    # A package upload (perhaps at a new path (= from a different source
+    # package)) is only allowed if the maintainer is also allowed to upload to
+    # all of the auth_paths (= source packages) for the existing
+    # package. (i.e. you can upload a new version of a package in a different
+    # place, if you are allowed to upload in the old place as well)
+    for p in scan_result.packages:
+        for arch in packages:
+            if p in packages[arch]:
+                for ap in packages[arch][p].auth_path:
+                    if ap not in m.pkgs:
+                        logging.error("package '%s' needs authorization '%s' not in the package list for maintainer '%s'" % (p, ap, m.name))
+                        scan_result.error = True
+                    else:
+                        logging.debug("package '%s' has authorization '%s' in the package list for maintainer '%s'" % (p, ap, m.name))
diff --git a/test/test_calm.py b/test/test_calm.py
index c494b76..a61a644 100755
--- a/test/test_calm.py
+++ b/test/test_calm.py
@@ -391,6 +391,7 @@ class CalmTest(unittest.TestCase):
         args.force = False
         args.pkglist = 'testdata/pkglist/cygwin-pkg-maint'
         args.stale = True
+        args.trustedmaint = ''
 
         # set appropriate !ready
         m_homedir = os.path.join(args.homedir, 'Blooey McFooey')
@@ -407,6 +408,41 @@ class CalmTest(unittest.TestCase):
                 compare_with_expected_file(self, 'testdata/conflict', dirlist, d)
                 shutil.rmtree(getattr(args, d))
 
+    def test_process_upload_no_auth(self):
+        self.maxDiff = None
+
+        args = types.SimpleNamespace()
+
+        for d in ARGDIRS:
+            setattr(args, d, tempfile.mktemp())
+            logging.info('%s = %s', d, getattr(args, d))
+
+        shutil.copytree('testdata/relarea', args.rel_area)
+        shutil.copytree('testdata/homes.no_auth', args.homedir)
+        os.mkdir(args.stagingdir)
+
+        args.dryrun = False
+        args.email = None
+        args.force = False
+        args.pkglist = 'testdata/pkglist/cygwin-pkg-maint'
+        args.stale = True
+        args.trustedmaint = ''
+
+        # set appropriate !ready
+        m_homedir = os.path.join(args.homedir, 'Blooey McFooey')
+        os.system('touch "%s"' % (os.path.join(m_homedir, 'x86_64', 'release', 'testpackage', '!ready')))
+
+        state = calm.calm.CalmState()
+        state.packages = calm.calm.process_relarea(args, state)
+        state.packages = calm.calm.process_uploads(args, state)
+        self.assertTrue(state.packages)
+
+        for d in ARGDIRS:
+            with self.subTest(directory=d):
+                dirlist = capture_dirtree(getattr(args, d))
+                compare_with_expected_file(self, 'testdata/upload_bad_auth', dirlist, d)
+                shutil.rmtree(getattr(args, d))
+
     def test_process(self):
         self.maxDiff = None
 
@@ -425,6 +461,7 @@ class CalmTest(unittest.TestCase):
         args.repodir = 'testdata/repodir'
         args.setup_version = '3.1415'
         args.stale = True
+        args.trustedmaint = ''
 
         state = calm.calm.CalmState()
         state.args = args
diff --git a/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1-src.hint b/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1-src.hint
new file mode 100644
index 0000000..07e6025
--- /dev/null
+++ b/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1-src.hint	
@@ -0,0 +1,5 @@
+sdesc: "The UNIX emulation engine"
+ldesc: "A fraudulent package"
+category: Base
+homepage: https://cygwin.com
+
diff --git a/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1-src.tar.xz b/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1-src.tar.xz
new file mode 100644
index 0000000..0e6f1e8
Binary files /dev/null and b/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1-src.tar.xz differ
diff --git a/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1.hint b/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1.hint
new file mode 100644
index 0000000..9365ed8
--- /dev/null
+++ b/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1.hint	
@@ -0,0 +1,4 @@
+sdesc: "The UNIX emulation engine"
+ldesc: "A fraudulent packagee"
+category: Base
+requires: base-cygwin _windows ( >= 6.0 )
diff --git a/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1.tar.xz b/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1.tar.xz
new file mode 100644
index 0000000..0e6f1e8
Binary files /dev/null and b/test/testdata/homes.no_auth/Blooey McFooey/x86_64/release/testpackage/cygwin/cygwin-3.0.0-1.tar.xz differ
diff --git a/test/testdata/htdocs.expected/dirtree.expected b/test/testdata/htdocs.expected/dirtree.expected
index ec63f08..ce68626 100644
--- a/test/testdata/htdocs.expected/dirtree.expected
+++ b/test/testdata/htdocs.expected/dirtree.expected
@@ -93,8 +93,8 @@
                              'staleversion-260-0-src'],
  'x86_64/test-c': ['.htaccess', 'test-c-1.0-1'],
  'x86_64/test-c-src': ['.htaccess', 'test-c-1.0-1-src'],
- 'x86_64/test-d': ['.htaccess', 'test-d-1.0-1'],
- 'x86_64/test-d-src': ['.htaccess', 'test-d-1.0-1-src'],
+ 'x86_64/test-d': ['.htaccess', 'test-d-1.0.42590-1'],
+ 'x86_64/test-d-src': ['.htaccess', 'test-d-1.0.42590-1-src'],
  'x86_64/test-e': ['.htaccess', 'test-e-1.0-1'],
  'x86_64/test-e-src': ['.htaccess', 'test-e-1.0-1-src'],
  'x86_64/testpackage': ['.htaccess', 'testpackage-0.1-1'],
diff --git a/test/testdata/htdocs.expected/x86_64/test-d-src/test-d-1.0-1-src b/test/testdata/htdocs.expected/x86_64/test-d-src/test-d-1.0.42590-1-src
similarity index 100%
rename from test/testdata/htdocs.expected/x86_64/test-d-src/test-d-1.0-1-src
rename to test/testdata/htdocs.expected/x86_64/test-d-src/test-d-1.0.42590-1-src
diff --git a/test/testdata/htdocs.expected/x86_64/test-d/test-d-1.0-1 b/test/testdata/htdocs.expected/x86_64/test-d/test-d-1.0.42590-1
similarity index 100%
rename from test/testdata/htdocs.expected/x86_64/test-d/test-d-1.0-1
rename to test/testdata/htdocs.expected/x86_64/test-d/test-d-1.0.42590-1
diff --git a/test/testdata/htdocs.expected/x86_64/test-d/test-d-1.0-1-src b/test/testdata/htdocs.expected/x86_64/test-d/test-d-1.0.42590-1-src
similarity index 100%
rename from test/testdata/htdocs.expected/x86_64/test-d/test-d-1.0-1-src
rename to test/testdata/htdocs.expected/x86_64/test-d/test-d-1.0.42590-1-src
diff --git a/test/testdata/process_arch/htdocs.expected b/test/testdata/process_arch/htdocs.expected
index f63f346..80e2fdf 100644
--- a/test/testdata/process_arch/htdocs.expected
+++ b/test/testdata/process_arch/htdocs.expected
@@ -83,8 +83,8 @@
  'x86_64/staleversion-src': ['.htaccess', 'staleversion-243-0-src', 'staleversion-250-0-src', 'staleversion-260-0-src'],
  'x86_64/test-c': ['.htaccess', 'test-c-1.0-1'],
  'x86_64/test-c-src': ['.htaccess', 'test-c-1.0-1-src'],
- 'x86_64/test-d': ['.htaccess', 'test-d-1.0-1'],
- 'x86_64/test-d-src': ['.htaccess', 'test-d-1.0-1-src'],
+ 'x86_64/test-d': ['.htaccess', 'test-d-1.0.42590-1'],
+ 'x86_64/test-d-src': ['.htaccess', 'test-d-1.0.42590-1-src'],
  'x86_64/test-e': ['.htaccess', 'test-e-1.0-1'],
  'x86_64/test-e-src': ['.htaccess', 'test-e-1.0-1-src'],
  'x86_64/testpackage': ['.htaccess', 'testpackage-1.0-1'],
diff --git a/test/testdata/upload_bad_auth/homedir.expected b/test/testdata/upload_bad_auth/homedir.expected
new file mode 100644
index 0000000..7d4a92b
--- /dev/null
+++ b/test/testdata/upload_bad_auth/homedir.expected
@@ -0,0 +1,9 @@
+{'.': [],
+ 'Blooey McFooey': [],
+ 'Blooey McFooey/x86_64': [],
+ 'Blooey McFooey/x86_64/release': [],
+ 'Blooey McFooey/x86_64/release/testpackage': [],
+ 'Blooey McFooey/x86_64/release/testpackage/cygwin': ['cygwin-3.0.0-1-src.hint',
+                                                      'cygwin-3.0.0-1-src.tar.xz',
+                                                      'cygwin-3.0.0-1.hint',
+                                                      'cygwin-3.0.0-1.tar.xz']}
diff --git a/test/testdata/upload_bad_auth/htdocs.expected b/test/testdata/upload_bad_auth/htdocs.expected
new file mode 100644
index 0000000..6d3735d
--- /dev/null
+++ b/test/testdata/upload_bad_auth/htdocs.expected
@@ -0,0 +1 @@
+{'.': ['calm.db']}
diff --git a/test/testdata/upload_bad_auth/rel_area.expected b/test/testdata/upload_bad_auth/rel_area.expected
new file mode 100644
index 0000000..ce4225b
--- /dev/null
+++ b/test/testdata/upload_bad_auth/rel_area.expected
@@ -0,0 +1,182 @@
+{'.': [],
+ 'noarch': ['sha512.sum'],
+ 'noarch/release': ['sha512.sum'],
+ 'noarch/release/obs-a': ['obs-a-1.0-1-src.hint',
+                          'obs-a-1.0-1-src.tar.xz',
+                          'obs-a-1.0-1.hint',
+                          'obs-a-1.0-1.tar.xz',
+                          'sha512.sum'],
+ 'noarch/release/obs-b': ['obs-b-1.0-1-src.hint',
+                          'obs-b-1.0-1-src.tar.xz',
+                          'obs-b-1.0-1.hint',
+                          'obs-b-1.0-1.tar.xz',
+                          'sha512.sum'],
+ 'noarch/release/perl-Net-SMTP-SSL': ['perl-Net-SMTP-SSL-1.01-1-src.hint',
+                                      'perl-Net-SMTP-SSL-1.01-1-src.tar.xz',
+                                      'perl-Net-SMTP-SSL-1.01-1.hint',
+                                      'perl-Net-SMTP-SSL-1.01-1.tar.xz',
+                                      'perl-Net-SMTP-SSL-1.02-1-src.hint',
+                                      'perl-Net-SMTP-SSL-1.02-1-src.tar.xz',
+                                      'perl-Net-SMTP-SSL-1.02-1.hint',
+                                      'perl-Net-SMTP-SSL-1.02-1.tar.xz',
+                                      'perl-Net-SMTP-SSL-1.03-1-src.hint',
+                                      'perl-Net-SMTP-SSL-1.03-1-src.tar.xz',
+                                      'perl-Net-SMTP-SSL-1.03-1.hint',
+                                      'perl-Net-SMTP-SSL-1.03-1.tar.xz',
+                                      'sha512.sum'],
+ 'noarch/release/test-c': ['sha512.sum',
+                           'test-c-1.0-1-src.hint',
+                           'test-c-1.0-1-src.tar.xz',
+                           'test-c-1.0-1.hint',
+                           'test-c-1.0-1.tar.xz'],
+ 'noarch/release/test-d': ['sha512.sum',
+                           'test-d-1.0-1-src.hint',
+                           'test-d-1.0-1-src.tar.xz',
+                           'test-d-1.0-1.hint',
+                           'test-d-1.0-1.tar.xz'],
+ 'noarch/release/test-e': ['sha512.sum',
+                           'test-e-1.0-1-src.hint',
+                           'test-e-1.0-1-src.tar.xz',
+                           'test-e-1.0-1.hint',
+                           'test-e-1.0-1.tar.xz'],
+ 'x86_64': ['sha512.sum'],
+ 'x86_64/release': ['sha512.sum'],
+ 'x86_64/release/arc': ['arc-4.32.7-10-src.hint',
+                        'arc-4.32.7-10-src.tar.bz2',
+                        'arc-4.32.7-10.hint',
+                        'arc-4.32.7-10.tar.bz2'],
+ 'x86_64/release/base-cygwin': ['base-cygwin-3.6-1.hint',
+                                'base-cygwin-3.6-1.tar.xz',
+                                'base-cygwin-3.8-1.hint',
+                                'base-cygwin-3.8-1.tar.xz',
+                                'sha512.sum'],
+ 'x86_64/release/corrupt': ['corrupt-2.0.0-1-src.hint',
+                            'corrupt-2.0.0-1-src.tar.xz',
+                            'corrupt-2.0.0-1.hint',
+                            'corrupt-2.0.0-1.tar.xz',
+                            'sha512.sum'],
+ 'x86_64/release/cygwin': ['.this-should-be-ignored',
+                           'cygwin-2.2.0-1-src.hint',
+                           'cygwin-2.2.0-1-src.tar.xz',
+                           'cygwin-2.2.0-1.hint',
+                           'cygwin-2.2.0-1.tar.xz',
+                           'cygwin-2.2.1-1-src.hint',
+                           'cygwin-2.2.1-1-src.tar.xz',
+                           'cygwin-2.2.1-1.hint',
+                           'cygwin-2.2.1-1.tar.xz',
+                           'cygwin-2.3.0-0.3-src.hint',
+                           'cygwin-2.3.0-0.3-src.tar.xz',
+                           'cygwin-2.3.0-0.3.hint',
+                           'cygwin-2.3.0-0.3.tar.xz',
+                           'override.hint',
+                           'sha512.sum'],
+ 'x86_64/release/cygwin/cygwin-debuginfo': ['cygwin-debuginfo-2.2.0-1.hint',
+                                            'cygwin-debuginfo-2.2.0-1.tar.xz',
+                                            'cygwin-debuginfo-2.2.1-1.hint',
+                                            'cygwin-debuginfo-2.2.1-1.tar.xz',
+                                            'cygwin-debuginfo-2.3.0-0.3.hint',
+                                            'cygwin-debuginfo-2.3.0-0.3.tar.xz',
+                                            'override.hint',
+                                            'sha512.sum'],
+ 'x86_64/release/cygwin/cygwin-devel': ['cygwin-devel-2.2.0-1.hint',
+                                        'cygwin-devel-2.2.0-1.tar.xz',
+                                        'cygwin-devel-2.2.1-1.hint',
+                                        'cygwin-devel-2.2.1-1.tar.xz',
+                                        'cygwin-devel-2.3.0-0.3.hint',
+                                        'cygwin-devel-2.3.0-0.3.tar.xz',
+                                        'override.hint',
+                                        'sha512.sum'],
+ 'x86_64/release/invalid': ['invalid-0.hint', 'sha512.sum'],
+ 'x86_64/release/keychain': ['keychain-2.6.8-1-src.hint',
+                             'keychain-2.6.8-1-src.tar.bz2',
+                             'keychain-2.6.8-1.hint',
+                             'keychain-2.6.8-1.tar.bz2',
+                             'keychain-2.7.1-1-src.hint',
+                             'keychain-2.7.1-1-src.tar.bz2',
+                             'keychain-2.7.1-1.hint',
+                             'keychain-2.7.1-1.tar.bz2',
+                             'sha512.sum'],
+ 'x86_64/release/libspiro': ['libspiro-20071029-1.hint', 'sha512.sum'],
+ 'x86_64/release/libspiro/libspiro-devel': ['libspiro-devel-20071029-1.hint', 'sha512.sum'],
+ 'x86_64/release/libspiro/libspiro0': ['libspiro0-20071029-1.hint', 'sha512.sum'],
+ 'x86_64/release/libtextcat': ['libtextcat-2.2-2-src.hint',
+                               'libtextcat-2.2-2-src.tar.bz2',
+                               'libtextcat-2.2-2.hint',
+                               'libtextcat-2.2-2.tar.bz2',
+                               'sha512.sum'],
+ 'x86_64/release/libtextcat/libtextcat-devel': ['libtextcat-devel-2.2-2.hint',
+                                                'libtextcat-devel-2.2-2.tar.bz2',
+                                                'sha512.sum'],
+ 'x86_64/release/libtextcat/libtextcat0': ['libtextcat0-2.2-2.hint', 'libtextcat0-2.2-2.tar.bz2', 'sha512.sum'],
+ 'x86_64/release/mDNSResponder': ['mDNSResponder-379.32.1-1-src.hint',
+                                  'mDNSResponder-379.32.1-1-src.tar.bz2',
+                                  'mDNSResponder-379.32.1-1.hint',
+                                  'mDNSResponder-379.32.1-1.tar.bz2',
+                                  'sha512.sum'],
+ 'x86_64/release/mDNSResponder/libdns_sd-devel': ['libdns_sd-devel-379.32.1-1.hint',
+                                                  'libdns_sd-devel-379.32.1-1.tar.bz2',
+                                                  'sha512.sum'],
+ 'x86_64/release/mDNSResponder/libdns_sd1': ['libdns_sd1-379.32.1-1.hint',
+                                             'libdns_sd1-379.32.1-1.tar.bz2',
+                                             'sha512.sum'],
+ 'x86_64/release/mingw64-i686-binutils': ['mingw64-i686-binutils-2.29.1.787c9873-1.hint', 'sha512.sum'],
+ 'x86_64/release/mingw64-i686-binutils/mingw64-i686-binutils-debuginfo': ['mingw64-i686-binutils-debuginfo-2.29.1.787c9873-1.hint',
+                                                                          'sha512.sum'],
+ 'x86_64/release/openssh': ['openssh-7.2p2-1-src.hint',
+                            'openssh-7.2p2-1-src.tar.xz',
+                            'openssh-7.2p2-1.hint',
+                            'openssh-7.2p2-1.tar.xz',
+                            'sha512.sum'],
+ 'x86_64/release/per-version': ['override.hint',
+                                'per-version-4.0-1-src.hint',
+                                'per-version-4.0-1-src.tar.xz',
+                                'per-version-4.0-1.hint',
+                                'per-version-4.0-1.tar.xz',
+                                'per-version-4.8-1-src.hint',
+                                'per-version-4.8-1-src.tar.xz',
+                                'per-version-4.8-1.hint',
+                                'per-version-4.8-1.tar.xz',
+                                'sha512.sum'],
+ 'x86_64/release/per-version-incomplete': ['override.hint',
+                                           'per-version-incomplete-36-1-src.hint',
+                                           'per-version-incomplete-36-1-src.tar.xz',
+                                           'per-version-incomplete-36-1.hint',
+                                           'per-version-incomplete-36-1.tar.xz',
+                                           'per-version-incomplete-39-1-src.tar.xz',
+                                           'per-version-incomplete-39-1.tar.xz',
+                                           'sha512.sum'],
+ 'x86_64/release/per-version-replacement-hint-only': ['per-version-replacement-hint-only-1.0-1-src.hint',
+                                                      'per-version-replacement-hint-only-1.0-1-src.tar.xz',
+                                                      'per-version-replacement-hint-only-1.0-1.hint',
+                                                      'per-version-replacement-hint-only-1.0-1.tar.xz',
+                                                      'sha512.sum'],
+ 'x86_64/release/proj': ['proj-4.8.0-1.hint', 'sha512.sum'],
+ 'x86_64/release/proj/libproj-devel': ['libproj-devel-4.8.0-1.hint', 'sha512.sum'],
+ 'x86_64/release/proj/libproj1': ['libproj1-4.8.0-1.hint', 'sha512.sum'],
+ 'x86_64/release/rpm-doc': ['rpm-doc-4.1-2-src.hint',
+                            'rpm-doc-4.1-2-src.tar.bz2',
+                            'rpm-doc-4.1-2.hint',
+                            'rpm-doc-4.1-2.tar.bz2',
+                            'rpm-doc-999-1.hint',
+                            'rpm-doc-999-1.tar.bz2',
+                            'sha512.sum'],
+ 'x86_64/release/splint': ['sha512.sum', 'splint-3.1.2-1.hint'],
+ 'x86_64/release/staleversion': ['override.hint',
+                                 'sha512.sum',
+                                 'staleversion-243-0-src.hint',
+                                 'staleversion-243-0-src.tar.xz',
+                                 'staleversion-243-0.hint',
+                                 'staleversion-243-0.tar.xz',
+                                 'staleversion-250-0-src.hint',
+                                 'staleversion-250-0-src.tar.xz',
+                                 'staleversion-250-0.hint',
+                                 'staleversion-250-0.tar.xz',
+                                 'staleversion-260-0-src.hint',
+                                 'staleversion-260-0-src.tar.xz',
+                                 'staleversion-260-0.hint',
+                                 'staleversion-260-0.tar.xz'],
+ 'x86_64/release/testpackage': ['sha512.sum',
+                                'testpackage-0.1-1-src.hint',
+                                'testpackage-0.1-1-src.tar.bz2',
+                                'testpackage-0.1-1.hint',
+                                'testpackage-0.1-1.tar.bz2']}
diff --git a/test/testdata/upload_bad_auth/stagingdir.expected b/test/testdata/upload_bad_auth/stagingdir.expected
new file mode 100644
index 0000000..e82d3c8
--- /dev/null
+++ b/test/testdata/upload_bad_auth/stagingdir.expected
@@ -0,0 +1 @@
+{'.': []}
diff --git a/test/testdata/upload_bad_auth/vault.expected b/test/testdata/upload_bad_auth/vault.expected
new file mode 100644
index 0000000..edf2c65
--- /dev/null
+++ b/test/testdata/upload_bad_auth/vault.expected
@@ -0,0 +1,15 @@
+{'.': [],
+ 'x86_64': [],
+ 'x86_64/release': [],
+ 'x86_64/release/staleversion': ['staleversion-240-1-src.hint',
+                                 'staleversion-240-1-src.tar.xz',
+                                 'staleversion-240-1.hint',
+                                 'staleversion-240-1.tar.xz',
+                                 'staleversion-242-0-src.hint',
+                                 'staleversion-242-0-src.tar.xz',
+                                 'staleversion-242-0.hint',
+                                 'staleversion-242-0.tar.xz',
+                                 'staleversion-251-0-src.hint',
+                                 'staleversion-251-0-src.tar.xz',
+                                 'staleversion-251-0.hint',
+                                 'staleversion-251-0.tar.xz']}
diff --git a/test/testdata/uploads/pkglist.expected b/test/testdata/uploads/pkglist.expected
index 224e4bf..59e9289 100644
--- a/test/testdata/uploads/pkglist.expected
+++ b/test/testdata/uploads/pkglist.expected
@@ -5,7 +5,7 @@
                     'Bézier."',
            'category': 'Devel',
            'depends': ['cygwin']}}, {}, False),
- 'testpackage-src': Package('testpackage', {'1.0-1': Tar('testpackage-1.0-1-src.tar.bz2', 'x86_64/release/testpackage', 'acfd77df3347e6432ccf29c12989964bc680a158d574f85dfa7ef222759f411006c7bd2773e37c5abdee628bea769b2da9aae213db615cd91402fd385373933d', 266, False)}, {'1.0-1': {'sdesc': '"A test package"',
+ 'testpackage-src': Package('testpackage-src', {'1.0-1': Tar('testpackage-1.0-1-src.tar.bz2', 'x86_64/release/testpackage', 'acfd77df3347e6432ccf29c12989964bc680a158d574f85dfa7ef222759f411006c7bd2773e37c5abdee628bea769b2da9aae213db615cd91402fd385373933d', 266, False)}, {'1.0-1': {'sdesc': '"A test package"',
            'ldesc': '"A test package\n'
                     "It's description might contains some unicode junk\n"
                     'Like it’s you’re Markup Language™ Nokogiri’s tool―that '
@@ -14,7 +14,7 @@
            'homepage': 'http://homepage.url',
            'parse-warnings': ["key 'license' missing"],
            'depends': []}}, {}, False),
- 'testpackage-subpackage': Package('testpackage/testpackage-subpackage', {'1.0-1': Tar('testpackage-subpackage-1.0-1.tar.bz2', 'x86_64/release/testpackage/testpackage-subpackage', 'aff488008bee3486e25b539fe6ccd1397bd3c5c0ba2ee2cf34af279554baa195af7493ee51d6f8510735c9a2ea54436d776a71e768165716762aec286abbbf83', 195, False)}, {'1.0-1': {'sdesc': '"A test subpackage"',
+ 'testpackage-subpackage': Package('testpackage-subpackage', {'1.0-1': Tar('testpackage-subpackage-1.0-1.tar.bz2', 'x86_64/release/testpackage/testpackage-subpackage', 'aff488008bee3486e25b539fe6ccd1397bd3c5c0ba2ee2cf34af279554baa195af7493ee51d6f8510735c9a2ea54436d776a71e768165716762aec286abbbf83', 195, False)}, {'1.0-1': {'sdesc': '"A test subpackage"',
            'ldesc': '"A test subpackage"',
            'category': 'Devel',
            'external-source': 'testpackage-src',
@@ -23,7 +23,7 @@
            'sdesc': '"test package (zstd compressed)"',
            'ldesc': '"test package (zstd compressed)"',
            'depends': []}}, {}, False),
- 'testpackage-zstd-src': Package('testpackage-zstd', {'1.0-1': Tar('testpackage-zstd-1.0-1-src.tar.zst', 'x86_64/release/testpackage-zstd', '90561ec4dad76268773856cbdda891b0e7b53f26492777f1ff76757844cb47124396feb76f1e30bc1baa680f1d788de21d89e612faeb30b5039b210ca9186434', 313, False)}, {'1.0-1': {'category': 'Base',
+ 'testpackage-zstd-src': Package('testpackage-zstd-src', {'1.0-1': Tar('testpackage-zstd-1.0-1-src.tar.zst', 'x86_64/release/testpackage-zstd', '90561ec4dad76268773856cbdda891b0e7b53f26492777f1ff76757844cb47124396feb76f1e30bc1baa680f1d788de21d89e612faeb30b5039b210ca9186434', 313, False)}, {'1.0-1': {'category': 'Base',
            'build-depends': ['cygport'],
            'sdesc': '"test package (zstd compressed)"',
            'ldesc': '"test package (zstd compressed)"',
@@ -31,7 +31,7 @@
            'skip': '',
            'parse-warnings': ["key 'license' missing"],
            'depends': []}}, {}, False),
- 'testpackage2-subpackage': Package('testpackage2/testpackage2-subpackage', {'1.0-1': Tar('testpackage2-subpackage-1.0-1.tar.bz2', 'x86_64/release/testpackage2/testpackage2-subpackage', 'c4bf8e28d71b532e2b741e2931906dec0f0a70d4d051c0503476f864a5228f43765ae3342aafcebfd5a1738073537726b2bfbbd89c6da939a5f46d95aca3feaf', 46, True)}, {'1.0-1': {'sdesc': '"A test subpackage 2"',
+ 'testpackage2-subpackage': Package('testpackage2-subpackage', {'1.0-1': Tar('testpackage2-subpackage-1.0-1.tar.bz2', 'x86_64/release/testpackage2/testpackage2-subpackage', 'c4bf8e28d71b532e2b741e2931906dec0f0a70d4d051c0503476f864a5228f43765ae3342aafcebfd5a1738073537726b2bfbbd89c6da939a5f46d95aca3feaf', 46, True)}, {'1.0-1': {'sdesc': '"A test subpackage 2"',
            'ldesc': '"A test subpackage 2"',
            'category': 'Devel',
            'depends': []}}, {}, False)}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-04-17 19:03 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-17 19:03 [calm - Cygwin server-side packaging maintenance script] branch master, updated. 20230209-96-g693426a Jon Turney

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).