Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package osc for openSUSE:Factory checked in at 2022-04-27 21:41:20 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/osc (Old) and /work/SRC/openSUSE:Factory/.osc.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "osc" Wed Apr 27 21:41:20 2022 rev:160 rq:973067 version:0.177.0 Changes: -------- --- /work/SRC/openSUSE:Factory/osc/osc.changes 2022-03-02 18:20:48.356655317 +0100 +++ /work/SRC/openSUSE:Factory/.osc.new.1538/osc.changes 2022-04-27 21:41:29.989025615 +0200 @@ -1,0 +2,20 @@ +Tue Apr 26 07:53:48 UTC 2022 - Marco Strigl <[email protected]> + +- 0.177.0 + * switch to python3 in osc-wrapper and make python3 explicit + * allow formatting of the sccache uri + * show repository state and details + * a few minor fixes and improvements in credentials handling + * order credential managers by priority + * kernel keyring is now supported as credential manager + * support regex based name filtering in core.get_prj_results() + * revision parsing parseRevisionOption(): cleanup and make logic consistent + * use sr_ids[0] for superseding (fixes issues with superseding requests + containing many packages + * download logs and metadata in subdirs named by packages when osc getbinaries + is issued on project level or in multibuild case + +- spec file: + * recommed python-keyring-keyutils for new kernel keyring backend + +------------------------------------------------------------------- Old: ---- osc-0.176.0.tar.gz New: ---- osc-0.177.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ osc.spec ++++++ --- /var/tmp/diff_new_pack.9wgOgC/_old 2022-04-27 21:41:30.845026624 +0200 +++ /var/tmp/diff_new_pack.9wgOgC/_new 2022-04-27 21:41:30.853026633 +0200 @@ -27,7 +27,7 @@ %define use_python python %endif -%define version_unconverted 0.176.0 +%define version_unconverted 0.177.0 %define osc_plugin_dir %{_prefix}/lib/osc-plugins %define macros_file macros.osc %if ! %{defined _rpmmacrodir} @@ -35,7 +35,7 @@ %endif Name: osc -Version: 0.176.0 +Version: 0.177.0 Release: 0 Summary: Open Build Service Commander License: GPL-2.0-or-later @@ -98,8 +98,10 @@ %if 0%{?suse_version} > 1000 || 0%{?mandriva_version} || 0%{?mdkversion} || 0%{?fedora} >= 29 || 0%{?rhel} >= 8 %if %{with python3} Recommends: python3-keyring +Recommends: python3-keyring-keyutils %else Recommends: python-keyring +Recommends: python-keyring-keyutils %endif %endif %if 0%{?rhel} && 0%{?rhel} < 6 ++++++ PKGBUILD ++++++ --- /var/tmp/diff_new_pack.9wgOgC/_old 2022-04-27 21:41:30.881026666 +0200 +++ /var/tmp/diff_new_pack.9wgOgC/_new 2022-04-27 21:41:30.885026671 +0200 @@ -1,5 +1,5 @@ pkgname=osc -pkgver=0.176.0 +pkgver=0.177.0 pkgrel=0 pkgdesc="Open Build Service client" arch=('x86_64') ++++++ _service ++++++ --- /var/tmp/diff_new_pack.9wgOgC/_old 2022-04-27 21:41:30.901026689 +0200 +++ /var/tmp/diff_new_pack.9wgOgC/_new 2022-04-27 21:41:30.905026694 +0200 @@ -1,7 +1,7 @@ <services> <service name="tar_scm" mode="disabled"> - <param name="version">0.176.0</param> - <param name="revision">0.176.0</param> + <param name="version">0.177.0</param> + <param name="revision">0.177.0</param> <param name="url">https://github.com/openSUSE/osc.git</param> <param name="scm">git</param> </service> ++++++ debian.changelog ++++++ --- /var/tmp/diff_new_pack.9wgOgC/_old 2022-04-27 21:41:30.965026765 +0200 +++ /var/tmp/diff_new_pack.9wgOgC/_new 2022-04-27 21:41:30.973026774 +0200 @@ -1,4 +1,4 @@ -osc (0.176.0-0) unstable; urgency=low +osc (0.177.0-0) unstable; urgency=low - Update to 0.174.0: - fix password deletion via "osc config -d <apiurl> pass" - support changing the password store via "osc config <apiurl> ++++++ osc-0.176.0.tar.gz -> osc-0.177.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/NEWS new/osc-0.177.0/NEWS --- old/osc-0.176.0/NEWS 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/NEWS 2022-04-26 09:48:53.000000000 +0200 @@ -1,3 +1,17 @@ +0.177.0 + - switch to python3 in osc-wrapper and make python3 explicit + - allow formatting of the sccache uri + - show repository state and details + - a few minor fixes and improvements in credentials handling + - order credential managers by priority + - kernel keyring is now supported as credential manager + - support regex based name filtering in core.get_prj_results() + - revision parsing parseRevisionOption(): cleanup and make logic consistent + - use sr_ids[0] for superseding (fixes issues with superseding requests + containing many packages + - download logs and metadata in subdirs named by packages when osc getbinaries + is issued on project level or in multibuild case + 0.176.0 - add -F option to osc submitreq - add --verbose option to build command diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/osc/babysitter.py new/osc-0.177.0/osc/babysitter.py --- old/osc-0.176.0/osc/babysitter.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/osc/babysitter.py 2022-04-26 09:48:53.000000000 +0200 @@ -151,7 +151,7 @@ raise print(e, file=sys.stderr) except (oscerr.ConfigError, oscerr.NoConfigfile) as e: - print(e.msg, file=sys.stderr) + print(e, file=sys.stderr) except configparser.Error as e: print(e.message, file=sys.stderr) except oscerr.OscIOError as e: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/osc/build.py new/osc-0.177.0/osc/build.py --- old/osc-0.176.0/osc/build.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/osc/build.py 2022-04-26 09:48:53.000000000 +0200 @@ -144,7 +144,15 @@ self.release = root.find('release').text else: self.release = None - self.downloadurl = root.get('downloadurl') + if config['api_host_options'][apiurl]['downloadurl']: + self.enable_cpio = False + self.downloadurl = config['api_host_options'][apiurl]['downloadurl'] + "/repositories" + if config['http_debug']: + print("?????? setting dl_url to %s" % config['api_host_options'][apiurl]['downloadurl']) + else: + self.enable_cpio = True + self.downloadurl = root.get('downloadurl') + self.debuginfo = 0 if root.find('debuginfo') != None: try: @@ -181,7 +189,12 @@ # a hash providing the matching URL for specific repos for newer OBS instances if node.get('url'): url = node.get('url').replace('%', '%%') - self.urls[node.get('project')+"/"+node.get('repository')] = url + '/%(arch)s/%(filename)s' + if config['api_host_options'][apiurl]['downloadurl']: + # Add the path element to the download url override. + baseurl = config['api_host_options'][apiurl]['downloadurl'] + urlsplit(node.get('url'))[2] + else: + baseurl = node.get('url') + self.urls[node.get('project')+"/"+node.get('repository')] = baseurl + '/%(arch)s/%(filename)s' self.vminstall_list = [ dep.name for dep in self.deps if dep.vminstall ] self.preinstall_list = [ dep.name for dep in self.deps if dep.preinstall ] @@ -682,16 +695,6 @@ if opts.pkg_ccache: buildargs.append('--pkg-ccache=%s' % opts.pkg_ccache) xp.append('ccache') - if opts.sccache_uri or config['sccache_uri'] or opts.sccache or config['sccache']: - if opts.pkg_ccache or opts.ccache or config['ccache']: - raise oscerr.WrongArgs('Error: sccache and ccache can not be enabled at the same time') - if opts.sccache_uri: - buildargs.append('--sccache-uri=%s' % opts.sccache_uri) - elif config['sccache_uri']: - buildargs.append('--sccache-uri=%s' % config['sccache_uri']) - else: - buildargs.append('--sccache') - xp.append('sccache') if opts.linksources: buildargs.append('--linksources') if opts.baselibs: @@ -779,6 +782,21 @@ except: pass + # We configure sccache after pacname, so that in default cases we can have an sccache for each + # package to prevent cross-cache polutions. It helps to make the local-use case a bit nicer. + if opts.sccache_uri or config['sccache_uri'] or opts.sccache or config['sccache']: + if opts.pkg_ccache or opts.ccache or config['ccache']: + raise oscerr.WrongArgs('Error: sccache and ccache can not be enabled at the same time') + sccache_arg = "--sccache-uri=/var/tmp/osbuild-sccache-{pkgname}.tar" + if opts.sccache_uri: + sccache_arg = '--sccache-uri=%s' % opts.sccache_uri + elif config['sccache_uri']: + sccache_arg = '--sccache-uri=%s' % config['sccache_uri'] + # Format the package name. + sccache_arg = sccache_arg.format(pkgname=pacname) + buildargs.append(sccache_arg) + xp.append('sccache') + # define buildinfo & config local cache bi_file = None bc_file = None @@ -1057,7 +1075,7 @@ offline = opts.noinit or opts.offline, http_debug = config['http_debug'], modules = bi.modules, - enable_cpio = not opts.disable_cpio_bulk_download, + enable_cpio=not opts.disable_cpio_bulk_download and bi.enable_cpio, cookiejar=cookiejar, download_api_only=opts.download_api_only) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/osc/cmdln.py new/osc-0.177.0/osc/cmdln.py --- old/osc-0.176.0/osc/cmdln.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/osc/cmdln.py 2022-04-26 09:48:53.000000000 +0200 @@ -791,10 +791,10 @@ # practice dictates that command help strings begin with this, but # it isn't at all wanted for the command list. to_strip = "${cmd_name}:" - if doc and doc.startswith(to_strip): + if doc and doc.lstrip().startswith(to_strip): #log.debug("stripping %r from start of %s's help string", # to_strip, cmdname) - doc = doc[len(to_strip):].lstrip() + doc = (doc.lstrip())[len(to_strip):].lstrip() if not getattr(self._get_cmd_handler(cmdname), "hidden", None): linedata.append( (cmdstr, doc) ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/osc/commandline.py new/osc-0.177.0/osc/commandline.py --- old/osc-0.176.0/osc/commandline.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/osc/commandline.py 2022-04-26 09:48:53.000000000 +0200 @@ -1379,7 +1379,7 @@ if len(myreqs) > 0: for req in myreqs: change_request_state(apiurl, str(req), 'superseded', - 'superseded by %s' % result, result) + 'superseded by %s' % sr_ids[0], sr_ids[0]) sys.exit('Successfully finished') @@ -2150,6 +2150,8 @@ @cmdln.option('-d', '--diff', action='store_true', help='generate a diff') + @cmdln.option('-S', '--superseded-request', metavar='SUPERSEDED_REQUEST', + help='Create the diff relative to a given former request') @cmdln.option('-u', '--unified', action='store_true', help='output the diff in the unified diff format') @cmdln.option('--no-devel', action='store_true', @@ -2575,8 +2577,15 @@ diff = b'' try: # works since OBS 2.1 - diff = request_diff(apiurl, reqid) + diff = request_diff(apiurl, reqid, opts.superseded_request) except HTTPError as e: + if e.code == 404: + # Any referenced object does not exist, eg. the superseded request + root = ET.fromstring(e.read()) + summary = root.find('summary') + print(summary.text, file=sys.stderr) + raise oscerr.WrongOptions("Object does not exist") + # for OBS 2.0 and before sr_actions = r.get_actions('submit') if not r.get_actions('submit') and not r.get_actions('maintenance_incident') and not r.get_actions('maintenance_release'): @@ -3110,8 +3119,7 @@ """${cmd_name}: Release sources and binaries This command is used to transfer sources and binaries without rebuilding them. - It requires defined release targets set to trigger="manual". Please refer the - release management chapter in the OBS book for details. + It requires defined release targets set to trigger="manual". usage: osc release [ SOURCEPROJECT [ SOURCEPACKAGE ] ] @@ -7040,7 +7048,7 @@ project = args[0] package = args[1] - rev, rev_upper = parseRevisionOption(opts.revision) + rev, rev_upper = parseRevisionOption(opts.revision, allow_md5=False) if rev and not checkRevision(project, package, rev, apiurl, opts.meta): print('Revision \'%s\' does not exist' % rev, file=sys.stderr) sys.exit(1) @@ -7510,8 +7518,10 @@ if package is None: + package_specified = False package = meta_get_packagelist(apiurl, project, deleted=0) else: + package_specified = True if opts.multibuild_package: packages = [] for subpackage in opts.multibuild_package: @@ -7538,12 +7548,6 @@ for i in binaries: if binary != None and binary != i.name: continue - # skip metadata (unless explicitly specified as the `FILE` (== `binary`) argument) - if not binary and i.name.startswith("_"): - continue - # skip logs (unless explicitly specified as the `FILE` (== `binary`) argument) - if not binary and i.name.endswith(".log"): - continue # skip source rpms if not opts.sources and (i.name.endswith('src.rpm') or i.name.endswith('sdeb')): continue @@ -7552,7 +7556,18 @@ continue if i.name.find('-debugsource-') >= 0: continue - fname = '%s/%s' % (target_dir, i.name) + + if package_specified: + # if package is specified, download everything into the target dir + fname = '%s/%s' % (target_dir, i.name) + elif i.name.startswith("_") or i.name.endswith(".log"): + # download logs and metadata into subdirs + # to avoid overwriting them with files with indentical names + fname = '%s/%s/%s' % (target_dir, pac, i.name) + else: + # always download packages into the target dir + fname = '%s/%s' % (target_dir, i.name) + if os.path.exists(fname): st = os.stat(fname) if st.st_mtime == i.mtime and st.st_size == i.size: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/osc/conf.py new/osc-0.177.0/osc/conf.py --- old/osc-0.176.0/osc/conf.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/osc/conf.py 2022-04-26 09:48:53.000000000 +0200 @@ -221,7 +221,8 @@ 'status_mtime_heuristic', 'print_web_links', 'ccache', 'sccache', 'build-shell-after-fail'] integer_opts = ['build-jobs'] -api_host_options = ['user', 'pass', 'passx', 'aliases', 'http_headers', 'realname', 'email', 'sslcertck', 'cafile', 'capath', 'trusted_prj'] +api_host_options = ['user', 'pass', 'passx', 'aliases', 'http_headers', 'realname', 'email', 'sslcertck', 'cafile', 'capath', 'trusted_prj', + 'downloadurl'] new_conf_template = """ [general] @@ -304,9 +305,14 @@ # ccache = 1 # Enable sccache in build roots. Conflicts with ccache. +# Equivalent to sccache_uri = file:///var/tmp/osbuild-sccache-{pkgname}.tar # sccache = 1 -# Optional remote URI for sccache storage. +# Optional URI for sccache storage. Maybe a file://, redis:// or other URI supported +# by the configured sccache install. This uri MAY take {pkgname} as a special parameter +# which will be replaced with the name of the package to be built. +# sccache_uri = file:///var/tmp/osbuild-sccache-{pkgname}.tar.lzop +# sccache_uri = file:///var/tmp/osbuild-sccache-{pkgname}.tar # sccache_uri = redis://127.0.0.1:6379 # extra packages to install when building packages locally (osc build) @@ -759,7 +765,7 @@ # change password store creds_mgr = _get_credentials_manager(section, cp) user = _extract_user_compat(cp, section, creds_mgr) - val = creds_mgr.get_password(section, user) + val = creds_mgr.get_password(section, user, defer=False) run = False if val: @@ -879,6 +885,8 @@ def __getitem__(self, key, *args, **kwargs): value = super(self.__class__, self).__getitem__(key, *args, **kwargs) if key == 'pass' and callable(value): + print('Warning: use of a deprecated credentials manager API.', + file=sys.stderr) value = value() return value @@ -974,7 +982,7 @@ user = _extract_user_compat(cp, url, creds_mgr) if user is None: raise oscerr.ConfigMissingCredentialsError('No user found in section %s' % url, conffile, url) - password = creds_mgr.get_password(url, user) + password = creds_mgr.get_password(url, user, defer=True) if password is None: raise oscerr.ConfigMissingCredentialsError('No password found in section %s' % url, conffile, url) @@ -1019,6 +1027,16 @@ else: api_host_options[apiurl]['trusted_prj'] = [] + # ?????? This option is experimental and may be removed at any time in the future! + # This allows overriding the download url for an OBS instance to specify a closer mirror + # or proxy system, which can greatly improve download performance, latency and more. + # For example, this can use https://github.com/Firstyear/opensuse-proxy-cache in a local + # geo to improve performance. + if cp.has_option(url, 'downloadurl'): + api_host_options[apiurl]['downloadurl'] = cp.get(url, 'downloadurl') + else: + api_host_options[apiurl]['downloadurl'] = None + # add the auth data we collected to the config dict config['api_host_options'] = api_host_options config['apiurl_aliases'] = aliases @@ -1104,9 +1122,21 @@ if not credentials.has_keyring_support(): print('To use keyrings please install python%d-keyring.' % sys.version_info.major) creds_mgr_descriptors = credentials.get_credentials_manager_descriptors() + + rows = [] for i, creds_mgr_descr in enumerate(creds_mgr_descriptors, 1): - print('%d) %s (%s)' % (i, creds_mgr_descr.name(), creds_mgr_descr.description()))# - i = raw_input('Select credentials manager: ') + rows += [str(i), creds_mgr_descr.name(), creds_mgr_descr.description()] + + from .core import build_table + headline = ('NUM', 'NAME', 'DESCRIPTION') + table = build_table(len(headline), rows, headline) + print() + for row in table: + print(row) + + i = raw_input('Select credentials manager [default=1]: ') + if not i: + i = "1" if not i.isdigit(): sys.exit('Invalid selection') i = int(i) - 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/osc/core.py new/osc-0.177.0/osc/core.py --- old/osc-0.176.0/osc/core.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/osc/core.py 2022-04-26 09:48:53.000000000 +0200 @@ -5,7 +5,7 @@ from __future__ import print_function -__version__ = '0.176.0' +__version__ = '0.177.0' # __store_version__ is to be incremented when the format of the working copy # "store" changes in an incompatible way. Please add any needed migration @@ -4302,9 +4302,9 @@ r = root.get('id') except HTTPError as e: if e.hdrs.get('X-Opensuse-Errorcode') == "submit_request_rejected": - print("WARNING:") - print("WARNING: Project does not accept submit request, a NEW maintenance incident request will be created instead") - print("WARNING:") + print('WARNING: As the project is in maintenance, a maintenance incident request is') + print('WARNING: being created (instead of a regular submit request). If this is not your') + print('WARNING: intention please revoke it to avoid unnecessary work for all involved parties.') xpath = 'maintenance/maintains/@project = \'%s\' and attribute/@name = \'%s\'' % (dst_project, conf.config['maintenance_attribute']) res = search(apiurl, project_id=xpath) root = res['project_id'] @@ -4747,6 +4747,15 @@ target_filename = target_filename or filename + # create target directory if it doesn't exist + target_dir = os.path.dirname(target_filename) + if target_dir: + try: + os.makedirs(target_dir, 0o755) + except OSError as e: + if e.errno != errno.EEXIST: + raise + where = package or '_repository' u = makeurl(apiurl, ['build', prj, repo, arch, where, filename]) download(u, target_filename, progress_obj, target_mtime) @@ -4952,8 +4961,11 @@ return rdiff -def request_diff(apiurl, reqid): - u = makeurl(apiurl, ['request', reqid], query={'cmd': 'diff'} ) +def request_diff(apiurl, reqid, superseded_reqid=None): + query = {'cmd': 'diff'} + if superseded_reqid: + query['diff_to_superseded'] = superseded_reqid + u = makeurl(apiurl, ['request', reqid], query) f = http_POST(u) return f.read() @@ -5634,24 +5646,37 @@ r.insert(1, '------------ -------') else: - result_line_templ = '%(name)-25s %(project)-25s %(repository)-25s %(reponame)s' f = http_GET(makeurl(apiurl, ['distributions'])) root = ET.fromstring(b''.join(f)) + distlist = [] for node in root.findall('distribution'): - rmap = {} - for node2 in node.findall('name'): - rmap['name'] = node2.text - for node3 in node.findall('project'): - rmap['project'] = node3.text - for node4 in node.findall('repository'): - rmap['repository'] = node4.text - for node5 in node.findall('reponame'): - rmap['reponame'] = node5.text - r.append(result_line_templ % rmap) - - r.insert(0, 'distribution project repository reponame') - r.insert(1, '------------ ------- ---------- --------') + dmap = {} + for child in node: + if child.tag in ('name', 'project', 'repository', 'reponame'): + dmap[child.tag] = child.text + dmap['distribution'] = dmap.pop('name') + distlist.append(dmap) + + # pretty printing table + headers = ('distribution', 'project', 'repository', 'reponame') + maxlen = [len(h) for h in headers] + for d in distlist: + for i,field in enumerate(headers): + maxlen[i] = max(maxlen[i], len(d[field])) + + def format_row(dist, proj, repotype, reponame): + result_line_templ = '%-*s %-*s %-*s %-s' + return result_line_templ % ( + maxlen[0], dist, + maxlen[1], proj, + maxlen[2], repotype, + reponame + ) + r.append(format_row('distribution', 'project', 'repository', 'reponame')) + r.append(format_row('-'*maxlen[0], '-'*maxlen[1], '-'*maxlen[2], '-'*maxlen[2])) + for d in distlist: + r.append(format_row(d['distribution'], d['project'], d['repository'], d['reponame'])) return r @@ -5785,7 +5810,7 @@ rmap['repostate'] = node.get('code') rmap['pkg'] = rmap['package'] = rmap['pac'] = '' rmap['code'] = node.get('code') - rmap['details'] = '' + rmap['details'] = node.get('details') # the way we currently use this function, there should be # always a status element snodes = node.findall('status') @@ -5806,6 +5831,10 @@ details = statusnode.find('details') if details is not None: smap['details'] = details.text + if rmap['code'] == 'broken': + # real error just becomes visible in details/verbose + smap['code'] = rmap['code'] + smap['details'] = "repository: " + rmap['details'] yield smap, is_multi @@ -5927,6 +5956,9 @@ f = show_prj_results_meta(apiurl, prj) root = ET.fromstring(b''.join(f)) + if name_filter is not None: + name_filter = re.compile(name_filter) + pacs = [] # sequence of (repo,arch) tuples targets = [] @@ -5948,6 +5980,8 @@ state = "outdated" else: state = node.get('state') + if node.get('details'): + state += ' details: ' + node.get('details') tg = (node.get('repository'), node.get('arch'), state) targets.append(tg) for pacnode in node.findall('status'): @@ -5981,12 +6015,12 @@ if not name_filter: pacs_to_show.append(pkg) targets_to_show.append(repo) - elif name_filter in pkg: + elif name_filter.search(pkg) is not None: pacs_to_show.append(pkg) #filtering for Package Name elif name_filter: for pkg in pacs: - if name_filter in pkg: + if name_filter.search(pkg) is not None: pacs_to_show.append(pkg) #filter non building states @@ -6687,32 +6721,22 @@ return root.get('code') -def parseRevisionOption(string): +def parseRevisionOption(string, allow_md5=True): """ returns a tuple which contains the revisions """ + revisions = [None, None] if string: - if ':' in string: - splitted_rev = string.split(':') - try: - for i in splitted_rev: - int(i) - return splitted_rev - except ValueError: + parts = string.split(':') + for i, revision in enumerate(parts[0:2], 0): + if revision.isdigit() or (allow_md5 and revision.isalnum() and len(revision) == 32): + revisions[i] = revision + elif revision != '' and revision != 'latest': print('your revision \'%s\' will be ignored' % string, file=sys.stderr) return None, None - else: - if string.isdigit(): - return string, None - elif string.isalnum() and len(string) == 32: - # could be an md5sum - return string, None - else: - print('your revision \'%s\' will be ignored' % string, file=sys.stderr) - return None, None - else: - return None, None + + return tuple(revisions) def checkRevision(prj, pac, revision, apiurl=None, meta=False): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/osc/credentials.py new/osc-0.177.0/osc/credentials.py --- old/osc-0.176.0/osc/credentials.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/osc/credentials.py 2022-04-26 09:48:53.000000000 +0200 @@ -2,18 +2,65 @@ import bz2 import base64 import getpass +import sys + try: from urllib.parse import urlsplit except ImportError: from urlparse import urlsplit + try: import keyring except ImportError: keyring = None +except BaseException as e: + # catch and report any exceptions raised in the 'keyring' module + msg = "Warning: Unable to load the 'keyring' module due to an internal error:" + print(msg, e, file=sys.stderr) + keyring = None + try: import gnomekeyring except ImportError: gnomekeyring = None +except BaseException as e: + # catch and report any exceptions raised in the 'gnomekeyring' module + msg = "Warning: Unable to load the 'gnomekeyring' module due to an internal error:" + print(msg, e, file=sys.stderr) + gnomekeyring = None + +from . import conf +from . import oscerr + + +class _LazyPassword(object): + def __init__(self, pwfunc): + self._pwfunc = pwfunc + self._password = None + + def __str__(self): + if self._password is None: + password = self._pwfunc() + if callable(password): + print('Warning: use of a deprecated credentials manager API.', + file=sys.stderr) + password = password() + if password is None: + raise oscerr.OscIOError(None, 'Unable to retrieve password') + self._password = password + return self._password + + def __len__(self): + return len(str(self)) + + def __add__(self, other): + return str(self) + other + + def __radd__(self, other): + return other + str(self) + + def __getattr__(self, name): + return getattr(str(self), name) class AbstractCredentialsManagerDescriptor(object): @@ -23,11 +70,16 @@ def description(self): raise NotImplementedError() + def priority(self): + # priority determines order in the credentials managers list + # higher number means higher priority + raise NotImplementedError() + def create(self, cp): raise NotImplementedError() def __lt__(self, other): - return self.name() < other.name() + return (-self.priority(), self.name()) < (-other.priority(), other.name()) class AbstractCredentialsManager(object): @@ -42,14 +94,15 @@ def create(cls, cp, options): return cls(cp, options) - def get_password(self, url, user, defer=True): - # If defer is True a callable can be returned - # and the password is retrieved if the callable - # is called. Implementations are free to ignore - # defer parameter and can directly return the password. - # If defer is False the password is directly returned. + def _get_password(self, url, user): raise NotImplementedError() + def get_password(self, url, user, defer=True): + if defer: + return _LazyPassword(lambda: self._get_password(url, user)) + else: + return self._get_password(url, user) + def set_password(self, url, user, password): raise NotImplementedError() @@ -81,10 +134,13 @@ class PlaintextConfigFileDescriptor(AbstractCredentialsManagerDescriptor): def name(self): - return 'Config file credentials manager' + return 'Config' def description(self): - return 'Store the credentials in the config file (plain text)' + return 'Store the password in plain text in the osc config file [insecure, persistent]' + + def priority(self): + return 1 def create(self, cp): return PlaintextConfigFileCredentialsManager(cp, None) @@ -116,10 +172,13 @@ class ObfuscatedConfigFileDescriptor(AbstractCredentialsManagerDescriptor): def name(self): - return 'Obfuscated Config file credentials manager' + return 'Obfuscated config' def description(self): - return 'Store the credentials in the config file (obfuscated)' + return 'Store the password in obfuscated form in the osc config file [insecure, persistent]' + + def priority(self): + return 2 def create(self, cp): return ObfuscatedConfigFileCredentialsManager(cp, None) @@ -134,10 +193,10 @@ if options is not None: raise RuntimeError('options must be None') - def get_password(self, url, user, defer=True): - if defer: - return self - return self() + def _get_password(self, url, user): + if self._password is None: + self._password = getpass.getpass('Password: ') + return self._password def set_password(self, url, user, password): self._password = password @@ -146,18 +205,16 @@ def delete_password(self, url, user): self._password = None - def __call__(self): - if self._password is None: - self._password = getpass.getpass('Password: ') - return self._password - class TransientDescriptor(AbstractCredentialsManagerDescriptor): def name(self): - return 'Transient password store' + return 'Transient' def description(self): - return 'Do not store the password and always ask for the password' + return 'Do not store the password and always ask for it [secure, in-memory]' + + def priority(self): + return 3 def create(self, cp): return TransientCredentialsManager(cp, None) @@ -170,7 +227,11 @@ self._backend_cls_name = options def _load_backend(self): - keyring_backend = keyring.core.load_keyring(self._backend_cls_name) + try: + keyring_backend = keyring.core.load_keyring(self._backend_cls_name) + except ModuleNotFoundError: + msg = "Invalid credentials_mgr_class: {}".format(self._backend_cls_name) + raise oscerr.ConfigError(msg, conf.config['conffile']) keyring.set_keyring(keyring_backend) @classmethod @@ -179,7 +240,7 @@ return None return super(cls, cls).create(cp, options) - def get_password(self, url, user, defer=True): + def _get_password(self, url, user): self._load_backend() return keyring.get_password(urlsplit(url)[1], user) @@ -195,18 +256,29 @@ class KeyringCredentialsDescriptor(AbstractCredentialsManagerDescriptor): - def __init__(self, keyring_backend): + def __init__(self, keyring_backend, name=None, description=None, priority=None): self._keyring_backend = keyring_backend + self._name = name + self._description = description + self._priority = priority def name(self): + if self._name: + return self._name if hasattr(self._keyring_backend, 'name'): return self._keyring_backend.name - else: - return self._keyring_backend.__class__.__name__ + return self._keyring_backend.__class__.__name__ def description(self): + if self._description: + return self._description return 'Backend provided by python-keyring' + def priority(self): + if self._priority is not None: + return self._priority + return 0 + def create(self, cp): qualified_backend_name = qualified_name(self._keyring_backend) return KeyringCredentialsManager(cp, qualified_backend_name) @@ -219,7 +291,7 @@ return None return super(cls, cls).create(cp, options) - def get_password(self, url, user, defer=True): + def _get_password(self, url, user): gk_data = self._keyring_data(url, user) if gk_data is None: return None @@ -276,24 +348,55 @@ return 'Deprecated GNOME Keyring Manager. If you use \ this we will send you a Dial-In modem' + def priority(self): + return 0 + def create(self, cp): return GnomeKeyringCredentialsManager(cp, None) +# we're supporting only selected python-keyring backends in osc +SUPPORTED_KEYRING_BACKENDS = { + "keyutils.osc.OscKernelKeyringBackend": { + "name": "Kernel keyring", + "description": "Store password in user session keyring in kernel keyring [secure, in-memory, per-session]", + "priority": 10, + }, + "keyring.backends.SecretService.Keyring": { + "name": "Secret Service", + "description": "Store password in Secret Service (GNOME Keyring backend) [secure, persistent]", + "priority": 9, + }, + "keyring.backends.kwallet.DBusKeyring": { + "name": "KWallet", + "description": "Store password in KWallet [secure, persistent]", + "priority": 8, + }, +} + + def get_credentials_manager_descriptors(): - if has_keyring_support(): - backend_list = keyring.backend.get_all_keyring() - else: - backend_list = [] descriptors = [] - for backend in backend_list: - descriptors.append(KeyringCredentialsDescriptor(backend)) - descriptors.sort() + + if has_keyring_support(): + for backend in keyring.backend.get_all_keyring(): + qualified_backend_name = qualified_name(backend) + data = SUPPORTED_KEYRING_BACKENDS.get(qualified_backend_name, None) + if not data: + continue + descriptor = KeyringCredentialsDescriptor( + backend, + data["name"], + data["description"], + data["priority"] + ) + descriptors.append(descriptor) if gnomekeyring: descriptors.append(GnomeKeyringCredentialsDescriptor()) descriptors.append(PlaintextConfigFileDescriptor()) descriptors.append(ObfuscatedConfigFileDescriptor()) descriptors.append(TransientDescriptor()) + descriptors.sort() return descriptors diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/osc/oscerr.py new/osc-0.177.0/osc/oscerr.py --- old/osc-0.176.0/osc/oscerr.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/osc/oscerr.py 2022-04-26 09:48:53.000000000 +0200 @@ -22,6 +22,9 @@ self.msg = msg self.file = fname + def __str__(self): + return "Error in config file {}\n {}".format(self.file, self.msg) + class ConfigMissingApiurl(ConfigError): """Exception raised when a apiurl does not exist in the config file""" def __init__(self, msg, fname, url): @@ -46,6 +49,9 @@ self.file = fname self.msg = msg + def __str__(self): + return "Config file cannot be found: {}\n {}".format(self.file, self.msg) + class ExtRuntimeError(OscBaseError): """Exception raised when there is a runtime error of an external tool""" def __init__(self, msg, fname): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/osc-wrapper.py new/osc-0.177.0/osc-wrapper.py --- old/osc-0.176.0/osc-wrapper.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/osc-wrapper.py 2022-04-26 09:48:53.000000000 +0200 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # this wrapper exists so it can be put into /usr/bin, but still allows the # python module to be called within the source directory during development diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.176.0/setup.py new/osc-0.177.0/setup.py --- old/osc-0.176.0/setup.py 2022-02-28 16:44:11.000000000 +0100 +++ new/osc-0.177.0/setup.py 2022-04-26 09:48:53.000000000 +0200 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 from distutils.core import setup import distutils.core ++++++ osc.dsc ++++++ --- /var/tmp/diff_new_pack.9wgOgC/_old 2022-04-27 21:41:31.285027142 +0200 +++ /var/tmp/diff_new_pack.9wgOgC/_new 2022-04-27 21:41:31.289027147 +0200 @@ -1,6 +1,6 @@ Format: 1.0 Source: osc -Version: 0.176.0-0 +Version: 0.177.0-0 Binary: osc Maintainer: Adrian Schroeter <[email protected]> Architecture: any
