Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package osc for openSUSE:Factory checked in at 2023-08-10 15:34:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/osc (Old) and /work/SRC/openSUSE:Factory/.osc.new.11712 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "osc" Thu Aug 10 15:34:24 2023 rev:180 rq:1103223 version:1.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/osc/osc.changes 2023-07-15 23:15:24.723603895 +0200 +++ /work/SRC/openSUSE:Factory/.osc.new.11712/osc.changes 2023-08-10 15:34:37.404434297 +0200 @@ -1,0 +2,25 @@ +Wed Aug 9 11:36:47 UTC 2023 - Daniel Mach <[email protected]> + +- 1.3.0 + - Command-line: + - Add experimental support of Git SCM to the 'build' command + - Add experimental support of Git SCM to the 'service' command + - Make 'meta' command capable of editing attributes + - Change '--add' option in 'meta attribute' command to skip duplicate values + - Add an interactive option to display build log in 'request list -i' command + - Add '--setopt' option for setting config options from the command-line + - Fix '--prefer-pkgs' option for noinstall="1" packages in kiwi builds + - Change 'checkout' command to print open requests only when running in an interactive terminal + - Enhance '--force' option description in the 'request' command + - Connection: + - Fix crash when HTTP_PROXY env contains no auth + - Library: + - Add 'git_scm' module for handling packages that live in git scm rather than usual obs scm + - Change pop_project_package_from_args() to use get_store() to support Git SCM + - Change osc.build module to use 'store' object instead of calling core.store_*() functions + - Use alternative project if specified in parse_repoarchdescr() + - Fix xml indent() on Python 3.6 + - Fix less pager by adding '-R' to LESS env + - Improve print_msg() and migrate some arbitrary prints to it + +------------------------------------------------------------------- Old: ---- osc-1.2.0.tar.gz New: ---- osc-1.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ osc.spec ++++++ --- /var/tmp/diff_new_pack.nqBlCg/_old 2023-08-10 15:34:38.336440110 +0200 +++ /var/tmp/diff_new_pack.nqBlCg/_new 2023-08-10 15:34:38.340440135 +0200 @@ -49,7 +49,7 @@ %endif Name: osc -Version: 1.2.0 +Version: 1.3.0 Release: 0 Summary: Command-line client for the Open Build Service License: GPL-2.0-or-later @@ -75,6 +75,8 @@ BuildRequires: %{use_python_pkg}-setuptools BuildRequires: %{use_python_pkg}-urllib3 BuildRequires: diffstat +# needed for git scm tests +BuildRequires: git-core Requires: %{use_python_pkg}-cryptography Requires: %{use_python_pkg}-rpm @@ -96,6 +98,10 @@ Recommends: powerpc32 Recommends: sudo +# needed for building from git +Recommends: git-core +Recommends: git-lfs + # needed for `osc add <URL>` Recommends: obs-service-recompress Recommends: obs-service-download_files ++++++ PKGBUILD ++++++ --- /var/tmp/diff_new_pack.nqBlCg/_old 2023-08-10 15:34:38.376440359 +0200 +++ /var/tmp/diff_new_pack.nqBlCg/_new 2023-08-10 15:34:38.380440384 +0200 @@ -1,5 +1,5 @@ pkgname=osc -pkgver=1.2.0 +pkgver=1.3.0 pkgrel=0 pkgdesc="Command-line client for the Open Build Service" arch=('x86_64') ++++++ debian.changelog ++++++ --- /var/tmp/diff_new_pack.nqBlCg/_old 2023-08-10 15:34:38.420440634 +0200 +++ /var/tmp/diff_new_pack.nqBlCg/_new 2023-08-10 15:34:38.424440659 +0200 @@ -1,4 +1,4 @@ -osc (1.2.0-0) unstable; urgency=low +osc (1.3.0-0) unstable; urgency=low * Placeholder ++++++ osc-1.2.0.tar.gz -> osc-1.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/NEWS new/osc-1.3.0/NEWS --- old/osc-1.2.0/NEWS 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/NEWS 2023-08-09 13:34:16.000000000 +0200 @@ -1,3 +1,25 @@ +- 1.3.0 + - Command-line: + - Add experimental support of Git SCM to the 'build' command + - Add experimental support of Git SCM to the 'service' command + - Make 'meta' command capable of editing attributes + - Change '--add' option in 'meta attribute' command to skip duplicate values + - Add an interactive option to display build log in 'request list -i' command + - Add '--setopt' option for setting config options from the command-line + - Fix '--prefer-pkgs' option for noinstall="1" packages in kiwi builds + - Change 'checkout' command to print open requests only when running in an interactive terminal + - Enhance '--force' option description in the 'request' command + - Connection: + - Fix crash when HTTP_PROXY env contains no auth + - Library: + - Add 'git_scm' module for handling packages that live in git scm rather than usual obs scm + - Change pop_project_package_from_args() to use get_store() to support Git SCM + - Change osc.build module to use 'store' object instead of calling core.store_*() functions + - Use alternative project if specified in parse_repoarchdescr() + - Fix xml indent() on Python 3.6 + - Fix less pager by adding '-R' to LESS env + - Improve print_msg() and migrate some arbitrary prints to it + - 1.2.0 - Command-line: - Add 'repo' command and subcommands for managing repositories in project meta diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/__init__.py new/osc-1.3.0/osc/__init__.py --- old/osc-1.2.0/osc/__init__.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/__init__.py 2023-08-09 13:34:16.000000000 +0200 @@ -13,7 +13,7 @@ from .util import git_version -__version__ = git_version.get_version('1.2.0') +__version__ = git_version.get_version('1.3.0') # vim: sw=4 et diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/_private/common.py new/osc-1.3.0/osc/_private/common.py --- old/osc-1.2.0/osc/_private/common.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/_private/common.py 2023-08-09 13:34:16.000000000 +0200 @@ -1,16 +1,22 @@ import sys -def print_msg(msg, print_to="debug"): +def print_msg(*args, print_to="debug"): from .. import conf if print_to is None: return elif print_to == "debug": + # print a debug message to stderr if config["debug"] is set if conf.config["debug"]: - print(f"DEBUG: {msg}", file=sys.stderr) + print("DEBUG:", *args, file=sys.stderr) + elif print_to == "verbose": + # print a verbose message to stdout if config["verbose"] or config["debug"] is set + if conf.config["verbose"] or conf.config["debug"]: + print(*args) elif print_to == "stdout": - print(msg) + # print the message to stdout + print(*args) else: raise ValueError(f"Invalid value of the 'print_to' option: {print_to}") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/_private/project.py new/osc-1.3.0/osc/_private/project.py --- old/osc-1.2.0/osc/_private/project.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/_private/project.py 2023-08-09 13:34:16.000000000 +0200 @@ -10,7 +10,7 @@ self.apiurl = apiurl def to_bytes(self): - ET.indent(self.root, space=" ", level=0) + api.xml_indent(self.root) return ET.tostring(self.root, encoding="utf-8") def to_string(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/babysitter.py new/osc-1.3.0/osc/babysitter.py --- old/osc-1.2.0/osc/babysitter.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/babysitter.py 2023-08-09 13:34:16.000000000 +0200 @@ -116,9 +116,8 @@ except AttributeError: body = '' - if osc_conf.config["debug"]: - print(e.hdrs, file=sys.stderr) - print(body, file=sys.stderr) + _private.print_msg(e.hdrs, print_to="debug") + _private.print_msg(body, print_to="debug") if e.code in [400, 403, 404, 500]: if b'<summary>' in body: @@ -162,8 +161,7 @@ print(e.message, file=sys.stderr) except oscerr.OscIOError as e: print(e.msg, file=sys.stderr) - if osc_conf.config["debug"]: - print(e.e, file=sys.stderr) + _private.print_msg(e.e, print_to="debug") except (oscerr.WrongOptions, oscerr.WrongArgs) as e: print(e, file=sys.stderr) return 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/build.py new/osc-1.3.0/osc/build.py --- old/osc-1.2.0/osc/build.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/build.py 2023-08-09 13:34:16.000000000 +0200 @@ -20,7 +20,7 @@ from . import connection from . import core from . import oscerr -from .core import get_buildinfo, store_read_project, store_read_package, meta_exists, quote_plus, get_buildconfig, is_package_dir, dgst +from .core import get_buildinfo, meta_exists, quote_plus, get_buildconfig, dgst from .core import get_binarylist, get_binary_file, run_external, return_external, raw_input from .fetch import Fetcher, OscFileGrabber, verify_pacs from .meter import create_text_meter @@ -595,12 +595,13 @@ return kiwipath -def calculate_prj_pac(opts, descr): - project = opts.alternative_project or store_read_project('.') +def calculate_prj_pac(store, opts, descr): + project = opts.alternative_project or store.project if opts.local_package: package = os.path.splitext(os.path.basename(descr))[0] else: - package = store_read_package('.') + store.assert_is_package() + package = store.package return project, package @@ -639,7 +640,7 @@ return run_external(cmd[0], *cmd[1:]) -def main(apiurl, opts, argv): +def main(apiurl, store, opts, argv): repo = argv[0] arch = argv[1] @@ -768,11 +769,11 @@ prj = opts.alternative_project pac = '_repository' else: - prj = store_read_project(os.curdir) + prj = store.project if opts.local_package: pac = '_repository' else: - pac = store_read_package(os.curdir) + pac = store.package if opts.multibuild_package: buildargs.append('--buildflavor=%s' % opts.multibuild_package) pac = pac + ":" + opts.multibuild_package @@ -797,7 +798,7 @@ if pacname == '_repository': if not opts.local_package: try: - pacname = store_read_package(os.curdir) + pacname = store.package except oscerr.NoWorkingCopy: opts.local_package = True if opts.local_package: @@ -834,7 +835,7 @@ bc_file = None bi_filename = '_buildinfo-%s-%s.xml' % (repo, arch) bc_filename = '_buildconfig-%s-%s' % (repo, arch) - if is_package_dir('.') and os.access(core.store, os.W_OK): + if store.is_package and os.access(core.store, os.W_OK): bi_filename = os.path.join(os.getcwd(), core.store, bi_filename) bc_filename = os.path.join(os.getcwd(), core.store, bc_filename) elif not os.access('.', os.W_OK): @@ -859,7 +860,7 @@ if opts.noinit: buildargs.append('--noinit') - if not is_package_dir('.'): + if not store.is_package: opts.noservice = True # check for source services @@ -1268,10 +1269,24 @@ if name == filename: print("Using prefered package: " + path + "/" + filename) os.unlink(tffn) - if opts.linksources: - os.link(path + "/" + filename, tffn) - else: - os.symlink(path + "/" + filename, tffn) + + if prefer_pkgs: + localpkgdir = "repos/_local/" + os.mkdir(localpkgdir) + buildargs.append("--kiwi-parameter") + buildargs.append("--add-repo") + buildargs.append("--kiwi-parameter") + buildargs.append(f"dir://./{localpkgdir}") + buildargs.append("--kiwi-parameter") + buildargs.append("--add-repotype") + buildargs.append("--kiwi-parameter") + buildargs.append("rpm-md") + for name, path in prefer_pkgs.items(): + tffn = os.path.join(localpkgdir, os.path.basename(path)) + if opts.linksources: + os.link(path, tffn) + else: + os.symlink(path, tffn) if build_type == 'kiwi': # Is a obsrepositories tag used? @@ -1481,8 +1496,8 @@ cmd = [change_personality[bi.buildarch]] + cmd # record our settings for later builds - if is_package_dir(os.curdir): - core.store_write_last_buildroot(os.curdir, repo, arch, vm_type) + if store.is_package: + store.last_buildroot = repo, arch, vm_type try: rc = run_external(cmd[0], *cmd[1:]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/commandline.py new/osc-1.3.0/osc/commandline.py --- old/osc-1.2.0/osc/commandline.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/commandline.py 2023-08-09 13:34:16.000000000 +0200 @@ -30,6 +30,7 @@ from . import cmdln from . import commands as osc_commands from . import conf +from . import git_scm from . import oscerr from . import store as osc_store from .core import * @@ -384,6 +385,13 @@ help="specify alternate configuration file", ) self.add_argument( + "--setopt", + metavar="KEY=VALUE", + action="append", + default=[], + help="set a config option for the current program run", + ) + self.add_argument( "--no-keyring", action="store_true", help="disable usage of desktop keyring system", @@ -401,6 +409,11 @@ # let's leave setting the right value to conf.get_config() pass + overrides = {} + for i in args.setopt: + key, value = i.split("=") + overrides[key] = value + try: conf.get_config( override_apiurl=args.apiurl, @@ -412,6 +425,7 @@ override_post_mortem=args.post_mortem, override_traceback=args.traceback, override_verbose=args.verbose, + overrides=overrides, ) except oscerr.NoConfigfile as e: print(e.msg, file=sys.stderr) @@ -675,9 +689,8 @@ if project == ".": # project name taken from the working copy - project_store = osc_store.Store(path) try: - project_store = osc_store.Store(path) + project_store = osc_store.get_store(path) project = project_store.project except oscerr.NoWorkingCopy: if not project_is_optional: @@ -687,7 +700,7 @@ if package == ".": # package name taken from the working copy try: - package_store = osc_store.Store(path) + package_store = osc_store.get_store(path) package_store.assert_is_package() package = package_store.package except oscerr.NoWorkingCopy: @@ -1741,7 +1754,7 @@ @cmdln.option('-s', '--set', metavar='ATTRIBUTE_VALUES', help='set attribute values') @cmdln.option('--add', metavar='ATTRIBUTE_VALUES', - help='add to the existing attribute values') + help='add to the existing attribute values, skip duplicates') @cmdln.option('--delete', action='store_true', help='delete a pattern or attribute') def do_meta(self, subcmd, opts, *args): @@ -1816,7 +1829,16 @@ if opts.add and opts.set: self.argparse_error("Options --add and --set are mutually exclusive") + if cmd == "attribute" and opts.edit and not opts.attribute: + self.argparse_error("Please specify --attribute") + apiurl = self.get_api_url() + project = None + package = None + subpackage = None + user = None + group = None + pattern = None # Specific arguments # @@ -1953,6 +1975,16 @@ path_args=(project, pattern), apiurl=apiurl, template_args=None) + elif cmd == 'attribute': + edit_meta( + metatype='attribute', + edit=True, + path_args=(quote_plus(project), quote_plus(opts.attribute)), + apiurl=apiurl, + # PUT is not supported + method="POST", + template_args=None, + ) # create attribute entry if (opts.create or opts.set or opts.add) and cmd == 'attribute': @@ -1963,7 +1995,7 @@ if len(aname) != 2: raise oscerr.WrongOptions('Given attribute is not in "NAMESPACE:NAME" style') - values = '' + values = [] if opts.add: # read the existing values from server @@ -1971,8 +2003,7 @@ nodes = _private.api.find_nodes(root, "attributes", "attribute", {"namespace": aname[0], "name": aname[1]}, "value") for node in nodes: # append the existing values - value = _private.api.xml_escape(node.text) - values += f"<value>{value}</value>" + values.append(node.text) # pretend we're setting values in order to append the values we have specified on the command-line, # because OBS API doesn't support extending the value list directly @@ -1980,9 +2011,20 @@ if opts.set: for i in opts.set.split(','): - values += '<value>%s</value>' % _private.api.xml_escape(i) + # append the new values + # we skip duplicates during --add + if opts.add and i in values: + continue + values.append(i) - d = '<attributes><attribute namespace=\'%s\' name=\'%s\' >%s</attribute></attributes>' % (aname[0], aname[1], values) + values_str = "" + for value in values: + value = _private.api.xml_escape(value) + values_str += f"<value>{value}</value>" + + ns = _private.api.xml_escape(aname[0]) + name = _private.api.xml_escape(aname[1]) + d = f"<attributes><attribute namespace='{ns}' name='{name}' >{values_str}</attribute></attributes>" url = makeurl(apiurl, attributepath) for data in streamfile(url, http_POST, data=d): sys.stdout.buffer.write(data) @@ -2976,7 +3018,7 @@ @cmdln.option('-a', '--all', action='store_true', help='all states. Same as\'-s all\'') @cmdln.option('-f', '--force', action='store_true', - help='enforce state change, can be used to ignore open reviews') + help='enforce state change, can be used to ignore open reviews, devel-package dependencies and more') @cmdln.option('-s', '--state', help='only list requests in one of the comma separated given states (new/review/accepted/revoked/declined) or "all" [default="new,review,declined"]') @cmdln.option('-D', '--days', metavar='DAYS', @@ -4254,8 +4296,7 @@ except: print('Error while checkout package:\n', package, file=sys.stderr) - if conf.config['verbose']: - print('Note: You can use "osc delete" or "osc submitpac" when done.\n') + _private.print_msg('Note: You can use "osc delete" or "osc submitpac" when done.\n', print_to="verbose") @cmdln.alias('branchco') @cmdln.alias('bco') @@ -4406,8 +4447,7 @@ if opts.checkout: checkout_package(apiurl, targetprj, package, server_service_files=False, expand_link=True, prj_dir=Path(targetprj)) - if conf.config['verbose']: - print('Note: You can use "osc delete" or "osc submitpac" when done.\n') + _private.print_msg('Note: You can use "osc delete" or "osc submitpac" when done.\n', print_to="verbose") else: apiopt = '' if conf.get_configParser().get('general', 'apiurl') != apiurl: @@ -5257,7 +5297,8 @@ server_service_files=opts.server_side_source_service_files, progress_obj=self.download_progress, size_limit=opts.limit_size, meta=opts.meta, outdir=opts.output_dir) - print_request_list(apiurl, project, package) + if os.isatty(sys.stdout.fileno()): + print_request_list(apiurl, project, package) elif project: sep = '/' if not opts.output_dir and conf.config['checkout_no_colon'] else conf.config['project_separator'] @@ -5318,7 +5359,8 @@ server_service_files=opts.server_side_source_service_files, progress_obj=self.download_progress, size_limit=opts.limit_size, meta=opts.meta) - print_request_list(apiurl, project) + if os.isatty(sys.stdout.fileno()): + print_request_list(apiurl, project) else: self.argparse_error("Incorrect number of arguments.") @@ -6923,7 +6965,7 @@ if no_repo: raise oscerr.WrongArgs("Repository is missing. Cannot guess build description without repository") apiurl = self.get_api_url() - project = store_read_project('.') + project = alternative_project or store_read_project('.') # some distros like Debian rename and move build to obs-build if not os.path.isfile('/usr/lib/build/queryconfig') and os.path.isfile('/usr/lib/obs-build/queryconfig'): queryconfig = '/usr/lib/obs-build/queryconfig' @@ -7187,25 +7229,22 @@ if len(args) > 3: raise oscerr.WrongArgs('Too many arguments') - project = None - try: - project = store_read_project(Path.cwd()) - if project == opts.alternative_project: - opts.alternative_project = None - except oscerr.NoWorkingCopy: - # This may be a project managed entirely via git? - if os.path.isdir(Path.cwd().parent / '.osc') and os.path.isdir(Path.cwd().parent / '.git'): - project = store_read_project(Path.cwd()) - opts.alternative_project = project - pass + store = osc_store.get_store(Path.cwd(), print_warnings=True) + store.assert_is_package() + + if opts.alternative_project == store.project: + opts.alternative_project = None - if len(args) == 0 and is_package_dir(Path.cwd()): + # HACK: avoid calling some underlying store_*() functions from parse_repoarchdescr() method + # We'll fix parse_repoarchdescr() later because it requires a larger change + if not opts.alternative_project and isinstance(store, git_scm.GitStore): + opts.alternative_project = store.project + + if len(args) == 0 and store.is_package and store.last_buildroot: # build env not specified, just read from last build attempt - lastbuildroot = store_read_last_buildroot(Path.cwd()) - if lastbuildroot: - args = [lastbuildroot[0], lastbuildroot[1]] - if not opts.vm_type: - opts.vm_type = lastbuildroot[2] + args = [store.last_buildroot[0], store.last_buildroot[1]] + if not opts.vm_type: + opts.vm_type = store.last_buildroot[2] vm_chroot = opts.vm_type or conf.config['build-type'] if (subcmd in ('shell', 'chroot') or opts.shell or opts.wipe) and not vm_chroot: @@ -7214,7 +7253,7 @@ else: args = self.parse_repoarchdescr(args, opts.noinit or opts.offline, opts.alternative_project, False, opts.vm_type, opts.multibuild_package) repo, arch, build_descr = args - prj, pac = osc_build.calculate_prj_pac(opts, build_descr) + prj, pac = osc_build.calculate_prj_pac(store, opts, build_descr) apihost = urlsplit(self.get_api_url())[1] build_root = osc_build.calculate_build_root(apihost, prj, pac, repo, arch) @@ -7238,12 +7277,12 @@ if not opts.local_package: try: - package = store_read_package(Path.cwd()) prj = Project(os.pardir, getPackageList=False, wc_check=False) - if prj.status(package) == 'A': + if prj.status(store.package) == "A": # a package with state 'A' most likely does not exist on # the server - hence, treat it as a local package opts.local_package = True + print("INFO: Building the package as a local package.", file=sys.stderr) except oscerr.NoWorkingCopy: pass @@ -7272,7 +7311,7 @@ print('Building %s for %s/%s' % (args[2], args[0], args[1])) if not opts.host: - return osc_build.main(self.get_api_url(), opts, args) + return osc_build.main(self.get_api_url(), store, opts, args) else: return self._do_rbuild(subcmd, opts, *args) @@ -7583,36 +7622,33 @@ """ # disabledrun and localrun exists as well, but are considered to be obsolete - args = slash_split(args) - project = package = singleservice = mode = None + args = list(args) apiurl = self.get_api_url() - remote_commands = ('remoterun', 'rr', 'merge', 'wait') + project = None + package = None + singleservice = None + mode = None + remote_commands = ("remoterun", "rr", "merge", "wait") + obsolete_commands = ("localrun", "lr", "disabledrun", "dr") if len(args) < 1: - raise oscerr.WrongArgs('No command given.') - elif len(args) < 3: - if args[0] in remote_commands: - if not is_package_dir(Path.cwd()): - msg = ('Either specify the project and package or execute ' - 'the command in a package working copy.') - raise oscerr.WrongArgs(msg) - package = store_read_package(Path.cwd()) - project = store_read_project(Path.cwd()) - else: - # raise an appropriate exception if Path.cwd() is no package wc - store_read_package(Path.cwd()) - if len(args) == 2: - singleservice = args[1] - elif len(args) == 3 and args[0] in remote_commands: - project = self._process_project_name(args[1]) - package = args[2] - else: - raise oscerr.WrongArgs('Too many arguments.') + self.argparse_error("Please specify a command") - command = args[0] + command = args.pop(0) + if command not in ("runall", "ra", "run", "localrun", "manualrun", "disabledrun", "remoterun", "lr", "dr", "mr", "rr", "merge", "wait"): + self.argparse_error(f"Invalid command: {command}") - if command not in ('runall', 'ra', 'run', 'localrun', 'manualrun', 'disabledrun', 'remoterun', 'lr', 'dr', 'mr', 'rr', 'merge', 'wait'): - raise oscerr.WrongArgs('Wrong command given.') + if command in obsolete_commands: + print(f"WARNING: Command '{command}' is obsolete", file=sys.stderr) + + if len(args) == 1: + singleservice = args.pop(0) + elif len(args) in (0, 2) and command in remote_commands: + project, package = pop_project_package_from_args( + args, default_project=".", default_package=".", package_is_optional=False + ) + + ensure_no_remaining_args(args) if command in ('remoterun', 'rr'): print(runservice(apiurl, project, package)) @@ -7626,9 +7662,10 @@ print(mergeservice(apiurl, project, package)) return + store = osc_store.get_store(Path.cwd(), print_warnings=True) + store.assert_is_package() + if command in ('runall', 'ra', 'run', 'localrun', 'manualrun', 'disabledrun', 'lr', 'mr', 'dr', 'r'): - if not is_package_dir(Path.cwd()): - raise oscerr.WrongArgs('Local directory is no package') p = Package(".") if command in ("localrun", "lr"): mode = "local" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/conf.py new/osc-1.3.0/osc/conf.py --- old/osc-1.2.0/osc/conf.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/conf.py 2023-08-09 13:34:16.000000000 +0200 @@ -232,11 +232,13 @@ cp = OscConfigParser.OscConfigParser(config) cp.add_section("general") - typed_opts = ((_boolean_opts, cp.getboolean), (_integer_opts, cp.getint)) - for opts, meth in typed_opts: + typed_opts = ((_boolean_opts, cp.getboolean, bool), (_integer_opts, cp.getint, int)) + for opts, meth, typ in typed_opts: for opt in opts: if opt not in config: continue + if isinstance(config[opt], typ): + continue try: config[opt] = meth('general', opt) except ValueError as e: @@ -750,7 +752,9 @@ override_traceback=None, override_post_mortem=None, override_no_keyring=None, - override_verbose=None): + override_verbose=None, + overrides=None + ): """do the actual work (see module documentation)""" global config @@ -786,6 +790,17 @@ raise oscerr.ConfigError(msg, conffile) config = dict(cp.items('general', raw=1)) + + # if the overrides trigger an exception, the 'post_mortem' option + # must be set to the appropriate type otherwise the non-empty string gets evaluated as True + config = apply_option_types(config, conffile) + + overrides = overrides or {} + for key, value in overrides.items(): + if key not in config: + raise oscerr.ConfigError(f"Unknown config option '{key}'", "<command-line>") + config[key] = value + config['conffile'] = conffile config = apply_option_types(config, conffile) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/connection.py new/osc-1.3.0/osc/connection.py --- old/osc-1.2.0/osc/connection.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/connection.py 2023-08-09 13:34:16.000000000 +0200 @@ -21,6 +21,7 @@ import urllib3.util from . import __version__ +from . import _private from . import conf from . import oscerr from . import oscssl @@ -107,10 +108,11 @@ user_agent=f"osc/{__version__}", ) - proxy_basic_auth = urllib.parse.unquote(proxy_purl.auth) - proxy_basic_auth = proxy_basic_auth.encode("utf-8") - proxy_basic_auth = base64.b64encode(proxy_basic_auth).decode() - proxy_headers["Proxy-Authorization"] = f"Basic {proxy_basic_auth:s}" + if proxy_purl.auth: + proxy_basic_auth = urllib.parse.unquote(proxy_purl.auth) + proxy_basic_auth = proxy_basic_auth.encode("utf-8") + proxy_basic_auth = base64.b64encode(proxy_basic_auth).decode() + proxy_headers["Proxy-Authorization"] = f"Basic {proxy_basic_auth:s}" manager = urllib3.ProxyManager(proxy_url, proxy_headers=proxy_headers) return manager @@ -685,9 +687,7 @@ return False if not self.ssh_keygen_path: - if conf.config["debug"]: - msg = "Skipping signature auth because ssh-keygen is not available" - print(msg, file=sys.stderr) + _private.print_msg("Skipping signature auth because ssh-keygen is not available", print_to="debug") return False if not self.sshkey_known(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/core.py new/osc-1.3.0/osc/core.py --- old/osc-1.2.0/osc/core.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/core.py 2023-08-09 13:34:16.000000000 +0200 @@ -18,6 +18,7 @@ import fnmatch import glob import hashlib +import io import locale import os import platform @@ -49,6 +50,7 @@ from . import conf from . import meter from . import oscerr +from . import store as osc_store from .connection import http_request, http_GET, http_POST, http_PUT, http_DELETE from .store import Store from .util.helper import decode_list, decode_it, raw_input, _html_escape @@ -516,8 +518,7 @@ raise oscerr.PackageNotInstalled("obs-service-%s" % cmd[0]) cmd[0] = "/usr/lib/obs/service/" + cmd[0] cmd = cmd + ["--outdir", temp_dir] - if conf.config['verbose'] or verbose or conf.config['debug']: - print("Run source service:", ' '.join(cmd)) + _private.print_msg("Run source service:", " ".join(cmd), print_to="verbose") r = run_external(*cmd) if r != 0: @@ -1239,7 +1240,8 @@ self.dir = workingdir or "." self.absdir = os.path.abspath(self.dir) - self.store = Store(self.dir) + self.store = osc_store.get_store(self.dir) + self.store.assert_is_package() self.storedir = os.path.join(self.absdir, store) self.progress_obj = progress_obj self.size_limit = size_limit @@ -1247,10 +1249,8 @@ if size_limit and size_limit == 0: self.size_limit = None - check_store_version(self.dir) - - self.prjname = store_read_project(self.dir) - self.name = store_read_package(self.dir) + self.prjname = self.store.project + self.name = self.store.package self.apiurl = self.store.apiurl self.update_datastructs() @@ -3600,8 +3600,7 @@ function. In case of a list not -- this is to be backwards compatible. """ query = query or [] - if conf.config['debug']: - print('makeurl:', baseurl, l, query) + _private.print_msg("makeurl:", baseurl, l, query, print_to="debug") if isinstance(query, list): query = '&'.join(query) @@ -3929,7 +3928,7 @@ def __call__(self, **kwargs): return self._delegate(**kwargs) - def __init__(self, url, input, change_is_required=False, file_ext='.xml'): + def __init__(self, url, input, change_is_required=False, file_ext='.xml', method=None): if isinstance(url, self._URLFactory): self._url_factory = url else: @@ -3939,6 +3938,7 @@ self.url = self._url_factory() self.change_is_required = change_is_required (fd, self.filename) = tempfile.mkstemp(prefix='osc_metafile.', suffix=file_ext) + self._method = method open_mode = 'w' input_as_str = None @@ -3964,7 +3964,10 @@ print('Sending meta data...') # don't do any exception handling... it's up to the caller what to do in case # of an exception - http_PUT(self.url, file=self.filename) + if self._method == "POST": + http_POST(self.url, file=self.filename) + else: + http_PUT(self.url, file=self.filename) os.unlink(self.filename) print('Done.') @@ -4021,7 +4024,7 @@ 'template': new_package_templ, 'file_ext': '.xml' }, - 'attribute': {'path': 'source/%s/%s/_meta', + 'attribute': {'path': 'source/%s/_attribute/%s', 'template': new_attribute_templ, 'file_ext': '.xml' }, @@ -4117,6 +4120,7 @@ remove_linking_repositories=False, change_is_required=False, apiurl: Optional[str] = None, + method: Optional[str] = None, msg=None, ): @@ -4150,7 +4154,7 @@ return make_meta_url(metatype, path_args, apiurl, force, remove_linking_repositories, msg) url_factory = metafile._URLFactory(delegate) - f = metafile(url_factory, data, change_is_required, metatypes[metatype]['file_ext']) + f = metafile(url_factory, data, change_is_required, metatypes[metatype]['file_ext'], method=method) if edit: f.edit() @@ -4402,11 +4406,21 @@ else: tmpfile.write(message) tmpfile.flush() + + env = os.environ.copy() + pager = os.getenv("PAGER", default="").strip() pager = pager or get_default_pager() + + # LESS env is not always set and we need -R to display escape sequences properly + less_opts = os.getenv("LESS", default="") + if "-R" not in less_opts: + less_opts += " -R" + env["LESS"] = less_opts + cmd = shlex.split(pager) + [tmpfile.name] try: - run_external(*cmd) + run_external(*cmd, env=env) finally: tmpfile.close() @@ -4774,8 +4788,7 @@ xpath_base = xpath_join(xpath_base, 'action/source/@%(kind)s=\'%(val)s\'', op='or', inner=True) xpath = xpath_join(xpath, xpath_base % {'kind': kind, 'val': val}, op='and', nexpr_parentheses=True) - if conf.config['debug']: - print('[ %s ]' % xpath) + _private.print_msg(f"[ {xpath} ]", print_to="debug") res = search(apiurl, request=xpath) collection = res['request'] requests = [] @@ -4916,8 +4929,7 @@ if req_type: xpath += " and action/@type=\'%s\'" % req_type - if conf.config['debug']: - print('[ %s ]' % xpath) + _private.print_msg(f"[ {xpath} ]", print_to="debug") res = search(apiurl, request=xpath) collection = res['request'] @@ -5589,10 +5601,10 @@ prj_dir = Path(str(prj_dir).replace(':', sep)) root_dots = Path('.') + oldproj = None if conf.config['checkout_rooted']: if prj_dir.stem == '/': - if conf.config['verbose']: - print("checkout_rooted ignored for %s" % prj_dir) + _private.print_msg(f"checkout_rooted ignored for {prj_dir}", print_to="verbose") # ?? should we complain if not is_project_dir(prj_dir) ?? else: # if we are inside a project or package dir, ascend to parent @@ -5619,9 +5631,7 @@ root_dots = root_dots / ("../" * n) if str(root_dots) != '.': - if conf.config['verbose']: - print("%s is project dir of %s. Root found at %s" % - (prj_dir, oldproj, os.path.abspath(root_dots))) + _private.print_msg(f"{prj_dir} is project dir of {oldproj}. Root found at {os.path.abspath(root_dots)}", print_to="verbose") prj_dir = root_dots / prj_dir if not pathname: @@ -6936,13 +6946,16 @@ strip_time=False, last=False, lastsucceeded=False, + output_buffer=None, ): """prints out the buildlog on stdout""" + output_buffer = output_buffer or sys.stdout.buffer + def print_data(data, strip_time=False): if strip_time: data = buildlog_strip_time(data) - sys.stdout.buffer.write(data) + output_buffer.write(data) query = {'nostream': '1', 'start': '%s' % offset} if last: @@ -8263,39 +8276,73 @@ print('Try -f to force the state change', file=sys.stderr) return False - def safe_get_rpmlint_log(src_actions): - lintlogs = [] + def get_repos(src_actions): + """ + Translate src_actions to [{"proj": ..., "pkg": ..., "repo": ..., "arch": ...}] + """ + result = [] for action in src_actions: - print('Type %s:' % action.type) disabled = show_package_disabled_repos(apiurl, action.src_project, action.src_package) for repo in get_repos_of_project(apiurl, action.src_project): - if (disabled is None) or (repo.name not in [d['repo'] for d in disabled]): - lintlog_entry = { - 'proj': action.src_project, - 'pkg': action.src_package, - 'repo': repo.name, - 'arch': repo.arch + if (disabled is None) or (repo.name not in [d["repo"] for d in disabled]): + entry = { + "proj": action.src_project, + "pkg": action.src_package, + "repo": repo.name, + "arch": repo.arch } - lintlogs.append(lintlog_entry) - print('(%i) %s/%s/%s/%s' % ((len(lintlogs) - 1), action.src_project, action.src_package, repo.name, repo.arch)) - if not lintlogs: - print('No possible rpmlintlogs found') - return False + result.append(entry) + return result + + def select_repo(src_actions): + """ + Prompt user to select a repo from a list. + """ + repos = get_repos(src_actions) + + for num, entry in enumerate(repos): + print(f"({num}) {entry['proj']}/{entry['pkg']}/{entry['repo']}/{entry['arch']}") + + if not repos: + print('No repos') + return None + while True: try: - lint_n = int(raw_input('Number of rpmlint log to examine (0 - %i): ' % (len(lintlogs) - 1))) - lintlogs[lint_n] - break + reply = raw_input(f"Number of repo to examine (0 - {len(repos)-1}): ").strip() + if not reply: + return None + reply_num = int(reply) + return repos[reply_num] except (ValueError, IndexError): - print('Invalid rpmlintlog index. Please choose between 0 and %i' % (len(lintlogs) - 1)) + print(f"Invalid index. Please choose between 0 and {len(repos)-1}") + + def safe_get_rpmlint_log(src_actions): + repo = select_repo(src_actions) + if not repo: + return try: - print(decode_it(get_rpmlint_log(apiurl, **lintlogs[lint_n]))) + run_pager(get_rpmlint_log(apiurl, **repo)) except HTTPError as e: if e.code == 404: - print('No rpmlintlog for %s %s' % (lintlogs[lint_n]['repo'], - lintlogs[lint_n]['arch'])) + print(f"No rpmlint log for {repo['repo']}/{repo['arch']}") else: - raise e + raise + + def get_build_log(src_actions): + repo = select_repo(src_actions) + if not repo: + return + try: + buffer = io.BytesIO() + print_buildlog(apiurl, repo["proj"], repo["pkg"], repo["repo"], repo["arch"], output_buffer=buffer) + buffer.seek(0) + run_pager(buffer.read()) + except HTTPError as e: + if e.code == 404: + print(f"No build log for {repo['repo']}/{repo['arch']}") + else: + raise def print_request(request): print(request) @@ -8344,10 +8391,10 @@ # actions which have sources + buildresults src_actions = editable_actions + request.get_actions('maintenance_release') if editable_actions: - prompt = 'd(i)ff/(a)ccept/(d)ecline/(r)evoke/(b)uildstatus/rpm(li)ntlog/c(l)one/(e)dit/co(m)ment/(s)kip/(c)ancel > ' + prompt = 'd(i)ff/(a)ccept/(d)ecline/(r)evoke/(b)uildstatus/(bl)buildlog/rpm(li)ntlog/c(l)one/(e)dit/co(m)ment/(s)kip/(c)ancel > ' elif src_actions: # no edit for maintenance release requests - prompt = 'd(i)ff/(a)ccept/(d)ecline/(r)evoke/(b)uildstatus/rpm(li)ntlog/c(l)one/co(m)ment/(s)kip/(c)ancel > ' + prompt = 'd(i)ff/(a)ccept/(d)ecline/(r)evoke/(b)uildstatus/(bl)buildlog/rpm(li)ntlog/c(l)one/co(m)ment/(s)kip/(c)ancel > ' editprj = '' orequest = None if source_buildstatus and src_actions: @@ -8406,6 +8453,8 @@ print_source_buildstatus(src_actions) elif repl == 'li' and src_actions: safe_get_rpmlint_log(src_actions) + elif repl == 'bl' and src_actions: + get_build_log(src_actions) elif repl == 'e' and editable_actions: # this is only for editable actions if not editprj: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/git_scm/README.md new/osc-1.3.0/osc/git_scm/README.md --- old/osc-1.2.0/osc/git_scm/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/osc-1.3.0/osc/git_scm/README.md 2023-08-09 13:34:16.000000000 +0200 @@ -0,0 +1,4 @@ +# Warning + +This module provides EXPERIMENTAL and UNSTABLE support for git scm such as https://src.opensuse.org/. +The code may change or disappear without a prior notice! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/git_scm/__init__.py new/osc-1.3.0/osc/git_scm/__init__.py --- old/osc-1.2.0/osc/git_scm/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/osc-1.3.0/osc/git_scm/__init__.py 2023-08-09 13:34:16.000000000 +0200 @@ -0,0 +1,7 @@ +import sys + +from .store import GitStore + + +def warn_experimental(): + print("WARNING: Using EXPERIMENTAL support for git scm. The functionality may change or disappear without a prior notice!", file=sys.stderr) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/git_scm/store.py new/osc-1.3.0/osc/git_scm/store.py --- old/osc-1.2.0/osc/git_scm/store.py 1970-01-01 01:00:00.000000000 +0100 +++ new/osc-1.3.0/osc/git_scm/store.py 2023-08-09 13:34:16.000000000 +0200 @@ -0,0 +1,151 @@ +import json +import os +import subprocess +import urllib.parse +from pathlib import Path + +from .. import conf as osc_conf +from .. import oscerr + + +class GitStore: + + @classmethod + def is_project_dir(cls, path): + try: + store = cls(path) + except oscerr.NoWorkingCopy: + return False + return store.is_project + + @classmethod + def is_package_dir(cls, path): + try: + store = cls(path) + except oscerr.NoWorkingCopy: + return False + return store.is_package + + def __init__(self, path, check=True): + self.path = path + self.abspath = os.path.abspath(self.path) + + # TODO: how to determine if the current git repo contains a project or a package? + self.is_project = False + self.is_package = os.path.exists(os.path.join(self.abspath, ".git")) + + self._package = None + self._project = None + + if check and not any([self.is_project, self.is_package]): + msg = f"Directory '{self.path}' is not a GIT working copy" + raise oscerr.NoWorkingCopy(msg) + + # TODO: decide if we need explicit 'git lfs pull' or not + # self._run_git(["lfs", "pull"]) + + def assert_is_project(self): + if not self.is_project: + msg = f"Directory '{self.path}' is not a GIT working copy of a project" + raise oscerr.NoWorkingCopy(msg) + + def assert_is_package(self): + if not self.is_package: + msg = f"Directory '{self.path}' is not a GIT working copy of a package" + raise oscerr.NoWorkingCopy(msg) + + def _run_git(self, args): + return subprocess.check_output(["git"] + args, encoding="utf-8", cwd=self.abspath).strip() + + @property + def apiurl(self): + # HACK: we're using the currently configured apiurl + return osc_conf.config["apiurl"] + + @property + def project(self): + if self._project is None: + # get project from the branch name + branch = self._run_git(["branch", "--show-current"]) + + # HACK: replace hard-coded mapping with metadata from git or the build service + if branch == "factory": + self._project = "openSUSE:Factory" + else: + raise RuntimeError(f"Couldn't map git branch '{branch}' to a project") + return self._project + + @project.setter + def project(self, value): + self._project = value + + @property + def package(self): + if self._package is None: + origin = self._run_git(["remote", "get-url", "origin"]) + self._package = Path(urllib.parse.urlsplit(origin).path).stem + return self._package + + @package.setter + def package(self, value): + self._package = value + + def _get_option(self, name): + try: + result = self._run_git(["config", "--local", "--get", f"osc.{name}"]) + except subprocess.CalledProcessError: + result = None + return result + + def _check_type(self, name, value, expected_type): + if not isinstance(value, expected_type): + raise TypeError(f"The option '{name}' should be {expected_type.__name__}, not {type(value).__name__}") + + def _set_option(self, name, value): + self._run_git(["config", "--local", f"osc.{name}", value]) + + def _unset_option(self, name): + try: + self._run_git(["config", "--local", "--unset", f"osc.{name}"]) + except subprocess.CalledProcessError: + pass + + def _get_dict_option(self, name): + result = self._get_option(name) + if result is None: + return None + result = json.loads(result) + self._check_type(name, result, dict) + return result + + def _set_dict_option(self, name, value): + if value is None: + self._unset_option(name) + return + self._check_type(name, value, dict) + value = json.dumps(value) + self._set_option(name, value) + + @property + def last_buildroot(self): + self.assert_is_package() + result = self._get_dict_option("last-buildroot") + if result is not None: + result = (result["repo"], result["arch"], result["vm_type"]) + return result + + @last_buildroot.setter + def last_buildroot(self, value): + self.assert_is_package() + if len(value) != 3: + raise ValueError("A tuple with exactly 3 items is expected: (repo, arch, vm_type)") + value = { + "repo": value[0], + "arch": value[1], + "vm_type": value[2], + } + self._set_dict_option("last-buildroot", value) + + @property + def scmurl(self): + return self._run_git(["remote", "get-url", "origin"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/store.py new/osc-1.3.0/osc/store.py --- old/osc-1.2.0/osc/store.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/store.py 2023-08-09 13:34:16.000000000 +0200 @@ -10,7 +10,7 @@ from . import oscerr from ._private import api - +from . import git_scm class Store: STORE_DIR = ".osc" @@ -309,3 +309,22 @@ else: root = self.read_xml_node("_meta", "project").getroot() return root + + +def get_store(path, check=True, print_warnings=False): + """ + Return a store object that wraps SCM in given `path`: + - Store for OBS SCM + - GitStore for Git SCM + """ + try: + store = Store(path, check) + except oscerr.NoWorkingCopy as ex: + try: + store = git_scm.GitStore(path, check) + if print_warnings: + git_scm.warn_experimental() + except oscerr.NoWorkingCopy as ex_git: + # raise the original exception, do not inform that we've tried git working copy + raise ex from None + return store diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/osc/util/git_version.py new/osc-1.3.0/osc/util/git_version.py --- old/osc-1.2.0/osc/util/git_version.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/osc/util/git_version.py 2023-08-09 13:34:16.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.2.0" + version = "1.3.0" 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.2.0/setup.cfg new/osc-1.3.0/setup.cfg --- old/osc-1.2.0/setup.cfg 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/setup.cfg 2023-08-09 13:34:16.000000000 +0200 @@ -35,6 +35,7 @@ osc osc._private osc.commands + osc.git_scm osc.output osc.util install_requires = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/tests/common.py new/osc-1.3.0/tests/common.py --- old/osc-1.2.0/tests/common.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/tests/common.py 2023-08-09 13:34:16.000000000 +0200 @@ -220,6 +220,7 @@ shutil.rmtree(self.tmpdir) except: pass + os.environ.pop("OSC_CONFIG", "") self.assertTrue(len(EXPECTED_REQUESTS) == 0) def _get_fixtures_dir(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/tests/test_conf.py new/osc-1.3.0/tests/test_conf.py --- old/osc-1.2.0/tests/test_conf.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/tests/test_conf.py 2023-08-09 13:34:16.000000000 +0200 @@ -1,9 +1,13 @@ import importlib +import os import unittest import osc.conf +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "conf_fixtures") + + class TestConf(unittest.TestCase): def setUp(self): # reset the global `config` in preparation for running the tests @@ -13,6 +17,9 @@ # reset the global `config` to avoid impacting tests from other classes importlib.reload(osc.conf) + def _get_fixtures_dir(self): + return FIXTURES_DIR + def test_bool_opts_defaults(self): config = osc.conf.config for opt in osc.conf._boolean_opts: @@ -28,7 +35,8 @@ self.assertIsInstance(config[opt], int, msg=f"option: '{opt}'") def test_bool_opts(self): - osc.conf.get_config() + oscrc = os.path.join(self._get_fixtures_dir(), "oscrc") + osc.conf.get_config(override_conffile=oscrc, override_no_keyring=True) config = osc.conf.config for opt in osc.conf._boolean_opts: if opt not in config: @@ -36,7 +44,8 @@ self.assertIsInstance(config[opt], bool, msg=f"option: '{opt}'") def test_int_opts(self): - osc.conf.get_config() + oscrc = os.path.join(self._get_fixtures_dir(), "oscrc") + osc.conf.get_config(override_conffile=oscrc, override_no_keyring=True) config = osc.conf.config for opt in osc.conf._integer_opts: if opt not in config: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/tests/test_git_scm_store.py new/osc-1.3.0/tests/test_git_scm_store.py --- old/osc-1.2.0/tests/test_git_scm_store.py 1970-01-01 01:00:00.000000000 +0100 +++ new/osc-1.3.0/tests/test_git_scm_store.py 2023-08-09 13:34:16.000000000 +0200 @@ -0,0 +1,49 @@ +import os +import shutil +import subprocess +import tempfile +import unittest + +from osc.git_scm.store import GitStore + + +class TestGitStore(unittest.TestCase): + def setUp(self): + self.tmpdir = tempfile.mkdtemp(prefix="osc_test") + os.chdir(self.tmpdir) + subprocess.check_output(["git", "init", "-b", "factory"]) + subprocess.check_output(["git", "remote", "add", "origin", "https://example.com/packages/my-package.git"]) + + def tearDown(self): + try: + shutil.rmtree(self.tmpdir) + except OSError: + pass + + def test_package(self): + store = GitStore(self.tmpdir) + self.assertEqual(store.package, "my-package") + + def test_project(self): + store = GitStore(self.tmpdir) + self.assertEqual(store.project, "openSUSE:Factory") + + def test_last_buildroot(self): + store = GitStore(self.tmpdir) + self.assertEqual(store.last_buildroot, None) + store.last_buildroot = ("repo", "arch", "vm_type") + + store = GitStore(self.tmpdir) + self.assertEqual(store.last_buildroot, ("repo", "arch", "vm_type")) + + def test_scmurl(self): + store = GitStore(self.tmpdir) + self.assertEqual(store.scmurl, "https://example.com/packages/my-package.git") + + +if not shutil.which("git"): + TestGitStore = unittest.skip("The 'git' executable is not available")(TestGitStore) + + +if __name__ == "__main__": + unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/tests/test_grabber.py new/osc-1.3.0/tests/test_grabber.py --- old/osc-1.2.0/tests/test_grabber.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/tests/test_grabber.py 2023-08-09 13:34:16.000000000 +0200 @@ -7,12 +7,16 @@ import osc.grabber as osc_grabber +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "conf_fixtures") + + class TestMirrorGroup(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix='osc_test') # reset the global `config` in preparation for running the tests importlib.reload(osc.conf) - osc.conf.get_config() + oscrc = os.path.join(self._get_fixtures_dir(), "oscrc") + osc.conf.get_config(override_conffile=oscrc, override_no_keyring=True) def tearDown(self): # reset the global `config` to avoid impacting tests from other classes @@ -22,6 +26,9 @@ except: pass + def _get_fixtures_dir(self): + return FIXTURES_DIR + def test_invalid_scheme(self): gr = osc_grabber.OscFileGrabber() mg = osc_grabber.OscMirrorGroup(gr, ["container://example.com"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-1.2.0/tests/test_output.py new/osc-1.3.0/tests/test_output.py --- old/osc-1.2.0/tests/test_output.py 2023-07-14 11:08:24.000000000 +0200 +++ new/osc-1.3.0/tests/test_output.py 2023-08-09 13:34:16.000000000 +0200 @@ -1,5 +1,10 @@ +import contextlib +import importlib +import io import unittest +import osc.conf +from osc._private import print_msg from osc.output import KeyValueTable @@ -67,5 +72,74 @@ self.assertEqual(str(t), expected) +class TestPrintMsg(unittest.TestCase): + def setUp(self): + # reset the global `config` in preparation for running the tests + importlib.reload(osc.conf) + + def tearDown(self): + # reset the global `config` to avoid impacting tests from other classes + importlib.reload(osc.conf) + + def test_debug(self): + osc.conf.config["debug"] = 0 + stdout = io.StringIO() + stderr = io.StringIO() + with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr): + print_msg("foo", "bar", print_to="debug") + self.assertEqual("", stdout.getvalue()) + self.assertEqual("", stderr.getvalue()) + + osc.conf.config["debug"] = 1 + stdout = io.StringIO() + stderr = io.StringIO() + with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr): + print_msg("foo", "bar", print_to="debug") + self.assertEqual("", stdout.getvalue()) + self.assertEqual("DEBUG: foo bar\n", stderr.getvalue()) + + def test_verbose(self): + osc.conf.config["verbose"] = 0 + stdout = io.StringIO() + stderr = io.StringIO() + with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr): + print_msg("foo", "bar", print_to="verbose") + self.assertEqual("", stdout.getvalue()) + self.assertEqual("", stderr.getvalue()) + + osc.conf.config["verbose"] = 1 + stdout = io.StringIO() + stderr = io.StringIO() + with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr): + print_msg("foo", "bar", print_to="verbose") + self.assertEqual("foo bar\n", stdout.getvalue()) + self.assertEqual("", stderr.getvalue()) + + osc.conf.config["verbose"] = 0 + osc.conf.config["debug"] = 1 + stdout = io.StringIO() + stderr = io.StringIO() + with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr): + print_msg("foo", "bar", print_to="verbose") + self.assertEqual("foo bar\n", stdout.getvalue()) + self.assertEqual("", stderr.getvalue()) + + def test_none(self): + stdout = io.StringIO() + stderr = io.StringIO() + with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr): + print_msg("foo", "bar", print_to=None) + self.assertEqual("", stdout.getvalue()) + self.assertEqual("", stderr.getvalue()) + + def test_stdout(self): + stdout = io.StringIO() + stderr = io.StringIO() + with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr): + print_msg("foo", "bar", print_to="stdout") + self.assertEqual("foo bar\n", stdout.getvalue()) + self.assertEqual("", stderr.getvalue()) + + if __name__ == "__main__": unittest.main() ++++++ osc.dsc ++++++ --- /var/tmp/diff_new_pack.nqBlCg/_old 2023-08-10 15:34:38.804443032 +0200 +++ /var/tmp/diff_new_pack.nqBlCg/_new 2023-08-10 15:34:38.812443081 +0200 @@ -1,6 +1,6 @@ Format: 1.0 Source: osc -Version: 1.2.0-0 +Version: 1.3.0-0 Binary: osc Maintainer: Adrian Schroeter <[email protected]> Architecture: any
