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