Hello community,

here is the log from the commit of package openSUSE-release-tools for 
openSUSE:Factory checked in at 2018-01-22 16:21:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/openSUSE-release-tools (Old)
 and      /work/SRC/openSUSE:Factory/.openSUSE-release-tools.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "openSUSE-release-tools"

Mon Jan 22 16:21:04 2018 rev:41 rq:568177 version:20180122.b5fe7e7

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/openSUSE-release-tools/openSUSE-release-tools.changes
    2018-01-13 21:47:14.563207026 +0100
+++ 
/work/SRC/openSUSE:Factory/.openSUSE-release-tools.new/openSUSE-release-tools.changes
       2018-01-22 16:22:23.415068383 +0100
@@ -1,0 +2,84 @@
+Mon Jan 22 13:40:29 UTC 2018 - [email protected]
+
+- Update to version 20180122.b5fe7e7:
+  * dist/ci/obs-deploy: only bother making request if diff.
+  * dist/ci/obs-deploy: limit `request list` by user to avoid source requests.
+  * docs/pkglistgen: update in the wake of wrapper script rewrite.
+
+-------------------------------------------------------------------
+Fri Jan 19 16:54:38 UTC 2018 - [email protected]
+
+- Update to version 20180119.a7c4bcf:
+  * repo_checker: package_comments(): provide option to post on target package.
+  * repo_checker: package_comments(): switch to devel_project_fallback().
+  * repo_checker: package_comments(): support mutiple target projects.
+  * ReviewBot: comment_write(): provide bot_name_suffix parameter.
+  * repo_checker: package_comments(): include link to target package.
+
+-------------------------------------------------------------------
+Fri Jan 19 16:41:59 UTC 2018 - [email protected]
+
+- Update to version 20180119.2d6b957:
+  * check_source: add_role: utilize devel project fallback.
+
+-------------------------------------------------------------------
+Fri Jan 19 12:48:12 UTC 2018 - [email protected]
+
+- Update to version 20180119.38fd846:
+  * osclib/request_splitter: replace devel_project_get() with 
core.devel_project_fallback().
+  * osclib/stagingapi: replace get_devel_project() with 
core.devel_project_get().
+  * update_crawler: replace get_devel_project() with 
osclib.core.devel_project_get().
+  * fcc_submitter: replace get_devel_project() with 
osclib.core.devel_project_get().
+  * ReviewBot: replace get_devel_project() with 
osclib.core.devel_project_get().
+  * osclib/core: devel_project_fallback(): do not fallback if openSUSE:Factory.
+  * osclib/core: provide devel_project_fallback() based on request_splitter.
+  * osclib/core: provide devel_project_get() adapted from ReviewBot.
+  * news/web: allow dot in version.
+  * cleanup_rings: Fix for 000product
+  * cleanup_rings: first attempt to fix for multibuild
+
+-------------------------------------------------------------------
+Wed Jan 17 22:35:59 UTC 2018 - [email protected]
+
+- Update to version 20180117.b6d80ea:
+  * fcc_submitter: fix a regression from previous commit
+
+-------------------------------------------------------------------
+Wed Jan 17 22:30:23 UTC 2018 - [email protected]
+
+- Update to version 20180117.e41b40e:
+  * requestfinder: fix comment output
+
+-------------------------------------------------------------------
+Wed Jan 17 22:24:12 UTC 2018 - [email protected]
+
+- Update to version 20180117.b335868:
+  * pkglistgen: delete kiwis by scope
+
+-------------------------------------------------------------------
+Wed Jan 17 22:09:09 UTC 2018 - [email protected]
+
+- Update to version 20180115.2da7981:
+  * osclib/request_splitter: rework quick strategy to handle Leap 15.0 
workflow.
+  * obs_clone: reword comment in project_workaround() since not temporary.
+  * obs_clone: clone rings.
+  * obs_clone: handle project links.
+  * osclib/request_splitter: allow special packages to be empty.
+  * fcc_submitter: ignore multibuild flavor package when creating frozenlinks
+  * fcc_submitter: move FCC project to :Staging:Factory:Candidates
+  * new requestfinder tool
+
+-------------------------------------------------------------------
+Fri Jan 12 16:04:06 UTC 2018 - [email protected]
+
+- Update to version 20180112.41846c5:
+  * systemd/metrics: change service to be non-incremental and weekly.
+  * metrics: provide --wipe-cache option.
+
+-------------------------------------------------------------------
+Thu Jan 11 14:19:43 UTC 2018 - [email protected]
+
+- Update to version 20180111.54880d3:
+  * dist/spec: license: s/and/AND/ to follow standard.
+
+-------------------------------------------------------------------

Old:
----
  openSUSE-release-tools-20180110.9ab0211.obscpio

New:
----
  openSUSE-release-tools-20180122.b5fe7e7.obscpio

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

Other differences:
------------------
++++++ openSUSE-release-tools.spec ++++++
--- /var/tmp/diff_new_pack.uLxMl8/_old  2018-01-22 16:22:24.031039572 +0100
+++ /var/tmp/diff_new_pack.uLxMl8/_new  2018-01-22 16:22:24.031039572 +0100
@@ -20,10 +20,10 @@
 %define source_dir osc-plugin-factory
 %define announcer_filename factory-package-news
 Name:           openSUSE-release-tools
-Version:        20180110.9ab0211
+Version:        20180122.b5fe7e7
 Release:        0
 Summary:        Tools to aid in staging and release work for openSUSE/SUSE
-License:        GPL-2.0+ and MIT
+License:        GPL-2.0+ AND MIT
 Group:          Development/Tools/Other
 Url:            https://github.com/openSUSE/osc-plugin-factory
 Source:         %{name}-%{version}.tar.xz

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.uLxMl8/_old  2018-01-22 16:22:24.079037328 +0100
+++ /var/tmp/diff_new_pack.uLxMl8/_new  2018-01-22 16:22:24.079037328 +0100
@@ -1,6 +1,6 @@
 <servicedata>
   <service name="tar_scm">
     <param 
name="url">https://github.com/openSUSE/osc-plugin-factory.git</param>
-    <param 
name="changesrevision">ecda2f03031e7bfc29f7d628c646f1940878c774</param>
+    <param 
name="changesrevision">b5fe7e74e890e1f5a30262865f1d63c6eae0742f</param>
   </service>
 </servicedata>

++++++ openSUSE-release-tools-20180110.9ab0211.obscpio -> 
openSUSE-release-tools-20180122.b5fe7e7.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openSUSE-release-tools-20180110.9ab0211/ReviewBot.py 
new/openSUSE-release-tools-20180122.b5fe7e7/ReviewBot.py
--- old/openSUSE-release-tools-20180110.9ab0211/ReviewBot.py    2018-01-11 
05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/ReviewBot.py    2018-01-22 
14:40:19.000000000 +0100
@@ -361,17 +361,6 @@
 
         return (None, None)
 
-    def get_devel_project(self, project, package):
-        try:
-            m = osc.core.show_package_meta(self.apiurl, project, package)
-            node = ET.fromstring(''.join(m)).find('devel')
-            if node is not None:
-                return node.get('project'), node.get('package', None)
-        except urllib2.HTTPError as e:
-            if e.code != 404:
-                raise e
-        return None, None
-
     def can_accept_review(self, request_id):
         """return True if there is a new review for the specified reviewer"""
         states = set()
@@ -436,7 +425,7 @@
 
     def comment_write(self, state='done', result=None, project=None, 
package=None,
                       request=None, message=None, identical=False, 
only_replace=False,
-                      info_extra=None, info_extra_identical=True):
+                      info_extra=None, info_extra_identical=True, 
bot_name_suffix=None):
         """Write comment if not similar to previous comment and replace old 
one.
 
         The state, result, and info_extra (dict) are combined to create the 
info
@@ -483,18 +472,22 @@
                 return
             message = '\n\n'.join(self.comment_handler.lines)
 
+        bot_name = self.bot_name
+        if bot_name_suffix:
+            bot_name = '::'.join([bot_name, bot_name_suffix])
+
         info = {'state': state, 'result': result}
         if info_extra and info_extra_identical:
             info.update(info_extra)
 
         comments = self.comment_api.get_comments(**kwargs)
-        comment, _ = self.comment_api.comment_find(comments, self.bot_name, 
info)
+        comment, _ = self.comment_api.comment_find(comments, bot_name, info)
 
         if info_extra and not info_extra_identical:
             # Add info_extra once comment has already been matched.
             info.update(info_extra)
 
-        message = self.comment_api.add_marker(message, self.bot_name, info)
+        message = self.comment_api.add_marker(message, bot_name, info)
         message = self.comment_api.truncate(message.strip())
 
         if (comment is not None and
@@ -510,7 +503,7 @@
 
         if comment is None:
             self.logger.debug('broadening search to include any state on 
{}'.format(debug_key))
-            comment, _ = self.comment_api.comment_find(comments, self.bot_name)
+            comment, _ = self.comment_api.comment_find(comments, bot_name)
         if comment is not None:
             self.logger.debug('removing previous comment on 
{}'.format(debug_key))
             if not self.dryrun:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/check_source.py 
new/openSUSE-release-tools-20180122.b5fe7e7/check_source.py
--- old/openSUSE-release-tools-20180110.9ab0211/check_source.py 2018-01-11 
05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/check_source.py 2018-01-22 
14:40:19.000000000 +0100
@@ -12,6 +12,8 @@
 
 import osc.conf
 import osc.core
+from osclib.core import devel_project_get
+from osclib.core import devel_project_fallback
 import urllib2
 import ReviewBot
 from check_maintenance_incidents import MaintenanceChecker
@@ -46,7 +48,7 @@
 
         if not self.ignore_devel:
             self.logger.info('checking if target package exists and has devel 
project')
-            devel_project, devel_package = 
self.get_devel_project(target_project, target_package)
+            devel_project, devel_package = devel_project_get(self.apiurl, 
target_project, target_package)
             if devel_project:
                 if (source_project != devel_project or source_package != 
devel_package) and \
                    not(source_project == target_project and source_package == 
target_package):
@@ -212,7 +214,8 @@
         message = 'Roles to packages are granted in the devel project, not in 
%s.' % action.tgt_project
 
         if action.tgt_package is not None:
-            message += ' Please send this request to %s/%s.' % 
self.get_devel_project(action.tgt_project, action.tgt_package)
+            project, package = devel_project_fallback(self.apiurl, 
action.tgt_project, action.tgt_package)
+            message += ' Send this request to {}/{}.'.format(project, package)
 
         self.review_messages['declined'] = message
         return False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/dist/ci/obs-deploy 
new/openSUSE-release-tools-20180122.b5fe7e7/dist/ci/obs-deploy
--- old/openSUSE-release-tools-20180110.9ab0211/dist/ci/obs-deploy      
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/dist/ci/obs-deploy      
2018-01-22 14:40:19.000000000 +0100
@@ -21,8 +21,21 @@
 OBS_TARGET_PROJECT="$(osc info | grep -oP "Link info:.*?project \K[^\s,]+")"
 OBS_TARGET_PACKAGE="$(osc info | grep -oP "Link info:.*?, package \K[^\s,]+")"
 echo "checking for existing requests to 
$OBS_TARGET_PROJECT/$OBS_TARGET_PACKAGE..."
-if osc request list "$OBS_TARGET_PROJECT" "$OBS_TARGET_PACKAGE" | grep 'No 
results for package' ; then
+# Limit by user in an attempt to avoid requests sourced from target project.
+# Unfortunately the command line provides no mechanism to do so and a full API
+# query is rather ungainly compared to this workaround.
+if osc request list -U "$OBS_USER" "$OBS_TARGET_PROJECT" "$OBS_TARGET_PACKAGE" 
|
+    grep 'No results for package' ; then
   osc service wait
-  osc sr --diff | cat
-  osc sr --yes -m "automatic update"
+  # Only bother making a request if there is a diff (always shows 3 lines).
+  if [ "$(osc sr --diff | tee temp.diff | wc -l)" -gt 3 ] ; then
+    echo "-> creating request"
+    cat temp.diff
+    rm temp.diff
+    osc sr --yes -m "automatic update"
+  else
+    echo "-> no difference (likely cron job)"
+  fi
+else
+  echo "-> existing request"
 fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/dist/package/openSUSE-release-tools.spec
 
new/openSUSE-release-tools-20180122.b5fe7e7/dist/package/openSUSE-release-tools.spec
--- 
old/openSUSE-release-tools-20180110.9ab0211/dist/package/openSUSE-release-tools.spec
        2018-01-11 05:13:58.000000000 +0100
+++ 
new/openSUSE-release-tools-20180122.b5fe7e7/dist/package/openSUSE-release-tools.spec
        2018-01-22 14:40:19.000000000 +0100
@@ -23,7 +23,7 @@
 Version:        0
 Release:        0
 Summary:        Tools to aid in staging and release work for openSUSE/SUSE
-License:        GPL-2.0+ and MIT
+License:        GPL-2.0+ AND MIT
 Group:          Development/Tools/Other
 Url:            https://github.com/openSUSE/osc-plugin-factory
 Source:         %{name}-%{version}.tar.xz
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/docs/pkglistgen.md 
new/openSUSE-release-tools-20180122.b5fe7e7/docs/pkglistgen.md
--- old/openSUSE-release-tools-20180110.9ab0211/docs/pkglistgen.md      
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/docs/pkglistgen.md      
2018-01-22 14:40:19.000000000 +0100
@@ -2,12 +2,16 @@
 
 ## Sources
 
-The package list generator consists of two scripts:
+The package list generator is contained within `pkglistgen.py` and consists of
+two phases.
 
-- pkglistgen.py - python script using libsolv to do the hard work
-- pkglistgen.sh - shell script for the dirty work. It calls the product
-  converter locally and then splits the output. See existing scripts in script/
-  for examples how to call it for Rings, Stagings etc.
+- `update`: updates local package cache
+- `solve`: uses libsolv to determine list of packages
+
+The two phases can be run together via the `update_and_solve` subcommand which
+also performs necessary initialization and post-processing. By default it will
+run against all _scopes_, but a specific scope such as `rings` or `staging` can
+be specified.
 
 ## Input and Output
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/factory-package-news/factory-package-news-web.py
 
new/openSUSE-release-tools-20180122.b5fe7e7/factory-package-news/factory-package-news-web.py
--- 
old/openSUSE-release-tools-20180110.9ab0211/factory-package-news/factory-package-news-web.py
        2018-01-11 05:13:58.000000000 +0100
+++ 
new/openSUSE-release-tools-20180122.b5fe7e7/factory-package-news/factory-package-news-web.py
        2018-01-22 14:40:19.000000000 +0100
@@ -28,7 +28,7 @@
 import sys
 from urlparse import urlparse
 
-digits_re = re.compile('^[0-9]+$')
+digits_re = re.compile('^[0-9.]+$')
 
 BASE_DIR = '/var/lib'
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/fcc_submitter.py 
new/openSUSE-release-tools-20180122.b5fe7e7/fcc_submitter.py
--- old/openSUSE-release-tools-20180110.9ab0211/fcc_submitter.py        
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/fcc_submitter.py        
2018-01-22 14:40:19.000000000 +0100
@@ -30,13 +30,14 @@
 
 import osc.conf
 import osc.core
+from osclib.core import devel_project_get
 
 from osc import oscerr
 from osclib.memoize import memoize
 
 OPENSUSE = 'openSUSE:Leap:15.0'
 OPENSUSE_PREVERSION = 'openSUSE:Leap:42.3'
-FCC = 'openSUSE:42:Factory-Candidates-Check'
+FCC = '{}:Staging:FactoryCandidates'.format(OPENSUSE)
 
 makeurl = osc.core.makeurl
 http_GET = osc.core.http_GET
@@ -73,6 +74,11 @@
         if package.startswith('_') or package.startswith('Test-DVD') or 
package.startswith('000'):
             return None
 
+        # filter out multibuild package
+        for originpackage in si.findall('originpackage'):
+            if ':' in package and package.split(':')[0] == originpackage.text:
+                return package
+
         for linked in si.findall('linked'):
             if linked.get('project') == self.factory:
                 if linked.get('package') in pkglist or linked.get('package') 
in pkglist_prever:
@@ -187,7 +193,7 @@
         failed_multibuild_pacs = []
         pacs = []
         for node in root.findall('result'):
-            if node.get('repository') == 'pure_42' and node.get('arch') == 
'x86_64':
+            if node.get('repository') == 'standard' and node.get('arch') == 
'x86_64':
                 for pacnode in node.findall('status'):
                     if ':' in pacnode.get('package'):
                         mainpac = pacnode.get('package').split(':')[0]
@@ -206,7 +212,7 @@
                     if pacnode.get('code') == 'succeeded':
                         pacs.append(pacnode.get('package'))
             else:
-                logging.error("Can not find pure_42/x86_64 results")
+                logging.error("Can not find standard/x86_64 results")
 
         return pacs
 
@@ -218,14 +224,6 @@
             return True
         return False
 
-    def get_devel_project(self, package):
-        m = osc.core.show_package_meta(self.apiurl, self.factory, package)
-        node = ET.fromstring(''.join(m)).find('devel')
-        if node is None:
-            return None, None
-        else:
-            return node.get('project'), node.get('package', None)
-
     def create_submitrequest(self, package):
         """Create a submit request using the osc.commandline.Osc class."""
         src_project = self.factory # submit from Factory only
@@ -250,7 +248,7 @@
         root = ET.fromstring(http_GET(url).read())
         data = {}
         linkinfo = root.find('linkinfo')
-        if linkinfo:
+        if linkinfo is not None:
             data['linkinfo'] = linkinfo.attrib['package']
         else:
             data['linkinfo'] = None
@@ -371,7 +369,7 @@
                 else:
                     logging.info("%d - Preparing submit %s to %s"%(i, package, 
self.to_prj))
                     # get devel project
-                    devel_prj, devel_pkg = self.get_devel_project(package)
+                    devel_prj, devel_pkg = devel_project_get(self.apiurl, 
self.factory, package)
                     # check devel project does not in the skip list
                     if devel_prj in self.skip_devel_project_list:
                         # check the except packages list
@@ -435,7 +433,7 @@
             uc.crawl()
 
 if __name__ == '__main__':
-    description = 'Create SR from openSUSE:42:Factory-Candidates-Check to '\
+    description = 'Create SR from FactoryCandidates to '\
                   'openSUSE Leap project for new build succeded packages.'
     parser = argparse.ArgumentParser(description=description)
     parser.add_argument('-A', '--apiurl', metavar='URL', help='API URL')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openSUSE-release-tools-20180110.9ab0211/leaper.py 
new/openSUSE-release-tools-20180122.b5fe7e7/leaper.py
--- old/openSUSE-release-tools-20180110.9ab0211/leaper.py       2018-01-11 
05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/leaper.py       2018-01-22 
14:40:19.000000000 +0100
@@ -35,6 +35,7 @@
 
 import osc.conf
 import osc.core
+from osclib.core import devel_project_get
 import urllib2
 import yaml
 import ReviewBot
@@ -181,7 +182,7 @@
                 else:
                     self.logger.info('different sources in 
{}'.format(self.rdiff_link(src_project, src_package, src_rev, prj, package)))
 
-            devel_project, devel_package = 
self.get_devel_project('openSUSE.org:openSUSE:Factory', package)
+            devel_project, devel_package = devel_project_get(self.apiurl, 
'openSUSE.org:openSUSE:Factory', package)
             if devel_project is not None:
                 # specifying devel package is optional
                 if devel_package is None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openSUSE-release-tools-20180110.9ab0211/metrics.py 
new/openSUSE-release-tools-20180122.b5fe7e7/metrics.py
--- old/openSUSE-release-tools-20180110.9ab0211/metrics.py      2018-01-11 
05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/metrics.py      2018-01-22 
14:40:19.000000000 +0100
@@ -349,6 +349,8 @@
 
     # Use separate cache since it is persistent.
     Cache.CACHE_DIR = os.path.expanduser('~/.cache/osc-plugin-factory-metrics')
+    if args.wipe_cache:
+        Cache.delete_all()
     Cache.PATTERNS['/search/request'] = sys.maxint
     Cache.init()
 
@@ -378,6 +380,7 @@
     parser.add_argument('--port', default=8086, help='InfluxDB post')
     parser.add_argument('--user', default='root', help='InfluxDB user')
     parser.add_argument('--password', default='root', help='InfluxDB password')
+    parser.add_argument('--wipe-cache', action='store_true', help='wipe GET 
request cache before executing')
     args = parser.parse_args()
 
     sys.exit(main(args))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openSUSE-release-tools-20180110.9ab0211/obs_clone.py 
new/openSUSE-release-tools-20180122.b5fe7e7/obs_clone.py
--- old/openSUSE-release-tools-20180110.9ab0211/obs_clone.py    2018-01-11 
05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/obs_clone.py    2018-01-22 
14:40:19.000000000 +0100
@@ -68,7 +68,11 @@
         path = ['group', group.get('groupid')]
         entity_clone(apiurl_source, apiurl_target, path, clone=group_clone)
 
-def project_repositories_remove(project):
+def project_references_remove(project):
+    # Remove links that reference other projects.
+    for link in project.xpath('link[@project]'):
+        link.getparent().remove(link)
+
     # Remove repositories that reference other projects.
     for repository in project.xpath('repository[releasetarget or path]'):
         repository.getparent().remove(repository)
@@ -89,9 +93,18 @@
     # Write stripped version that does not include repos with path references.
     url = makeurl(apiurl_target, ['source', project.get('name'), '_meta'])
     stripped = deepcopy(project)
-    project_repositories_remove(stripped)
+    project_references_remove(stripped)
     http_PUT(url, data=ET.tostring(stripped))
 
+    for link in project.xpath('link[@project]'):
+        if not project_fence(link.get('project')):
+            project.remove(link)
+            break
+
+        # Valid reference to project and thus should be cloned.
+        path = ['source', link.get('project'), '_meta']
+        entity_clone(apiurl_source, apiurl_target, path, clone=project_clone)
+
     # Clone projects referenced in repository paths.
     for repository in project.findall('repository'):
         for target in repository.xpath('./path') + 
repository.xpath('./releasetarget'):
@@ -105,7 +118,9 @@
 
 def project_workaround(project):
     if project.get('name') == 'openSUSE:Factory':
-        # TODO #1335: temporary scariness from from revision 429.
+        # See #1335 for details about temporary workaround in revision 429, but
+        # suffice it to say that over-complicates clone with multiple loops and
+        # may be introduced from time to time when Factory repo is hosed.
         scariness = 
project.xpath('repository[@name="standard"]/path[contains(@project, 
":0-Bootstrap")]')
         if len(scariness):
             scariness[0].getparent().remove(scariness[0])
@@ -151,6 +166,14 @@
 
     try:
         # TODO Decide how to choose what to clone via args.
+
+        # Rather than handle the self-referencing craziness with a proper 
solver
+        # the leaf can simple be used to start the chain and works as desired.
+        # Disable this when running clone repeatedly during developing as the
+        # projects cannot be cleanly re-created without more work.
+        entity_clone(apiurl_source, apiurl_target, ['source', project + 
':Rings:2-TestDVD', '_meta'],
+                     clone=project_clone)
+
         entity_clone(apiurl_source, apiurl_target, ['source', project + 
':Staging', 'dashboard', '_meta'],
                      clone=package_clone, after=package_clone_after)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/osclib/cleanup_rings.py 
new/openSUSE-release-tools-20180122.b5fe7e7/osclib/cleanup_rings.py
--- old/openSUSE-release-tools-20180110.9ab0211/osclib/cleanup_rings.py 
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/osclib/cleanup_rings.py 
2018-01-22 14:40:19.000000000 +0100
@@ -3,6 +3,7 @@
 from osc.core import makeurl
 from osc.core import http_GET
 
+import urllib2
 
 class CleanupRings(object):
     def __init__(self, api):
@@ -72,29 +73,34 @@
         root = ET.parse(f).getroot()
 
         for package in root.findall('package'):
-            source = package.find('source').text
-            if package.attrib['name'].startswith('preinstall'):
+            # use main package name for multibuild. We can't just ignore
+            # multibuild as eg installation-images has no results for the main
+            # package itself
+            # https://github.com/openSUSE/open-build-service/issues/4198
+            name = package.attrib['name'].split(':')[0]
+            if name.startswith('preinstall'):
                 continue
-            self.sources.add(source)
+
+            self.sources.add(name)
 
             for subpkg in package.findall('subpkg'):
                 subpkg = subpkg.text
                 if subpkg in self.bin2src:
-                    if self.bin2src[subpkg] == source:
+                    if self.bin2src[subpkg] == name:
                         # different archs
                         continue
-                    print('Binary {} is defined twice: {}/{}'.format(subpkg, 
prj, source))
-                self.bin2src[subpkg] = source
+                    print('# Binary {} is defined twice: {}/{}'.format(subpkg, 
prj, name))
+                self.bin2src[subpkg] = name
 
         for package in root.findall('package'):
-            source = package.find('source').text
+            name = package.attrib['name'].split(':')[0]
             for pkg in package.findall('pkgdep'):
                 if pkg.text not in self.bin2src:
                     if not pkg.text.startswith('texlive-'): # XXX: texlive 
bullshit packaging
                         print('Package {} not found in place'.format(pkg.text))
                     continue
                 b = self.bin2src[pkg.text]
-                self.pkgdeps[b] = source
+                self.pkgdeps[b] = name
 
     def repo_state_acceptable(self, project):
         url = makeurl(self.api.apiurl, ['build', project, '_result'])
@@ -112,16 +118,24 @@
         return True
 
     def check_image_bdeps(self, project, arch):
-        url = makeurl(self.api.apiurl, ['build', project, 'images', arch, 
'Test-DVD-' + arch, '_buildinfo'])
-        root = ET.parse(http_GET(url)).getroot()
-        for bdep in root.findall('bdep'):
-            if 'name' not in bdep.attrib:
-                continue
-            b = bdep.attrib['name']
-            if b not in self.bin2src:
-                continue
-            b = self.bin2src[b]
-            self.pkgdeps[b] = 'MYdvd{}'.format(self.api.rings.index(project))
+        for dvd in ('000product:openSUSE-dvd5-dvd-{}'.format(arch), 
'Test-DVD-{}'.format(arch)):
+            try:
+                url = makeurl(self.api.apiurl, ['build', project, 'images', 
arch, dvd, '_buildinfo'])
+                root = ET.parse(http_GET(url)).getroot()
+            except urllib2.HTTPError as e:
+                if e.code == 404:
+                    continue
+                raise
+            for bdep in root.findall('bdep'):
+                if 'name' not in bdep.attrib:
+                    continue
+                b = bdep.attrib['name']
+                if b not in self.bin2src:
+                    print "{} not found in bin2src".format(b)
+                    continue
+                b = self.bin2src[b]
+                self.pkgdeps[b] = 
'MYdvd{}'.format(self.api.rings.index(project))
+            break
 
     def check_buildconfig(self, project):
         url = makeurl(self.api.apiurl, ['build', project, 'standard', 
'_buildconfig'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/osclib/conf.py 
new/openSUSE-release-tools-20180122.b5fe7e7/osclib/conf.py
--- old/openSUSE-release-tools-20180110.9ab0211/osclib/conf.py  2018-01-11 
05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/osclib/conf.py  2018-01-22 
14:40:19.000000000 +0100
@@ -74,7 +74,8 @@
         'pkglistgen-archs-ports': 'aarch64',
         'pkglistgen-locales-from': 'openSUSE.product',
         'pkglistgen-include-suggested': '1',
-        'pkglistgen-delete-kiwis': 'openSUSE-ftp-ftp-x86_64.kiwi 
openSUSE-cd-mini-x86_64.kiwi',
+        'pkglistgen-delete-kiwis-rings': 'openSUSE-ftp-ftp-x86_64.kiwi 
openSUSE-cd-mini-x86_64.kiwi',
+        'pkglistgen-delete-kiwis-staging': 'openSUSE-ftp-ftp-x86_64.kiwi 
openSUSE-cd-mini-x86_64.kiwi',
     },
     r'SUSE:(?P<project>SLE-15.*$)': {
         'staging': 'SUSE:%(project)s:Staging',
@@ -92,6 +93,7 @@
         'main-repo': 'standard',
         # check_source.py
         'repo-checker': 'repo-checker',
+        'repo_checker-package-comment-devel': '',
         'pkglistgen-archs': 'x86_64',
         'pkglistgen-ignore-unresolvable': '1',
         'pkglistgen-ignore-recommended': '1',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/osclib/core.py 
new/openSUSE-release-tools-20180122.b5fe7e7/osclib/core.py
--- old/openSUSE-release-tools-20180110.9ab0211/osclib/core.py  2018-01-11 
05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/osclib/core.py  2018-01-22 
14:40:19.000000000 +0100
@@ -3,12 +3,14 @@
 import dateutil.parser
 import re
 from xml.etree import cElementTree as ET
+from urllib2 import HTTPError
 
 from osc.core import get_binarylist
 from osc.core import get_dependson
 from osc.core import http_GET
 from osc.core import makeurl
 from osc.core import owner
+from osc.core import show_package_meta
 from osc.core import show_project_meta
 from osclib.memoize import memoize
 
@@ -134,3 +136,31 @@
             binary_map[result.group('filename')] = package
 
     return package_binaries, binary_map
+
+@memoize(session=True)
+def devel_project_get(apiurl, target_project, target_package):
+    try:
+        meta = ET.fromstring(''.join(show_package_meta(apiurl, target_project, 
target_package)))
+        node = meta.find('devel')
+        if node is not None:
+            return node.get('project'), node.get('package')
+    except HTTPError as e:
+        if e.code != 404:
+            raise e
+
+    return None, None
+
+@memoize(session=True)
+def devel_project_fallback(apiurl, target_project, target_package):
+    project, package = devel_project_get(apiurl, target_project, 
target_package)
+    if project is None and target_project != 'openSUSE:Factory':
+        if target_project.startswith('openSUSE:'):
+            project, package = devel_project_get(apiurl, 'openSUSE:Factory', 
target_package)
+        elif target_project.startswith('SUSE:'):
+            # For SLE (assume IBS), fallback to openSUSE:Factory devel 
projects.
+            project, package = devel_project_get(apiurl, 
'openSUSE.org:openSUSE:Factory', target_package)
+            if project:
+                # Strip openSUSE.org: prefix since string since not used for 
lookup.
+                project = project.split(':', 1)[1]
+
+    return project, package
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/osclib/request_splitter.py 
new/openSUSE-release-tools-20180122.b5fe7e7/osclib/request_splitter.py
--- old/openSUSE-release-tools-20180110.9ab0211/osclib/request_splitter.py      
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/osclib/request_splitter.py      
2018-01-22 14:40:19.000000000 +0100
@@ -3,6 +3,8 @@
 import hashlib
 from lxml import etree as ET
 from osc import conf
+from osc.core import show_project_meta
+from osclib.core import devel_project_fallback
 import re
 
 class RequestSplitter(object):
@@ -16,7 +18,7 @@
         self.request_age_threshold = 
int(self.config.get('splitter-request-age-threshold', 55 * 60))
         self.staging_age_max = int(self.config.get('splitter-staging-age-max', 
8 * 60 * 60))
         special_packages= self.config.get('splitter-special-packages')
-        if special_packages:
+        if special_packages is not None:
             StrategySpecial.PACKAGES = special_packages.split(' ')
 
         self.requests_ignored = self.api.get_ignored_requests()
@@ -112,7 +114,7 @@
         target = request.find('./action/target')
         target_project = target.get('project')
         target_package = target.get('package')
-        devel = self.devel_project_get(target_project, target_package)
+        devel, _ = devel_project_fallback(self.api.apiurl, target_project, 
target_package)
         if not devel and request_type == 'submit':
             devel = request.find('./action/source').get('project')
         if devel:
@@ -145,18 +147,6 @@
             return self.api.project
         return None
 
-    def devel_project_get(self, target_project, target_package):
-        devel = self.api.get_devel_project(target_project, target_package)
-        if devel is None and self.api.project.startswith('openSUSE:'):
-            devel = self.api.get_devel_project('openSUSE:Factory', 
target_package)
-        if devel is None and self.api.project.startswith('SUSE:'):
-            # For SLE, fallback to openSUSE:Factory devel projects.
-            devel = 
self.api.get_devel_project('openSUSE.org:openSUSE:Factory', target_package)
-            if devel:
-                # Strip openSUSE.org: prefix since string since not used for 
lookup.
-                devel = devel.split(':', 1)[1]
-        return devel
-
     def filter_check(self, request):
         for xpath in self.filters:
             if not xpath(request):
@@ -476,10 +466,40 @@
 class StrategyQuick(StrategyNone):
     def apply(self, splitter):
         super(StrategyQuick, self).apply(splitter)
+
+        # Leaper accepted which means any extra reviews have been added.
         splitter.filter_add('./review[@by_user="leaper" and 
@state="accepted"]')
-        splitter.filter_add('not(./review[not(@by_user="leaper" or 
@by_group="factory-staging")])')
+
+        # No @by_project reviews that are not accepted. If not first round 
stage
+        # this should also ignore previous staging project reviews or already
+        # accepted human reviews.
+        splitter.filter_add('not(./review[@by_project and 
@state!="accepted"])')
+
+        # Only allow reviews by whitelisted groups and users as all others will
+        # be considered non-quick (like @by_group="legal-auto"). The allowed
+        # groups are only those configured as reviewers on the target project.
+        meta = ET.fromstring(''.join(show_project_meta(splitter.api.apiurl, 
splitter.api.project)))
+        allowed_groups = meta.xpath('group[@role="reviewer"]/@groupid')
+        allowed_users = []
+        if 'repo-checker' in splitter.config:
+            allowed_users.append(splitter.config['repo-checker'])
+
+        self.filter_review_whitelist(splitter, 'by_group', allowed_groups)
+        self.filter_review_whitelist(splitter, 'by_user', allowed_users)
+
+    def filter_review_whitelist(self, splitter, attribute, allowed):
+        # Rather than generate a bunch of @attribute="allowed[0]" pairs
+        # contains is used, but the attribute must be asserted first since
+        # concat() loses that requirement.
+        allowed = ' ' + ' '.join(allowed) + ' '
+        splitter.filter_add(
+            # Assert that no(non-whitelisted and not accepted) review is found.
+            'not(./review[@{attribute} and '
+            'not(contains("{allowed}", concat(" ", @{attribute}, " "))) and '
+            '@state!="accepted"])'.format(attribute=attribute, 
allowed=allowed))
 
 class StrategySpecial(StrategyNone):
+    # Configurable via splitter-special-packages.
     PACKAGES = [
         'gcc',
         'gcc7',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/osclib/stagingapi.py 
new/openSUSE-release-tools-20180122.b5fe7e7/osclib/stagingapi.py
--- old/openSUSE-release-tools-20180110.9ab0211/osclib/stagingapi.py    
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/osclib/stagingapi.py    
2018-01-22 14:40:19.000000000 +0100
@@ -48,6 +48,7 @@
 from osc.core import streamfile
 
 from osclib.cache import Cache
+from osclib.core import devel_project_get
 from osclib.comments import CommentAPI
 from osclib.ignore_command import IgnoreCommand
 from osclib.memoize import memoize
@@ -400,7 +401,7 @@
                 return None
         packages = root.findall('./frozenlink/package')
         # the first package's devel project is good enough
-        return self.get_devel_project(self.project, packages[0].get('name'))
+        return devel_project_get(self.apiurl, self.project, 
packages[0].get('name'))[0]
 
     def do_change_review_state(self, request_id, newstate, message=None,
                                by_group=None, by_user=None, by_project=None):
@@ -1794,17 +1795,6 @@
         else:
             return False
 
-    def get_devel_project(self, project, package):
-        try:
-            m = show_package_meta(self.apiurl, project, package)
-            node = ET.fromstring(''.join(m)).find('devel')
-            if node is not None:
-                return node.get('project')
-        except urllib2.HTTPError as e:
-            if e.code == 404:
-                pass
-        return None
-
     def staging_deactivate(self, project):
         """Cleanup staging after last request is removed and disable 
building."""
         # Clear pseudometa since it no longer represents the staging.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/pkglistgen.py 
new/openSUSE-release-tools-20180122.b5fe7e7/pkglistgen.py
--- old/openSUSE-release-tools-20180110.9ab0211/pkglistgen.py   2018-01-11 
05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/pkglistgen.py   2018-01-22 
14:40:19.000000000 +0100
@@ -1099,7 +1099,7 @@
             print(subprocess.check_output(
                 [PRODUCT_SERVICE, product_file, product_dir, opts.project]))
 
-        delete_kiwis = target_config.get('pkglistgen-delete-kiwis', 
'').split(' ')
+        delete_kiwis = 
target_config.get('pkglistgen-delete-kiwis-{}'.format(opts.scope), '').split(' 
')
         self.unlink_list(product_dir, delete_kiwis)
 
         spec_files = glob.glob(os.path.join(product_dir, '*.spec'))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/repo_checker.py 
new/openSUSE-release-tools-20180122.b5fe7e7/repo_checker.py
--- old/openSUSE-release-tools-20180110.9ab0211/repo_checker.py 2018-01-11 
05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/repo_checker.py 2018-01-22 
14:40:19.000000000 +0100
@@ -15,6 +15,7 @@
 from osclib.comments import CommentAPI
 from osclib.core import binary_list
 from osclib.core import depends_on
+from osclib.core import devel_project_fallback
 from osclib.core import package_binary_list
 from osclib.core import request_staged
 from osclib.core import target_archs
@@ -81,7 +82,17 @@
         self.logger.info('{} package 
comments'.format(len(self.package_results)))
 
         for package, sections in self.package_results.items():
-            message = 'The version of this package in `{}` has installation 
issues and may not be installable:'.format(project)
+            if 
bool(self.staging_config[project].get('repo_checker-package-comment-devel', 
True)):
+                bot_name_suffix = project
+                comment_project, comment_package = 
devel_project_fallback(self.apiurl, project, package)
+                message = 'The version of this package in 
[`{project}`](/package/show/{project}/{package}) ' \
+                    'has installation issues and may not be 
installable:'.format(
+                        project=project, package=package)
+            else:
+                bot_name_suffix = None
+                comment_project = project
+                comment_package = package
+                message = 'This package has installation issues and may not be 
installable:'
 
             # Sort sections by text to group binaries together.
             sections = sorted(sections, key=lambda s: s.text)
@@ -97,10 +108,9 @@
             info = ';'.join(['::'.join(sorted(binaries)), str(len(sections))])
             reference = hashlib.sha1(info).hexdigest()[:7]
 
-            # Post comment on devel package in order to notifiy maintainers.
-            devel_project, devel_package = self.get_devel_project(project, 
package)
-            self.comment_write(state='seen', result=reference,
-                               project=devel_project, package=devel_package, 
message=message)
+            # Post comment on package in order to notifiy maintainers.
+            self.comment_write(state='seen', result=reference, 
bot_name_suffix=bot_name_suffix,
+                               project=comment_project, 
package=comment_package, message=message)
 
     def prepare_review(self):
         # Reset for request batch.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/requestfinder.py 
new/openSUSE-release-tools-20180122.b5fe7e7/requestfinder.py
--- old/openSUSE-release-tools-20180110.9ab0211/requestfinder.py        
1970-01-01 01:00:00.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/requestfinder.py        
2018-01-22 14:40:19.000000000 +0100
@@ -0,0 +1,223 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018 SUSE LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+from ConfigParser import ConfigParser
+from xdg.BaseDirectory import load_first_config
+from xml.etree import cElementTree as ET
+import sys
+import cmdln
+import logging
+import urllib2
+import osc.core
+import yaml
+import os
+
+import ToolBase
+
+makeurl = osc.core.makeurl
+
+logger = logging.getLogger()
+
+
+class Requestfinder(ToolBase.ToolBase):
+
+    def __init__(self):
+        ToolBase.ToolBase.__init__(self)
+        self.devel = None
+
+    def fill_package_meta(self, project):
+        self.package_metas = dict()
+        url = makeurl(self.apiurl, ['search', 'package'], 
"match=[@project='%s']" % project)
+        root = ET.fromstring(self.cached_GET(url))
+        for p in root.findall('package'):
+            name = p.attrib['name']
+            self.package_metas[name] = p
+
+    def find_requests(self, xquery):
+
+        if self.devel:
+            self.fill_package_meta('openSUSE:Factory')
+
+        url = osc.core.makeurl(self.apiurl, ('search', 'request'), {"match": 
xquery})
+        root = ET.parse(osc.core.http_GET(url)).getroot()
+
+        self.requests = []
+
+        for request in root.findall('request'):
+            req = osc.core.Request()
+            req.read(request)
+            if self.devel:
+                p = req.actions[0].tgt_package
+                pm = self.package_metas[p] if p in self.package_metas else None
+                devel = pm.find('devel') if pm else None
+                if devel is None or devel.get('project') == self.devel:
+                    self.requests.append(req)
+            else:
+                self.requests.append(req)
+
+        return self.requests
+
+
+class CommandLineInterface(ToolBase.CommandLineInterface):
+
+    def __init__(self, *args, **kwargs):
+        ToolBase.CommandLineInterface.__init__(self, args, kwargs)
+
+        self.cp = ConfigParser()
+        d = load_first_config('opensuse-release-tools')
+        if d:
+            self.cp.read(os.path.join(d, 'requestfinder.conf'))
+
+    def get_optparser(self):
+        parser = ToolBase.CommandLineInterface.get_optparser(self)
+        parser.add_option('--devel', dest='devel', metavar='PROJECT',
+                          help='only packages with devel project')
+        return parser
+
+    def setup_tool(self):
+        tool = Requestfinder()
+        tool.devel = self.options.devel
+        return tool
+
+    def _load_settings(self, settings, name):
+        section = 'settings {}'.format(name)
+        for option in settings.keys():
+            if self.cp.has_option(section, option):
+                settings[option] = self.cp.get(section, option).replace('\n', 
' ')
+
+    @cmdln.option('--exclude-project', metavar='PROJECT', action='append', 
help='exclude review by specific project')
+    @cmdln.option('--exclude-user', metavar='USER', action='append', 
help='exclude review by specific user')
+    @cmdln.option('--query', metavar='filterstr', help='filter string')
+    @cmdln.option('--action', metavar='action', help='action (accept/decline)')
+    @cmdln.option('--settings', metavar='settings', help='settings to load 
from config file')
+    @cmdln.option('-m', '--message', metavar="message", help="message")
+    def do_review(self, subcmd, opts):
+        """${cmd_name}: print commands for reviews
+
+        ${cmd_usage}
+        ${cmd_option_list}
+        """
+
+        settings = {
+            'action': 'accept',
+            'message': 'ok',
+            'query': None,
+            'exclude-project': None,
+            'exclude-user': None,
+        }
+
+        if opts.settings:
+            self._load_settings(settings, opts.settings)
+
+        if opts.action:
+            settings['action'] = opts.action
+            settings['message'] = opts.action
+
+        if opts.message:
+            settings['message'] = opts.message
+
+        if opts.query:
+            settings['query'] = opts.query
+
+        if not settings['query']:
+            raise Exception('please specify query')
+
+        rqs = self.tool.find_requests(settings['query'])
+        for r in rqs:
+            if r.actions[0].type == 'submit':
+                print(' '.join(('#', r.reqid, r.actions[0].type, 
r.actions[0].src_project, r.actions[0].src_package, r.actions[0].tgt_project)))
+            else:
+                print(' '. join(('#', r.reqid, r.actions[0].type, 
r.actions[0].tgt_project)))
+            for review in r.reviews:
+                if review.state != 'new':
+                    continue
+
+                if review.by_project:
+                    skip = False
+                    if settings['exclude-project']:
+                        for p in settings['exclude-project']:
+                            if review.by_project.startswith(p):
+                                skip = True
+                                break
+                    if not skip:
+                        if review.by_package:
+                            print("osc review %s -m '%s' -P %s -p %s %s" % 
(settings['action'], settings['message'], review.by_project, review.by_package, 
r.reqid))
+                        else:
+                            print("osc review %s -m '%s' -P %s %s" % 
(settings['action'], settings['message'], review.by_project, r.reqid))
+                elif review.by_group:
+                    print("osc review %s -m '%s' -G %s %s" % 
(settings['action'], settings['message'], review.by_group, r.reqid))
+                elif review.by_user:
+                    skip = False
+                    if settings['exclude-user']:
+                        for u in settings['exclude-user']:
+                            if review.by_user == u:
+                                skip = True
+                                break
+                    if not skip:
+                        print("osc review %s -m '%s' -U %s %s" % 
(settings['action'], settings['message'], review.by_user, r.reqid))
+
+    @cmdln.option('--query', metavar='filterstr', help='filter string')
+    @cmdln.option('--action', metavar='action', help='action (accept/decline)')
+    @cmdln.option('--settings', metavar='settings', help='settings to load 
from config file')
+    @cmdln.option('-m', '--message', metavar="message", help="message")
+    def do_request(self, subcmd, opts):
+        """${cmd_name}: print commands for requests
+
+        ${cmd_usage}
+        ${cmd_option_list}
+        """
+
+        settings = {
+            'action': 'reopen',
+            'message': 'reopen',
+            'query': None,
+        }
+
+        if opts.settings:
+            self._load_settings(settings, opts.settings)
+
+        rqs = self.tool.find_requests(settings['query'])
+        for r in rqs:
+            print('#', r.reqid, r.get_creator(), r.actions[0].src_project, 
r.actions[0].src_package, r.actions[0].tgt_project)
+            print("osc rq {} -m '{}' {}".format(settings['action'], 
settings['message'], r.reqid))
+
+    def help_examples(self):
+        return """$ cat > ~/.config/opensuse-release-tools/requestfinder.conf 
<< EOF
+        [settings foo]
+        query = (review[@by_project='example' and @state='new']
+                 and state/@name='review'
+                 and action/source/@project='openSUSE:Factory'
+                 and action/target/@project='openSUSE:Leap:15.0'
+        exclude-user = repo-checker
+        exclude-project = openSUSE:Leap:15.0:Staging
+        message = override
+        action = accept
+        EOF
+        $ ${name} review --settings foo | tee doit.sh
+        ./doit.sh
+        """
+
+if __name__ == "__main__":
+    app = CommandLineInterface()
+    sys.exit(app.main())
+
+# vim: sw=4 et
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/systemd/[email protected] 
new/openSUSE-release-tools-20180122.b5fe7e7/systemd/[email protected]
--- old/openSUSE-release-tools-20180110.9ab0211/systemd/[email protected]   
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/systemd/[email protected]   
2018-01-22 14:40:19.000000000 +0100
@@ -4,7 +4,9 @@
 [Service]
 User=osrt-metrics
 SyslogIdentifier=osrt-metrics
-ExecStart=/usr/bin/osrt-metrics --debug -p "%i"
+# TODO #1244: improve incremental data ingest
+# ExecStart=/usr/bin/osrt-metrics --debug -p "%i"
+ExecStart=/usr/bin/osrt-metrics --debug -p "%i" --wipe-cache
 
 [Install]
 WantedBy=multi-user.target
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/systemd/[email protected] 
new/openSUSE-release-tools-20180122.b5fe7e7/systemd/[email protected]
--- old/openSUSE-release-tools-20180110.9ab0211/systemd/[email protected]     
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/systemd/[email protected]     
2018-01-22 14:40:19.000000000 +0100
@@ -3,7 +3,9 @@
 
 [Timer]
 OnBootSec=120
-OnCalendar=daily
+# TODO #1244: improve incremental data ingest
+# OnCalendar=daily
+OnCalendar=weekly
 Unit=osrt-metrics@%i.service
 
 [Install]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/tests/ReviewBot_tests.py 
new/openSUSE-release-tools-20180122.b5fe7e7/tests/ReviewBot_tests.py
--- old/openSUSE-release-tools-20180110.9ab0211/tests/ReviewBot_tests.py        
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/tests/ReviewBot_tests.py        
2018-01-22 14:40:19.000000000 +0100
@@ -132,6 +132,30 @@
         _, info = self.comments_filtered(self.bot)
         self.assertEqual(info['state'], 'changed')
 
+    def test_bot_name_suffix(self):
+        suffix1 = 'suffix1'
+        bot_suffixed1 = '::'.join([self.bot, suffix1])
+
+        suffix2 = 'suffix2'
+        bot_suffixed2 = '::'.join([self.bot, suffix2])
+
+        self.review_bot.comment_write(bot_name_suffix=suffix1, 
project=PROJECT, message=COMMENT)
+        self.assertFalse(self.comments_filtered(self.bot)[0])
+        self.assertTrue(self.comments_filtered(bot_suffixed1)[0])
+        self.assertFalse(self.comments_filtered(bot_suffixed2)[0])
+
+        self.review_bot.comment_write(bot_name_suffix=suffix2, 
project=PROJECT, message=COMMENT)
+        self.assertFalse(self.comments_filtered(self.bot)[0])
+        self.assertTrue(self.comments_filtered(bot_suffixed1)[0])
+        self.assertTrue(self.comments_filtered(bot_suffixed2)[0])
+
+        self.review_bot.comment_write(bot_name_suffix=suffix1, 
project=PROJECT, message=COMMENT + '\nnew')
+        comment, _ = self.comments_filtered(bot_suffixed1)
+        self.assertTrue(comment['comment'].endswith(COMMENT + '\nnew'))
+
+        comment, _ = self.comments_filtered(bot_suffixed2)
+        self.assertTrue(comment['comment'].endswith(COMMENT))
+
     def comments_filtered(self, bot):
         comments = self.api.get_comments(project_name=PROJECT)
         return self.api.comment_find(comments, bot)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20180110.9ab0211/update_crawler.py 
new/openSUSE-release-tools-20180122.b5fe7e7/update_crawler.py
--- old/openSUSE-release-tools-20180110.9ab0211/update_crawler.py       
2018-01-11 05:13:58.000000000 +0100
+++ new/openSUSE-release-tools-20180122.b5fe7e7/update_crawler.py       
2018-01-22 14:40:19.000000000 +0100
@@ -38,6 +38,7 @@
 
 from osclib.memoize import memoize
 from osclib.conf import Config
+from osclib.core import devel_project_get
 from osclib.stagingapi import StagingAPI
 
 OPENSUSE = 'openSUSE:Leap:42.3'
@@ -283,7 +284,7 @@
                 targetinfo = targets[package]
 
                 # XXX: make more generic :-)
-                devel_prj = self.api.get_devel_project(FACTORY, package)
+                devel_prj = devel_project_get(self.apiurl, FACTORY, package)
                 if devel_prj == 'devel:languages:haskell':
                     logging.info('skipping haskell package %s' % package)
                     continue

++++++ openSUSE-release-tools.obsinfo ++++++
--- /var/tmp/diff_new_pack.uLxMl8/_old  2018-01-22 16:22:24.863000661 +0100
+++ /var/tmp/diff_new_pack.uLxMl8/_new  2018-01-22 16:22:24.867000474 +0100
@@ -1,5 +1,5 @@
 name: openSUSE-release-tools
-version: 20180110.9ab0211
-mtime: 1515644038
-commit: 9ab0211e27d223add45e503348fdf8b8b4fde5fe
+version: 20180122.b5fe7e7
+mtime: 1516628419
+commit: b5fe7e74e890e1f5a30262865f1d63c6eae0742f
 


Reply via email to