Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package osc for openSUSE:Factory checked in 
at 2024-07-01 11:22:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/osc (Old)
 and      /work/SRC/openSUSE:Factory/.osc.new.18349 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "osc"

Mon Jul  1 11:22:22 2024 rev:196 rq:1184224 version:1.8.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/osc/osc.changes  2024-05-29 19:37:23.615837229 
+0200
+++ /work/SRC/openSUSE:Factory/.osc.new.18349/osc.changes       2024-07-01 
11:23:10.493176926 +0200
@@ -1,0 +2,39 @@
+Mon Jul  1 08:14:17 UTC 2024 - Daniel Mach <daniel.m...@suse.com>
+
+- 1.8.1
+  - Command-line:
+    - Fix 'linkpac' command crash when used with '--disable-build' or 
'--disable-publish' option
+
+-------------------------------------------------------------------
+Fri Jun 28 12:09:23 UTC 2024 - Daniel Mach <daniel.m...@suse.com>
+
+- 1.8.0
+  - Command-line:
+    - Improve 'submitrequest' command to inherit description from superseded 
request
+    - Fix 'mv' command when renaming a file multiple times
+    - Improve 'info' command to support projects
+    - Improve 'getbinaries' command by accepting '-M' / '--multibuild-package' 
option outside checkouts
+    - Add architecture filtering to 'release' command
+    - Change 'results' command so the normal and multibuild packages have the 
same output
+    - Change 'results' command to use csv writer instead of formatting csv as 
string
+    - Add couple mutually exclusive options errors to 'results' command
+    - Set a default value for 'results --format' only for the csv output
+    - Add support for 'results --format' for the default text mode
+    - Update help text for '--format' option in 'results' command
+    - Add 'results --fail-on-error/-F' flag
+    - Redirect venv warnings from stderr to debug output
+  - Configuration:
+    - Fix config parser to throw an exception on duplicate sections or options
+    - Modify conf.get_config() to print permissions warning to stderr rather 
than stdout
+  - Library:
+    - Run check_store_version() in obs_scm.Store and fix related code in 
Project and Package
+    - Forbid extracting files with absolute path from 'cpio' archives 
(boo#1122683)
+    - Forbid extracting files with absolute path from 'ar' archives 
(boo#1122683)
+    - Remove no longer valid warning from core.unpack_srcrpm()
+    - Make obs_api.KeyinfoSslcert keyid and fingerprint fields optional
+    - Fix return value in build build.create_build_descr_data()
+    - Fix core.get_package_results() to obey 'multibuild_packages' argument
+  - Tests:
+    - Fix tests so they don't modify fixtures
+
+-------------------------------------------------------------------

Old:
----
  osc-1.7.0.tar.gz

New:
----
  osc-1.8.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ osc.spec ++++++
--- /var/tmp/diff_new_pack.ChhHu5/_old  2024-07-01 11:23:11.113199366 +0200
+++ /var/tmp/diff_new_pack.ChhHu5/_new  2024-07-01 11:23:11.113199366 +0200
@@ -67,7 +67,7 @@
 %endif
 
 Name:           osc
-Version:        1.7.0
+Version:        1.8.1
 Release:        0
 Summary:        Command-line client for the Open Build Service
 License:        GPL-2.0-or-later

++++++ PKGBUILD ++++++
--- /var/tmp/diff_new_pack.ChhHu5/_old  2024-07-01 11:23:11.141200380 +0200
+++ /var/tmp/diff_new_pack.ChhHu5/_new  2024-07-01 11:23:11.149200669 +0200
@@ -1,6 +1,6 @@
 pkgname=osc
-pkgver=1.7.0
-pkgrel=bad8565349069252f0de429f71d702f1
+pkgver=1.8.1
+pkgrel=88bf8a6a77d4f6e711b9b16732e40f83
 pkgdesc="Command-line client for the Open Build Service"
 arch=('x86_64')
 url="https://www.github.com/openSUSE/osc";

++++++ debian.changelog ++++++
--- /var/tmp/diff_new_pack.ChhHu5/_old  2024-07-01 11:23:11.193202262 +0200
+++ /var/tmp/diff_new_pack.ChhHu5/_new  2024-07-01 11:23:11.197202406 +0200
@@ -1,4 +1,4 @@
-osc (1.7.0-0) unstable; urgency=low
+osc (1.8.1-0) unstable; urgency=low
 
   * Placeholder
 

++++++ osc-1.7.0.tar.gz -> osc-1.8.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/NEWS new/osc-1.8.1/NEWS
--- old/osc-1.7.0/NEWS  2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/NEWS  2024-07-01 10:11:40.000000000 +0200
@@ -1,3 +1,36 @@
+- 1.8.1
+  - Command-line:
+    - Fix 'linkpac' command crash when used with '--disable-build' or 
'--disable-publish' option
+
+- 1.8.0
+  - Command-line:
+    - Improve 'submitrequest' command to inherit description from superseded 
request
+    - Fix 'mv' command when renaming a file multiple times
+    - Improve 'info' command to support projects
+    - Improve 'getbinaries' command by accepting '-M' / '--multibuild-package' 
option outside checkouts
+    - Add architecture filtering to 'release' command
+    - Change 'results' command so the normal and multibuild packages have the 
same output
+    - Change 'results' command to use csv writer instead of formatting csv as 
string
+    - Add couple mutually exclusive options errors to 'results' command
+    - Set a default value for 'results --format' only for the csv output
+    - Add support for 'results --format' for the default text mode
+    - Update help text for '--format' option in 'results' command
+    - Add 'results --fail-on-error/-F' flag
+    - Redirect venv warnings from stderr to debug output
+  - Configuration:
+    - Fix config parser to throw an exception on duplicate sections or options
+    - Modify conf.get_config() to print permissions warning to stderr rather 
than stdout
+  - Library:
+    - Run check_store_version() in obs_scm.Store and fix related code in 
Project and Package
+    - Forbid extracting files with absolute path from 'cpio' archives 
(boo#1122683)
+    - Forbid extracting files with absolute path from 'ar' archives 
(boo#1122683)
+    - Remove no longer valid warning from core.unpack_srcrpm()
+    - Make obs_api.KeyinfoSslcert keyid and fingerprint fields optional
+    - Fix return value in build build.create_build_descr_data()
+    - Fix core.get_package_results() to obey 'multibuild_packages' argument
+  - Tests:
+    - Fix tests so they don't modify fixtures
+
 - 1.7.0
   - Command-line:
     - Add 'person search' command
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/OscConfigParser.py 
new/osc-1.8.1/osc/OscConfigParser.py
--- old/osc-1.7.0/osc/OscConfigParser.py        2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/osc/OscConfigParser.py        2024-07-01 10:11:40.000000000 
+0200
@@ -263,7 +263,9 @@
                 mo = self.SECTCRE.match(line)
                 if mo:
                     sectname = mo.group('header')
-                    if sectname in self._sections:
+                    if self._strict and sectname in self._sections:
+                        raise configparser.DuplicateSectionError(sectname, 
fpname, lineno)
+                    elif sectname in self._sections:
                         cursect = self._sections[sectname]
                     elif sectname == configparser.DEFAULTSECT:
                         cursect = self._defaults
@@ -294,7 +296,9 @@
                         if optval == '""':
                             optval = ''
                         optname = self.optionxform(optname.rstrip())
-                        if cursect == configparser.DEFAULTSECT:
+                        if self._strict and optname in self._sections[cursect]:
+                            raise configparser.DuplicateOptionError(sectname, 
optname, fpname, lineno)
+                        elif cursect == configparser.DEFAULTSECT:
                             self._defaults[optname] = optval
                         else:
                             self._sections[cursect]._add_option(optname, 
line=line)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/__init__.py 
new/osc-1.8.1/osc/__init__.py
--- old/osc-1.7.0/osc/__init__.py       2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/osc/__init__.py       2024-07-01 10:11:40.000000000 +0200
@@ -13,7 +13,7 @@
 
 
 from .util import git_version
-__version__ = git_version.get_version('1.7.0')
+__version__ = git_version.get_version('1.8.1')
 
 
 # vim: sw=4 et
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/_private/api_source.py 
new/osc-1.8.1/osc/_private/api_source.py
--- old/osc-1.7.0/osc/_private/api_source.py    2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/osc/_private/api_source.py    2024-07-01 10:11:40.000000000 
+0200
@@ -89,6 +89,7 @@
     project,
     package,
     repository,
+    architecture,
     target_project,
     target_repository,
     set_release_to=None,
@@ -102,6 +103,7 @@
         target_project,
         target_package=None,
         repository=repository,
+        architecture=architecture,
         dest_repository=target_repository,
         delayed=delayed,
     )
@@ -114,6 +116,8 @@
     url_query = {"cmd": "release"}
     if repository:
         url_query["repository"] = repository
+    if architecture:
+        url_query["arch"] = architecture
     if target_project:
         url_query["target_project"] = target_project
     if target_repository:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/_private/common.py 
new/osc-1.8.1/osc/_private/common.py
--- old/osc-1.7.0/osc/_private/common.py        2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/osc/_private/common.py        2024-07-01 10:11:40.000000000 
+0200
@@ -8,6 +8,7 @@
     dest_project=None,
     dest_package=None,
     repository=None,
+    architecture=None,
     dest_repository=None,
     **options,
 ):
@@ -34,6 +35,9 @@
     if dest_repository:
         msg += f" repository '{dest_repository}'"
 
+    if architecture:
+        msg += f" architecture '{architecture}'"
+
     msg_options = [key.replace("_", "-") for key, value in options.items() if 
value]
     if msg_options:
         msg_options.sort()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/build.py new/osc-1.8.1/osc/build.py
--- old/osc-1.7.0/osc/build.py  2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/osc/build.py  2024-07-01 10:11:40.000000000 +0200
@@ -732,7 +732,7 @@
                 result_data.append((b"_service", f.read()))
 
     if not result_data and not prefer_pkgs:
-        return None, None
+        return None, {}
 
     cpio_data = cpio.CpioWrite()
     for key, value in result_data:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/commandline.py 
new/osc-1.8.1/osc/commandline.py
--- old/osc-1.7.0/osc/commandline.py    2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/osc/commandline.py    2024-07-01 10:11:40.000000000 +0200
@@ -271,7 +271,7 @@
 
     def load_commands(self):
         if IN_VENV:
-            output.print_msg("Running in virtual environment, skipping loading 
plugins installed outside the virtual environment.", print_to="stderr")
+            output.print_msg("Running in virtual environment, skipping loading 
plugins installed outside the virtual environment.", print_to="debug")
 
         for module_prefix, module_path in self.MODULES:
             module_path = os.path.expanduser(module_path)
@@ -2418,6 +2418,12 @@
                                                                                
  dst_project, None,
                                                                                
  not opts.yes)
         if not opts.message:
+            msg = ""
+            if opts.supersede:
+                from .obs_api import Request
+                req = Request.from_api(apiurl, opts.supersede)
+                msg = req.description + "\n"
+
             difflines = []
             doappend = False
             changes_re = re.compile(r'^--- .*\.changes ')
@@ -2429,7 +2435,9 @@
                         doappend = False
                 if doappend:
                     difflines.append(line)
-            opts.message = edit_message(footer=rdiff, 
template='\n'.join(parse_diff_for_commit_message('\n'.join(difflines))))
+
+            diff = 
"\n".join(parse_diff_for_commit_message("\n".join(difflines)))
+            opts.message = edit_message(footer=rdiff, template=f"{msg}{diff}")
 
         result = create_submit_request(apiurl,
                                        src_project, src_package,
@@ -3922,6 +3930,8 @@
                      keep_link=opts.keep_link)
         print(decode_it(r))
 
+    @cmdln.option('-a', '--arch', metavar='ARCH',
+                        help='Release only binaries from the specified 
architecture')
     @cmdln.option('-r', '--repo', metavar='REPO',
                         help='Release only binaries from the specified 
repository')
     @cmdln.option('--target-project', metavar='TARGETPROJECT',
@@ -3954,6 +3964,7 @@
             project=project,
             package=package,
             repository=opts.repo,
+            architecture=opts.arch,
             target_project=opts.target_project,
             target_repository=opts.target_repository,
             set_release_to=opts.set_release,
@@ -6046,6 +6057,8 @@
                         help='list packages vertically instead horizontally 
for entire project')
     @cmdln.option('-w', '--watch', action='store_true',
                         help='watch the results until all finished building')
+    @cmdln.option('-F', '--fail-on-error', action='store_true',
+                        help='fail with exit 1 if any build has errored')
     @cmdln.option('-s', '--status-filter',
                         help='only show packages with the given build status')
     @cmdln.option('-f', '--failed', action='store_true',
@@ -6054,8 +6067,11 @@
                   help='generate output in XML (former results_meta)')
     @cmdln.option('', '--csv', action='store_true', default=False,
                   help='generate output in CSV format')
-    @cmdln.option('', '--format', 
default='%(repository)s|%(arch)s|%(state)s|%(dirty)s|%(code)s|%(details)s',
-                  help='format string for csv output')
+    @cmdln.option('', '--format', default=None,
+                  help="Change the format of the text (default) or csv output. 
Not supported for xml output.\n"
+                       "Supported fields: project, package, repository, arch, 
state, dirty, code, details.\n"
+                       "Text output format requires using the field names in 
form of named fields for string interpolation: ``%%(field)s``.\n"
+                       "CSV output format requires field names separated with 
commas.")
     @cmdln.option('--show-excluded', action='store_true',
                   help='show repos that are excluded for this package')
     def do_results(self, subcmd, opts, *args):
@@ -6090,6 +6106,12 @@
         if opts.failed and opts.status_filter:
             raise oscerr.WrongArgs('-s and -f cannot be used together')
 
+        if opts.multibuild_package and opts.no_multibuild:
+            self.argparser.error("-M/--multibuild-package and --no-multibuild 
are mutually exclusive")
+
+        if opts.xml and opts.format:
+            self.argparser.error("--xml and --format are mutually exclusive")
+
         if opts.failed:
             opts.status_filter = 'failed'
             opts.brief = True
@@ -6123,13 +6145,40 @@
                     print(decode_it(xml), end='')
                 else:
                     # csv formatting
-                    results = [r for r, _ in result_xml_to_dicts(xml)]
-                    print('\n'.join(format_results(results, opts.format)))
+                    if opts.format is None:
+                        columns = ["repository", "arch", "package", "state", 
"dirty", "code", "details"]
+                    else:
+                        # split columns by colon, semicolon or pipe
+                        columns = opts.format.split(",")
+
+                    supported_columns = ["project", "package", "repository", 
"arch", "state", "dirty", "code", "details"]
+                    unknown_columns = sorted(set(columns) - 
set(supported_columns))
+
+                    if unknown_columns:
+                        self.argparser.error(f"Unknown format fields: 
{''.join(unknown_columns)}")
+
+                    f = io.StringIO()
+                    writer = csv.writer(f, dialect="unix")
+
+                    rows = [r for r, _ in result_xml_to_dicts(xml)]
+                    for row in rows:
+                        writer.writerow([row[i] for i in columns])
+
+                    f.seek(0)
+                    print(f.read(), end="")
+
         else:
             kwargs['verbose'] = opts.verbose
             kwargs['wait'] = opts.watch
             kwargs['printJoin'] = '\n'
-            get_results(**kwargs)
+            kwargs['format'] = opts.format
+
+            out = {}
+            get_results(out=out, **kwargs)
+
+            if opts.fail_on_error and out['failed']:
+                sys.exit(1)
+
 
     # WARNING: this function is also called by do_results. You need to set a 
default there
     #          as well when adding a new option!
@@ -7868,9 +7917,12 @@
         """
 
         args = parseargs(args)
-        pacs = Package.from_paths(args)
-
-        for p in pacs:
+        for pdir in args:
+            store = osc_store.get_store(pdir)
+            if store.is_package:
+                p = Package(pdir)
+            else:
+                p = Project(pdir, getPackageList=False, wc_check=False)
             print(p.info())
 
     @cmdln.option('-M', '--multibuild-package', metavar='FLAVOR', 
action='append',
@@ -8099,9 +8151,6 @@
         package = None
         binary = None
 
-        if opts.multibuild_package and ((len(args) > 2) or (len(args) <= 2 and 
is_project_dir(Path.cwd()))):
-            self.argparse_error("The -M/--multibuild-package option can be 
only used from a package checkout.")
-
         if len(args) < 1 and is_package_dir('.'):
             self.print_repos()
 
@@ -8141,13 +8190,21 @@
         if package is None:
             package_specified = False
             package = meta_get_packagelist(apiurl, project, deleted=0)
+            if opts.multibuild_package:
+                # remove packages that do not have matching flavor
+                for i in package.copy():
+                    package_flavor = i.rsplit(":", 1)
+                    # package has flavor, check if the flavor is in 
opts.multibuild_packages
+                    flavor_match = len(package_flavor) == 2 and 
package_flavor[1] in opts.multibuild_package
+                    # package nas no flavor, check if "" is in 
opts.multibuild_package
+                    no_flavor_match = len(package_flavor) == 1 and "" in 
opts.multibuild_package
+                    if not flavor_match and not no_flavor_match:
+                        package.remove(i)
         else:
             package_specified = True
             if opts.multibuild_package:
-                packages = []
-                for subpackage in opts.multibuild_package:
-                    packages.append(package + ":" + subpackage)
-                package = packages
+                resolver = MultibuildFlavorResolver(apiurl, project, package, 
use_local=False)
+                package = resolver.resolve_as_packages(opts.multibuild_package)
             else:
                 package = [package]
 
@@ -9887,6 +9944,10 @@
         except oscerr.PackageFileConflict:
             # file is already tracked
             pass
+
+        # instantiate src_pkg *again* to load fresh state from .osc that was 
written on deleting a file in tgt_pkg
+        # it would be way better to use a single Package instance where 
possible
+        src_pkg = Package(source)
         src_pkg.delete_file(os.path.basename(source), force=opts.force)
 
     @cmdln.option('-d', '--delete', action='store_true',
@@ -10170,7 +10231,7 @@
 
     def _load_plugins(self):
         if IN_VENV:
-            output.print_msg("Running in virtual environment, skipping loading 
legacy plugins.", print_to="stderr")
+            output.print_msg("Running in virtual environment, skipping loading 
legacy plugins.", print_to="debug")
             return
 
         plugin_dirs = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/conf.py new/osc-1.8.1/osc/conf.py
--- old/osc-1.7.0/osc/conf.py   2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/osc/conf.py   2024-07-01 10:11:40.000000000 +0200
@@ -1875,7 +1875,7 @@
                 os.chmod(conffile, 0o600)
             except OSError as e:
                 if e.errno in (errno.EROFS, errno.EPERM):
-                    print(f"Warning: Configuration file '{conffile}' may have 
insecure file permissions.")
+                    print(f"Warning: Configuration file '{conffile}' may have 
insecure file permissions.", file=sys.stderr)
                 else:
                     raise e
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/core.py new/osc-1.8.1/osc/core.py
--- old/osc-1.7.0/osc/core.py   2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/osc/core.py   2024-07-01 10:11:40.000000000 +0200
@@ -235,6 +235,13 @@
 Link info: %s
 """
 
+project_info_templ = """\
+Project name: %s
+Path: %s
+API URL: %s
+Source URL: %s
+"""
+
 new_pattern_template = """\
 <!-- See 
https://github.com/openSUSE/libzypp/tree/master/zypp/parser/yum/schema/patterns.rng
 -->
 
@@ -3287,7 +3294,7 @@
 
     if disable_build or disable_publish:
         meta_change = True
-        root = ET.fromstring(b"".join(dst_meta))
+        root = ET.fromstring("".join(dst_meta))
 
         if disable_build:
             elm = root.find('build')
@@ -4089,15 +4096,26 @@
     return [format % r for r in results]
 
 
-def get_results(apiurl: str, project: str, package: str, verbose=False, 
printJoin="", *args, **kwargs):
+def get_results(
+    apiurl: str,
+    project: str,
+    package: str,
+    verbose=False,
+    printJoin="",
+    out: Optional[dict] = None,
+    *args,
+    **kwargs
+):
     """returns list of/or prints a human readable status for the specified 
package"""
     # hmm the function name is a bit too generic - something like
     # get_package_results_human would be better, but this would break the 
existing
     # api (unless we keep get_results around as well)...
-    result_line_templ = '%(rep)-20s %(arch)-10s %(status)s'
-    result_line_mb_templ = '%(rep)-20s %(arch)-10s %(pkg)-30s %(status)s'
+    format = kwargs.pop('format')
+    if format is None:
+        format = '%(rep)-20s %(arch)-10s %(pkg)-30s %(status)s'
     r = []
     printed = False
+    failed = False
     multibuild_packages = kwargs.pop('multibuild_packages', [])
     show_excluded = kwargs.pop('showexcl', False)
     code_filter = kwargs.get('code')
@@ -4138,10 +4156,10 @@
             # of the repository if the result is already prefiltered by the 
backend. So we need
             # to filter out the repository states.
             if code_filter is None or code_filter == res['code']:
-                if is_multi:
-                    r.append(result_line_mb_templ % res)
-                else:
-                    r.append(result_line_templ % res)
+                r.append(format % res)
+
+            if res['code'] in ('failed', 'broken', 'unresolvable'):
+                failed = True
 
         if printJoin:
             if printed:
@@ -4149,12 +4167,18 @@
                 print()
             print(printJoin.join(r))
             printed = True
+
+    if out is None:
+        out = {}
+
+    out["failed"] = failed
+
     return r
 
 
-def get_package_results(apiurl: str, project: str, package: Optional[str] = 
None, wait=False, *args, **kwargs):
+def get_package_results(apiurl: str, project: str, package: Optional[str] = 
None, wait=False, multibuild_packages: Optional[List[str]] = None, *args, 
**kwargs):
     """generator that returns a the package results as an xml structure"""
-    xml = ''
+    xml = b''
     waiting_states = ('blocked', 'scheduled', 'dispatching', 'building',
                       'signing', 'finished')
     while True:
@@ -4187,6 +4211,33 @@
                     waiting = True
                     break
 
+        # filter the result according to the specified multibuild_packages 
(flavors)
+        if multibuild_packages:
+            for result in list(root):
+                for status in list(result):
+                    package = status.attrib["package"]
+                    package_flavor = package.rsplit(":", 1)
+
+                    # package has flavor, check if the flavor is in 
multibuild_packages
+                    flavor_match = len(package_flavor) == 2 and 
package_flavor[1] in multibuild_packages
+
+                    # package nas no flavor, check if "" is in 
multibuild_packages
+                    no_flavor_match = len(package_flavor) == 1 and "" in 
multibuild_packages
+
+                    if not flavor_match and not no_flavor_match:
+                        # package doesn't match multibuild_packages, remove 
the corresponding <status> from <result>
+                        result.remove(status)
+
+                # remove empty <result> from <resultlist>
+                if len(result) == 0:
+                    root.remove(result)
+
+            if len(root) == 0:
+                break
+
+            xmlindent(root)
+            xml = ET.tostring(root)
+
         if not wait or not waiting:
             break
         else:
@@ -5181,8 +5232,6 @@
         with open(os.devnull, 'w') as devnull:
             rpm2cpio_proc = subprocess.Popen(['rpm2cpio'], stdin=fsrpm,
                                              stdout=subprocess.PIPE)
-            # XXX: shell injection is possible via the files parameter, but the
-            #      current osc code does not use the files parameter.
             cpio_proc = subprocess.Popen(['cpio', '-i'] + list(files),
                                          stdin=rpm2cpio_proc.stdout,
                                          stderr=devnull)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/obs_api/keyinfo_sslcert.py 
new/osc-1.8.1/osc/obs_api/keyinfo_sslcert.py
--- old/osc-1.7.0/osc/obs_api/keyinfo_sslcert.py        2024-05-22 
14:13:35.000000000 +0200
+++ new/osc-1.8.1/osc/obs_api/keyinfo_sslcert.py        2024-07-01 
10:11:40.000000000 +0200
@@ -4,7 +4,7 @@
 class KeyinfoSslcert(XmlModel):
     XML_TAG = "sslcert"
 
-    keyid: str = Field(
+    keyid: Optional[str] = Field(
         xml_attribute=True,
     )
 
@@ -36,7 +36,7 @@
         xml_attribute=True,
     )
 
-    fingerprint: str = Field(
+    fingerprint: Optional[str] = Field(
         xml_attribute=True,
     )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/obs_scm/package.py 
new/osc-1.8.1/osc/obs_scm/package.py
--- old/osc-1.7.0/osc/obs_scm/package.py        2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/osc/obs_scm/package.py        2024-07-01 10:11:40.000000000 
+0200
@@ -1573,16 +1573,18 @@
             raise oscerr.OscIOError(None, f'error: \'{dir}\' is already an 
initialized osc working copy')
         else:
             os.mkdir(os.path.join(dir, store))
-        store_write_project(dir, project)
-        store_write_string(dir, '_package', package + '\n')
-        Store(dir).apiurl = apiurl
+
+        s = Store(dir, check=False)
+        s.write_string("_osclib_version", Store.STORE_VERSION)
+        s.apiurl = apiurl
+        s.project = project
+        s.package = package
         if meta:
-            store_write_string(dir, '_meta_mode', '')
+            s.write_string("_meta_mode", "")
         if size_limit:
-            store_write_string(dir, '_size_limit', str(size_limit) + '\n')
+            s.size_limit = int(size_limit)
         if scm_url:
-            Store(dir).scmurl = scm_url
+            s.scmurl = scm_url
         else:
-            store_write_string(dir, '_files', '<directory />' + '\n')
-        store_write_string(dir, '_osclib_version', __store_version__ + '\n')
+            s.write_string("_files", "<directory />")
         return Package(dir, progress_obj=progress_obj, size_limit=size_limit)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/obs_scm/project.py 
new/osc-1.8.1/osc/obs_scm/project.py
--- old/osc-1.7.0/osc/obs_scm/project.py        2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/osc/obs_scm/project.py        2024-07-01 10:11:40.000000000 
+0200
@@ -242,6 +242,14 @@
         else:
             return None
 
+    def info(self):
+        from ..core import project_info_templ
+        from ..core import makeurl
+
+        source_url = makeurl(self.apiurl, ['source', self.name])
+        r = project_info_templ % (self.name, self.absdir, self.apiurl, 
source_url)
+        return r
+
     def new_package_entry(self, name, state):
         ET.SubElement(self.pac_root, 'package', name=name, state=state)
 
@@ -616,10 +624,12 @@
         else:
             os.mkdir(os.path.join(dir, store))
 
-        store_write_project(dir, project)
-        Store(dir).apiurl = apiurl
+        s = Store(dir, check=False)
+        s.write_string("_osclib_version", Store.STORE_VERSION)
+        s.apiurl = apiurl
+        s.project = project
         if scm_url:
-            Store(dir).scmurl = scm_url
+            s.scmurl = scm_url
             package_tracking = None
         if package_tracking:
             store_write_initial_packages(dir, project, [])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/obs_scm/store.py 
new/osc-1.8.1/osc/obs_scm/store.py
--- old/osc-1.7.0/osc/obs_scm/store.py  2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/osc/obs_scm/store.py  2024-07-01 10:11:40.000000000 +0200
@@ -42,6 +42,9 @@
         self.path = path
         self.abspath = os.path.abspath(self.path)
 
+        if check:
+            check_store_version(self.abspath)
+
         self.is_project = self.exists("_project") and not 
self.exists("_package")
         self.is_package = self.exists("_project") and self.exists("_package")
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/util/ar.py new/osc-1.8.1/osc/util/ar.py
--- old/osc-1.7.0/osc/util/ar.py        2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/osc/util/ar.py        2024-07-01 10:11:40.000000000 +0200
@@ -19,36 +19,32 @@
 import stat
 import sys
 from io import BytesIO
-
-
-# workaround for python24
-if not hasattr(os, 'SEEK_SET'):
-    os.SEEK_SET = 0
+from typing import Union
 
 
 class ArError(Exception):
     """Base class for all ar related errors"""
 
-    def __init__(self, fn, msg):
+    def __init__(self, fn: bytes, msg: str):
         super().__init__()
         self.file = fn
         self.msg = msg
 
     def __str__(self):
-        return 'ar error: %s' % self.msg
+        return f"{self.msg}: {self.file.decode('utf-8')}"
 
 
 class ArHdr:
     """Represents an ar header entry"""
 
-    def __init__(self, fn, date, uid, gid, mode, size, fmag, off):
+    def __init__(self, fn: bytes, date: bytes, uid: bytes, gid: bytes, mode: 
bytes, size: bytes, fmag: bytes, off: bytes):
         self.file = fn.strip()
         self.date = date.strip()
         self.uid = uid.strip()
         self.gid = gid.strip()
         if not mode.strip():
             # provide a dummy mode for the ext_fn hdr
-            mode = '0'
+            mode = b"0"
         self.mode = stat.S_IMODE(int(mode, 8))
         self.size = int(size)
         self.fmag = fmag
@@ -75,9 +71,17 @@
         working dir is used. Additionally it tries to set the owner/group
         and permissions.
         """
+        if self.name.startswith(b"/"):
+            raise ArError(self.name, "Extracting files with absolute paths is 
not supported for security reasons")
+
         if not dir:
             dir = os.getcwdb()
-        fn = os.path.join(dir, self.name)
+        fn = os.path.join(dir, self.name.decode("utf-8"))
+
+        dir_path, _ = os.path.split(fn)
+        if dir_path:
+            os.makedirs(dir_path, exist_ok=True)
+
         with open(fn, 'wb') as f:
             f.write(self.getvalue())
         os.chmod(fn, self.mode)
@@ -88,6 +92,7 @@
         if gid not in os.getgroups() or os.getegid() != 0:
             gid = -1
         os.chown(fn, uid, gid)
+        return fn
 
     def __str__(self):
         return '%s %s %s %s' % (self.name, self.uid,
@@ -140,26 +145,30 @@
         data section. The end of such a filename is indicated with a trailing 
'/'.
         Another special file is the '/' which contains the symbol lookup table.
         """
+
+        # read extended header with long file names and then only seek into 
the right offsets
+        self.__file.seek(self.ext_fnhdr.dataoff, os.SEEK_SET)
+        ext_fnhdr_data = self.__file.read(self.ext_fnhdr.size)
+
         for h in self.hdrs:
             if h.file == b'/':
                 continue
-            # remove slashes which are appended by ar
-            h.file = h.file.rstrip(b'/')
-            if not h.file.startswith(b'/'):
+
+            if h.file.endswith(b"/"):
+                # regular file name
+                h.file = h.file[:-1]
                 continue
-            # handle long filename
-            off = int(h.file[1:len(h.file)])
-            start = self.ext_fnhdr.dataoff + off
-            self.__file.seek(start, os.SEEK_SET)
-            # XXX: is it safe to read all the data in one chunk? I assume the 
'//' data section
-            #      won't be too large
-            data = self.__file.read(self.ext_fnhdr.size)
-            end = data.find(b'/')
-            if end != -1:
-                h.file = data[0:end]
-            else:
+
+            # long file name
+            assert h.file[0:1] == b"/"
+            start = int(h.file[1:])
+            end = ext_fnhdr_data.find(b'/', start)
+
+            if end == -1:
                 raise ArError(b'//', 'invalid data section - trailing slash 
(off: %d)' % start)
 
+            h.file = ext_fnhdr_data[start:end]
+
     def _get_file(self, hdr):
         self.__file.seek(hdr.dataoff, os.SEEK_SET)
         return ArFile(hdr.file, hdr.uid, hdr.gid, hdr.mode,
@@ -193,7 +202,10 @@
             pos += hdr.size + (hdr.size & 1)
         self._fixupFilenames()
 
-    def get_file(self, fn):
+    def get_file(self, fn: Union[str, bytes]):
+        # accept str for better user experience
+        if isinstance(fn, str):
+            fn = fn.encode("utf-8")
         for h in self.hdrs:
             if h.file == fn:
                 return self._get_file(h)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/util/cpio.py 
new/osc-1.8.1/osc/util/cpio.py
--- old/osc-1.7.0/osc/util/cpio.py      2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/osc/util/cpio.py      2024-07-01 10:11:40.000000000 +0200
@@ -20,10 +20,6 @@
 import sys
 
 
-# workaround for python24
-if not hasattr(os, 'SEEK_SET'):
-    os.SEEK_SET = 0
-
 # format implementation is based on src/copyin.c and src/util.c (see cpio 
sources)
 
 
@@ -129,7 +125,16 @@
             msg = '\'%s\' is no regular file - only regular files are 
supported atm' % hdr.filename
             raise NotImplementedError(msg)
         self.__file.seek(hdr.dataoff, os.SEEK_SET)
+
+        if fn.startswith(b"/"):
+            raise CpioError(fn, "Extracting files with absolute paths is not 
supported for security reasons")
+
         fn = os.path.join(dest, fn)
+
+        dir_path, _ = os.path.split(fn)
+        if dir_path:
+            os.makedirs(dir_path, exist_ok=True)
+
         with open(fn, 'wb') as f:
             f.write(self.__file.read(hdr.filesize))
         os.chmod(fn, hdr.mode)
@@ -183,12 +188,22 @@
         to a dir the file will be stored in dest/filename. In case new_fn is 
specified
         the file will be stored as new_fn.
         """
+
+        # accept str for better user experience
+        if isinstance(filename, str):
+            filename = filename.encode("utf-8")
+        if isinstance(dest, str):
+            dest = dest.encode("utf-8")
+        if isinstance(new_fn, str):
+            new_fn = new_fn.encode("utf-8")
+
         hdr = self._get_hdr(filename)
         if not hdr:
             raise CpioError(filename, '\'%s\' does not exist in archive' % 
filename)
         dest = dest or os.getcwdb()
         fn = new_fn or filename
         self._copyin_file(hdr, dest, fn)
+        return os.path.join(dest, fn).decode("utf-8")
 
     def copyin(self, dest=None):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/osc/util/git_version.py 
new/osc-1.8.1/osc/util/git_version.py
--- old/osc-1.7.0/osc/util/git_version.py       2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/osc/util/git_version.py       2024-07-01 10:11:40.000000000 
+0200
@@ -9,7 +9,7 @@
     """
     # the `version` variable contents get substituted during `git archive`
     # it requires adding this to .gitattributes: <path to this file> 
export-subst
-    version = "1.7.0"
+    version = "1.8.1"
     if version.startswith(("$", "%")):
         # "$": version hasn't been substituted during `git archive`
         # "%": "Format:" and "$" characters get removed from the version 
string (a GitHub bug?)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/addfile_fixtures/osctest/.osc/_osclib_version 
new/osc-1.8.1/tests/addfile_fixtures/osctest/.osc/_osclib_version
--- old/osc-1.7.0/tests/addfile_fixtures/osctest/.osc/_osclib_version   
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/addfile_fixtures/osctest/.osc/_osclib_version   
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/commit_fixtures/osctest/.osc/_osclib_version 
new/osc-1.8.1/tests/commit_fixtures/osctest/.osc/_osclib_version
--- old/osc-1.7.0/tests/commit_fixtures/osctest/.osc/_osclib_version    
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/commit_fixtures/osctest/.osc/_osclib_version    
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/common.py 
new/osc-1.8.1/tests/common.py
--- old/osc-1.7.0/tests/common.py       2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/tests/common.py       2024-07-01 10:11:40.000000000 +0200
@@ -73,7 +73,7 @@
         self.exp_method = exp_method
 
     def __str__(self):
-        return '%s, %s, %s, %s' % (self.url, self.exp_url, self.method, 
self.exp_method)
+        return f'{self.url}, {self.exp_url}, {self.method}, {self.exp_method}'
 
 
 class RequestDataMismatch(Exception):
@@ -85,7 +85,7 @@
         self.exp = exp
 
     def __str__(self):
-        return '%s, %s, %s' % (self.url, self.got, self.exp)
+        return f'{self.url}, {self.got}, {self.exp}'
 
 
 EXPECTED_REQUESTS = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/deletefile_fixtures/osctest/.osc/_osclib_version 
new/osc-1.8.1/tests/deletefile_fixtures/osctest/.osc/_osclib_version
--- old/osc-1.7.0/tests/deletefile_fixtures/osctest/.osc/_osclib_version        
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/deletefile_fixtures/osctest/.osc/_osclib_version        
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/difffile_fixtures/osctest/.osc/_osclib_version 
new/osc-1.8.1/tests/difffile_fixtures/osctest/.osc/_osclib_version
--- old/osc-1.7.0/tests/difffile_fixtures/osctest/.osc/_osclib_version  
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/difffile_fixtures/osctest/.osc/_osclib_version  
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/fixtures/README 
new/osc-1.8.1/tests/fixtures/README
--- old/osc-1.7.0/tests/fixtures/README 1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/fixtures/README 2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1,35 @@
+Generate data for creating test archives
+----------------------------------------
+
+echo 'foobar' > /tmp/foo
+
+# root perms required for the next command
+echo 'numbers' > /123
+
+echo 'qwerty' > very-long-long-long-long-name
+
+echo 'asdfgh' > very-long-long-long-long-name2
+
+echo 'newline' > 'very-long-name
+-with-newline'
+
+echo 'newline' > 'a
+b'
+
+mkdir 'dir'
+echo 'file-in-a-dir' > dir/file
+
+
+Create archive.ar
+-----------------
+
+ar qP archive.ar /tmp/foo /123 very-long-long-long-long-name 
very-long-long-long-long-name2 'very-long-name
+-with-newline' 'a
+b' dir/file
+
+
+Create archive.cpio
+-------------------
+
+printf 
"/tmp/foo\0/123\0very-long-long-long-long-name\0very-long-long-long-long-name2\0very-long-name
+-with-newline\0a\nb\0dir/file\0" | cpio -ocv0 --owner=root:root > archive.cpio
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/fixtures/archive.ar 
new/osc-1.8.1/tests/fixtures/archive.ar
--- old/osc-1.7.0/tests/fixtures/archive.ar     1970-01-01 01:00:00.000000000 
+0100
+++ new/osc-1.8.1/tests/fixtures/archive.ar     2024-07-01 10:11:40.000000000 
+0200
@@ -0,0 +1,25 @@
+!<arch>
+//                                              94        `
+very-long-long-long-long-name/
+very-long-long-long-long-name2/
+very-long-name
+-with-newline/
+
+/tmp/foo/       1716888536  1000  1000  100644  7         `
+foobar
+
+/123/           1716883776  0     0     100644  8         `
+numbers
+/0              1716882802  1000  1000  100644  7         `
+qwerty
+
+/31             1716882988  1000  1000  100644  7         `
+asdfgh
+
+/63             1716884767  1000  1000  100644  8         `
+newline
+a
+b/            1716884876  1000  1000  100644  8         `
+newline
+dir/file/       1716992150  1000  1000  100644  14        `
+file-in-a-dir
Binary files old/osc-1.7.0/tests/fixtures/archive.cpio and 
new/osc-1.8.1/tests/fixtures/archive.cpio differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/prdiff_fixtures/home:user:branches:some:project/.osc/_osclib_version
 
new/osc-1.8.1/tests/prdiff_fixtures/home:user:branches:some:project/.osc/_osclib_version
--- 
old/osc-1.7.0/tests/prdiff_fixtures/home:user:branches:some:project/.osc/_osclib_version
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/osc-1.8.1/tests/prdiff_fixtures/home:user:branches:some:project/.osc/_osclib_version
    2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/prdiff_fixtures/osctest/.osc/_osclib_version 
new/osc-1.8.1/tests/prdiff_fixtures/osctest/.osc/_osclib_version
--- old/osc-1.7.0/tests/prdiff_fixtures/osctest/.osc/_osclib_version    
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/prdiff_fixtures/osctest/.osc/_osclib_version    
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/prdiff_fixtures/some:project/.osc/_osclib_version 
new/osc-1.8.1/tests/prdiff_fixtures/some:project/.osc/_osclib_version
--- old/osc-1.7.0/tests/prdiff_fixtures/some:project/.osc/_osclib_version       
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/prdiff_fixtures/some:project/.osc/_osclib_version       
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/project_package_status_fixtures/osctest/.osc/_osclib_version
 
new/osc-1.8.1/tests/project_package_status_fixtures/osctest/.osc/_osclib_version
--- 
old/osc-1.7.0/tests/project_package_status_fixtures/osctest/.osc/_osclib_version
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/osc-1.8.1/tests/project_package_status_fixtures/osctest/.osc/_osclib_version
    2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/repairwc_fixtures/osctest/.osc/_osclib_version 
new/osc-1.8.1/tests/repairwc_fixtures/osctest/.osc/_osclib_version
--- old/osc-1.7.0/tests/repairwc_fixtures/osctest/.osc/_osclib_version  
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/repairwc_fixtures/osctest/.osc/_osclib_version  
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/repairwc_fixtures/prj_invalidapiurl/.osc/_osclib_version 
new/osc-1.8.1/tests/repairwc_fixtures/prj_invalidapiurl/.osc/_osclib_version
--- 
old/osc-1.7.0/tests/repairwc_fixtures/prj_invalidapiurl/.osc/_osclib_version    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/osc-1.8.1/tests/repairwc_fixtures/prj_invalidapiurl/.osc/_osclib_version    
    2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/repairwc_fixtures/prj_noapiurl/.osc/_osclib_version 
new/osc-1.8.1/tests/repairwc_fixtures/prj_noapiurl/.osc/_osclib_version
--- old/osc-1.7.0/tests/repairwc_fixtures/prj_noapiurl/.osc/_osclib_version     
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/repairwc_fixtures/prj_noapiurl/.osc/_osclib_version     
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/revertfile_fixtures/osctest/.osc/_osclib_version 
new/osc-1.8.1/tests/revertfile_fixtures/osctest/.osc/_osclib_version
--- old/osc-1.7.0/tests/revertfile_fixtures/osctest/.osc/_osclib_version        
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/revertfile_fixtures/osctest/.osc/_osclib_version        
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/test_commandline.py 
new/osc-1.8.1/tests/test_commandline.py
--- old/osc-1.7.0/tests/test_commandline.py     2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/tests/test_commandline.py     2024-07-01 10:11:40.000000000 
+0200
@@ -143,6 +143,8 @@
 class TestPopProjectPackageFromArgs(unittest.TestCase):
     def _write_store(self, project=None, package=None):
         store = Store(self.tmpdir, check=False)
+        store.write_string("_osclib_version", Store.STORE_VERSION)
+        store.apiurl = "http://localhost";
         if project:
             store.project = project
             store.is_project = True
@@ -408,6 +410,8 @@
 class TestPopProjectPackageRepositoryArchFromArgs(unittest.TestCase):
     def _write_store(self, project=None, package=None):
         store = Store(self.tmpdir, check=False)
+        store.write_string("_osclib_version", Store.STORE_VERSION)
+        store.apiurl = "http://localhost";
         if project:
             store.project = project
             store.is_project = True
@@ -609,6 +613,8 @@
 class 
TestPopProjectPackageTargetProjectTargetPackageFromArgs(unittest.TestCase):
     def _write_store(self, project=None, package=None):
         store = Store(self.tmpdir, check=False)
+        store.write_string("_osclib_version", Store.STORE_VERSION)
+        store.apiurl = "http://localhost";
         if project:
             store.project = project
             store.is_project = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/test_config_parser.py 
new/osc-1.8.1/tests/test_config_parser.py
--- old/osc-1.7.0/tests/test_config_parser.py   2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/tests/test_config_parser.py   2024-07-01 10:11:40.000000000 
+0200
@@ -1,3 +1,4 @@
+import configparser
 import unittest
 
 from osc.OscConfigParser import OscConfigParser
@@ -21,6 +22,28 @@
         # ValueError: invalid interpolation syntax in '%' at position 0
         self.parser.set("http://localhost";, "pass", "%")
 
+    def test_duplicate_section(self):
+        conf = """
+[general]
+
+[http://localhost]
+
+[http://localhost]
+"""
+        parser = OscConfigParser()
+        self.assertRaises(configparser.DuplicateSectionError, 
parser.read_string, conf)
+
+    def test_duplicate_option(self):
+        conf = """
+[general]
+
+[http://localhost]
+user=
+user=
+"""
+        parser = OscConfigParser()
+        self.assertRaises(configparser.DuplicateOptionError, 
parser.read_string, conf)
+
 
 if __name__ == "__main__":
     unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/test_init_package.py 
new/osc-1.8.1/tests/test_init_package.py
--- old/osc-1.7.0/tests/test_init_package.py    2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/tests/test_init_package.py    2024-07-01 10:11:40.000000000 
+0200
@@ -16,15 +16,10 @@
 
 class TestInitPackage(OscTestCase):
     def _get_fixtures_dir(self):
-        # workaround for git because it doesn't allow empty dirs
-        if not os.path.exists(os.path.join(FIXTURES_DIR, 'osctest')):
-            os.mkdir(os.path.join(FIXTURES_DIR, 'osctest'))
         return FIXTURES_DIR
 
-    def tearDown(self):
-        if os.path.exists(os.path.join(FIXTURES_DIR, 'osctest')):
-            os.rmdir(os.path.join(FIXTURES_DIR, 'osctest'))
-        super().tearDown()
+    def setUp(self):
+        super().setUp(copytree=False)
 
     def test_simple(self):
         """initialize a package dir"""
@@ -56,7 +51,7 @@
         osc.core.Package.init_package('http://localhost', 'osctest', 
'testpkg', pac_dir, meta=True)
         storedir = os.path.join(pac_dir, osc.core.store)
         self.assertFalse(os.path.exists(os.path.join(storedir, '_size_limit')))
-        self._check_list(os.path.join(storedir, '_meta_mode'), '')
+        self._check_list(os.path.join(storedir, '_meta_mode'), '\n')
         self._check_list(os.path.join(storedir, '_project'), 'osctest\n')
         self._check_list(os.path.join(storedir, '_package'), 'testpkg\n')
         self._check_list(os.path.join(storedir, '_files'), '<directory />\n')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/test_init_project.py 
new/osc-1.8.1/tests/test_init_project.py
--- old/osc-1.7.0/tests/test_init_project.py    2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/tests/test_init_project.py    2024-07-01 10:11:40.000000000 
+0200
@@ -17,15 +17,10 @@
 
 class TestInitProject(OscTestCase):
     def _get_fixtures_dir(self):
-        # workaround for git because it doesn't allow empty dirs
-        if not os.path.exists(os.path.join(FIXTURES_DIR, 'osctest')):
-            os.mkdir(os.path.join(FIXTURES_DIR, 'osctest'))
         return FIXTURES_DIR
 
-    def tearDown(self):
-        if os.path.exists(os.path.join(FIXTURES_DIR, 'osctest')):
-            os.rmdir(os.path.join(FIXTURES_DIR, 'osctest'))
-        super().tearDown()
+    def setUp(self):
+        super().setUp(copytree=False)
 
     def test_simple(self):
         """initialize a project dir"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/test_prdiff.py 
new/osc-1.8.1/tests/test_prdiff.py
--- old/osc-1.7.0/tests/test_prdiff.py  2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/tests/test_prdiff.py  2024-07-01 10:11:40.000000000 +0200
@@ -1,6 +1,8 @@
 import os
 import re
+import shutil
 import sys
+import tempfile
 import unittest
 
 import osc.commandline
@@ -50,6 +52,18 @@
 class TestProjectDiff(OscTestCase):
     diff_hdr = 'Index: 
%s\n==================================================================='
 
+    def setUp(self, copytree=True):
+        super().setUp(copytree=copytree)
+        self.tmpdir_fixtures = tempfile.mkdtemp(prefix='osc_test')
+        shutil.copytree(self._get_fixtures_dir(), 
os.path.join(self.tmpdir_fixtures, "fixtures"))
+
+    def tearDown(self):
+        try:
+            shutil.rmtree(self.tmpdir_fixtures)
+        except:
+            pass
+        super().tearDown()
+
     def _get_fixtures_dir(self):
         return FIXTURES_DIR
 
@@ -85,10 +99,10 @@
         os.chdir('/tmp')
         self.assertRaises(osc.oscerr.WrongArgs, runner)
 
-        self._change_to_tmpdir(FIXTURES_DIR, UPSTREAM)
+        self._change_to_tmpdir(self.tmpdir_fixtures, "fixtures", UPSTREAM)
         self.assertRaises(osc.oscerr.WrongArgs, runner)
 
-        self._change_to_tmpdir(FIXTURES_DIR, BRANCH)
+        self._change_to_tmpdir(self.tmpdir_fixtures, "fixtures", BRANCH)
         out = self._run_prdiff()
         self.assertEqualMultiline(out, exp)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/test_repairwc.py 
new/osc-1.8.1/tests/test_repairwc.py
--- old/osc-1.7.0/tests/test_repairwc.py        2024-05-22 14:13:35.000000000 
+0200
+++ new/osc-1.8.1/tests/test_repairwc.py        2024-07-01 10:11:40.000000000 
+0200
@@ -25,7 +25,7 @@
         try:
             meth(*args, **kwargs)
         except exception:
-            self.fail('%s raised' % exception.__name__)
+            self.fail(f'{exception.__name__} raised')
 
     def test_working_empty(self):
         """consistent, empty working copy"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/test_store.py 
new/osc-1.8.1/tests/test_store.py
--- old/osc-1.7.0/tests/test_store.py   2024-05-22 14:13:35.000000000 +0200
+++ new/osc-1.8.1/tests/test_store.py   2024-07-01 10:11:40.000000000 +0200
@@ -11,6 +11,8 @@
     def setUp(self):
         self.tmpdir = tempfile.mkdtemp(prefix='osc_test')
         self.store = Store(self.tmpdir, check=False)
+        self.store.write_string("_osclib_version", Store.STORE_VERSION)
+        self.store.apiurl = "http://localhost";
         self.store.is_package = True
         self.store.project = "project name"
         self.store.package = "package name"
@@ -87,9 +89,9 @@
         self.assertFalse("_foo" in self.store)
 
     def test_iter(self):
-        self.assertEqual(len(list(self.store)), 2)
+        self.assertEqual(len(list(self.store)), 4)
         for fn in self.store:
-            self.assertIn(fn, ["_project", "_package"])
+            self.assertIn(fn, ["_osclib_version", "_apiurl", "_project", 
"_package"])
 
     def test_apiurl(self):
         self.store.apiurl = "https://example.com";
@@ -159,7 +161,7 @@
         self.store.write_string("_osclib_version", "123")
         self.fileEquals("_osclib_version", "123\n")
 
-        store2 = Store(self.tmpdir)
+        store2 = Store(self.tmpdir, check=False)
         self.assertEqual(store2.osclib_version, "123")
 
     def test_files(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/test_util_ar.py 
new/osc-1.8.1/tests/test_util_ar.py
--- old/osc-1.7.0/tests/test_util_ar.py 1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/test_util_ar.py 2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1,86 @@
+import os
+import shutil
+import tempfile
+import unittest
+
+from osc.util.ar import Ar
+from osc.util.ar import ArError
+
+
+FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixtures")
+
+
+class TestAr(unittest.TestCase):
+    def setUp(self):
+        self.tmpdir = tempfile.mkdtemp(prefix="osc_test_")
+        try:
+            self.old_cwd = os.getcwd()
+        except FileNotFoundError:
+            self.old_cwd = os.path.expanduser("~")
+        os.chdir(self.tmpdir)
+        self.archive = os.path.join(FIXTURES_DIR, "archive.ar")
+        self.ar = Ar(self.archive)
+        self.ar.read()
+
+    def tearDown(self):
+        os.chdir(self.old_cwd)
+        shutil.rmtree(self.tmpdir)
+
+    def test_file_list(self):
+        actual = [i.name for i in self.ar]
+        expected = [
+            # absolute path
+            b"/tmp/foo",
+            # this is a filename, not a long filename reference
+            b"/123",
+            b"very-long-long-long-long-name",
+            b"very-long-long-long-long-name2",
+            # long file name with a newline
+            b"very-long-name\n-with-newline",
+            # short file name with a newline
+            b"a\nb",
+            b"dir/file",
+        ]
+        self.assertEqual(actual, expected)
+
+    def test_get_file(self):
+        f = self.ar.get_file(b"/tmp/foo")
+        self.assertIsNotNone(f)
+
+        f = self.ar.get_file("/tmp/foo")
+        self.assertIsNotNone(f)
+
+        f = self.ar.get_file("does-not-exist")
+        self.assertIsNone(f)
+
+    def test_saveTo(self):
+        f = self.ar.get_file("a\nb")
+        path = f.saveTo(self.tmpdir)
+
+        # check that we've got the expected path
+        self.assertEqual(path, os.path.join(self.tmpdir, "a\nb"))
+
+        # ... and that the contents also match
+        with open(path, "r", encoding="utf-8") as f:
+            self.assertEqual(f.read(), "newline\n")
+
+    def test_saveTo_subdir(self):
+        f = self.ar.get_file("dir/file")
+        path = f.saveTo(self.tmpdir)
+
+        # check that we've got the expected path
+        self.assertEqual(path, os.path.join(self.tmpdir, "dir/file"))
+
+        # ... and that the contents also match
+        with open(path, "r", encoding="utf-8") as f:
+            self.assertEqual(f.read(), "file-in-a-dir\n")
+
+    def test_saveTo_abspath(self):
+        f = self.ar.get_file("/tmp/foo")
+        assert f is not None
+        # this is supposed to throw an error, extracting files with absolute 
paths might overwrite system files
+        self.assertRaises(ArError, f.saveTo, self.tmpdir)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-1.7.0/tests/test_util_cpio.py 
new/osc-1.8.1/tests/test_util_cpio.py
--- old/osc-1.7.0/tests/test_util_cpio.py       1970-01-01 01:00:00.000000000 
+0100
+++ new/osc-1.8.1/tests/test_util_cpio.py       2024-07-01 10:11:40.000000000 
+0200
@@ -0,0 +1,71 @@
+import os
+import shutil
+import tempfile
+import unittest
+
+from osc.util.cpio import CpioRead
+from osc.util.cpio import CpioError
+
+
+FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixtures")
+
+
+class TestCpio(unittest.TestCase):
+    def setUp(self):
+        self.tmpdir = tempfile.mkdtemp(prefix="osc_test_")
+        try:
+            self.old_cwd = os.getcwd()
+        except FileNotFoundError:
+            self.old_cwd = os.path.expanduser("~")
+        os.chdir(self.tmpdir)
+        self.archive = os.path.join(FIXTURES_DIR, "archive.cpio")
+        self.cpio = CpioRead(self.archive)
+        self.cpio.read()
+
+    def tearDown(self):
+        os.chdir(self.old_cwd)
+        shutil.rmtree(self.tmpdir)
+
+    def test_file_list(self):
+        actual = [i.filename for i in self.cpio]
+        expected = [
+            # absolute path
+            b"/tmp/foo",
+            # this is a filename, not a long filename reference
+            b"/123",
+            b"very-long-long-long-long-name",
+            b"very-long-long-long-long-name2",
+            # long file name with a newline
+            b"very-long-name\n-with-newline",
+            # short file name with a newline
+            b"a\nb",
+            b"dir/file",
+        ]
+        self.assertEqual(actual, expected)
+
+    def test_copyin_file(self):
+        path = self.cpio.copyin_file("a\nb", dest=self.tmpdir)
+
+        # check that we've got the expected path
+        self.assertEqual(path, os.path.join(self.tmpdir, "a\nb"))
+
+        # ... and that the contents also match
+        with open(path, "r", encoding="utf-8") as f:
+            self.assertEqual(f.read(), "newline\n")
+
+    def test_copyin_file_abspath(self):
+        self.assertRaises(CpioError, self.cpio.copyin_file, "/tmp/foo")
+
+    def test_copyin_file_subdir(self):
+        path = self.cpio.copyin_file("dir/file", dest=self.tmpdir)
+
+        # check that we've got the expected path
+        self.assertEqual(path, os.path.join(self.tmpdir, "dir/file"))
+
+        # ... and that the contents also match
+        with open(path, "r", encoding="utf-8") as f:
+            self.assertEqual(f.read(), "file-in-a-dir\n")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osc-1.7.0/tests/update_fixtures/osctest/.osc/_osclib_version 
new/osc-1.8.1/tests/update_fixtures/osctest/.osc/_osclib_version
--- old/osc-1.7.0/tests/update_fixtures/osctest/.osc/_osclib_version    
1970-01-01 01:00:00.000000000 +0100
+++ new/osc-1.8.1/tests/update_fixtures/osctest/.osc/_osclib_version    
2024-07-01 10:11:40.000000000 +0200
@@ -0,0 +1 @@
+1.0

++++++ osc.dsc ++++++
--- /var/tmp/diff_new_pack.ChhHu5/_old  2024-07-01 11:23:11.633218187 +0200
+++ /var/tmp/diff_new_pack.ChhHu5/_new  2024-07-01 11:23:11.633218187 +0200
@@ -1,6 +1,6 @@
 Format: 1.0
 Source: osc
-Version: 1.7.0-0
+Version: 1.8.1-0
 Binary: osc
 Maintainer: Adrian Schroeter <adr...@suse.de>
 Architecture: any

Reply via email to