Hello community,

here is the log from the commit of package openSUSE-release-tools for 
openSUSE:Factory checked in at 2019-09-16 10:52:00
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/openSUSE-release-tools (Old)
 and      /work/SRC/openSUSE:Factory/.openSUSE-release-tools.new.7948 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "openSUSE-release-tools"

Mon Sep 16 10:52:00 2019 rev:218 rq:730792 version:20190913.9d119079

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/openSUSE-release-tools/openSUSE-release-tools.changes
    2019-09-13 14:58:17.973277188 +0200
+++ 
/work/SRC/openSUSE:Factory/.openSUSE-release-tools.new.7948/openSUSE-release-tools.changes
  2019-09-16 10:52:11.511158326 +0200
@@ -1,0 +2,53 @@
+Fri Sep 13 16:25:13 UTC 2019 - [email protected]
+
+- Update to version 20190913.9d119079:
+  * gocd: remove PackageHub15SP1.Manager42 as project is locked.
+
+-------------------------------------------------------------------
+Fri Sep 13 05:13:12 UTC 2019 - [email protected]
+
+- Update to version 20190913.d83c08a9:
+  * gocd: rabbit-openqa needs a config file installed
+
+-------------------------------------------------------------------
+Thu Sep 12 15:33:23 UTC 2019 - [email protected]
+
+- Update to version 20190912.9c45fc37:
+  * osclib/origin_listener: start additional listeners for remote origins.
+  * osclib/origin_listener: change skipping log message to mention origin.
+  * osclib/origin_listener: provide origin_updatable_map().
+
+-------------------------------------------------------------------
+Thu Sep 12 12:59:33 UTC 2019 - [email protected]
+
+- Update to version 20190912.f5a9538d:
+  * check_source: only log as info if we can't get build log. This needs a 
more general solution but for now we'll reduce the level to not pollute the log
+
+-------------------------------------------------------------------
+Thu Sep 12 11:09:05 UTC 2019 - [email protected]
+
+- Update to version 20190912.6dd6428d:
+  * Move the monitors to a agent type of their own
+
+-------------------------------------------------------------------
+Thu Sep 12 09:23:42 UTC 2019 - [email protected]
+
+- Update to version 20190912.49029a0e:
+  * gocd/checkers: reduce Origin.Manager.Update frequency to weekly.
+  * gocd/monitors: provide OriginManagerUpdate pipelines for listener.
+  * osc-origin: update: provide --listen option.
+  * gocd: utilize `origin update` without the need to indicate a project.
+  * osc-origin: support invoking update without a project.
+  * osclib/origin: provide origin_updatable_map().
+  * osclib/origin: provide origin_updatable().
+  * dist/obs: provide OSRT:OriginUpdateSkip attribute definition.
+  * osc-origin: utilize osclib.core.RequestFuture.print_and_create().
+  * osclib/core: provide RequestFuture.print_and_create().
+  * osclib/core: provide RequestFuture.create_tolerant().
+  * osclib/core: provide project_attributes_list().
+  * osclib/core: project_attribute_list(): provide locked parameter.
+  * osclib/core: project_attribute_list(): drop value parameter.
+  * osclib/core: provide devel_projects().
+  * Move PubSubConsumer to osclib.PubSubConsumer to allow for proper import.
+
+-------------------------------------------------------------------

Old:
----
  openSUSE-release-tools-20190911.d06fa8af.obscpio

New:
----
  openSUSE-release-tools-20190913.9d119079.obscpio

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

Other differences:
------------------
++++++ openSUSE-release-tools.spec ++++++
--- /var/tmp/diff_new_pack.Eytaky/_old  2019-09-16 10:52:13.423158078 +0200
+++ /var/tmp/diff_new_pack.Eytaky/_new  2019-09-16 10:52:13.427158078 +0200
@@ -20,7 +20,7 @@
 %define source_dir openSUSE-release-tools
 %define announcer_filename factory-package-news
 Name:           openSUSE-release-tools
-Version:        20190911.d06fa8af
+Version:        20190913.9d119079
 Release:        0
 Summary:        Tools to aid in staging and release work for openSUSE/SUSE
 License:        GPL-2.0-or-later AND MIT

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.Eytaky/_old  2019-09-16 10:52:13.471158072 +0200
+++ /var/tmp/diff_new_pack.Eytaky/_new  2019-09-16 10:52:13.475158072 +0200
@@ -1,6 +1,6 @@
 <servicedata>
   <service name="tar_scm">
     <param 
name="url">https://github.com/openSUSE/openSUSE-release-tools.git</param>
-    <param 
name="changesrevision">f30964b69c48d26cc05086784532ff509e058ac0</param>
+    <param 
name="changesrevision">bc377aa24366fef38f425e988b225a438af748e5</param>
   </service>
 </servicedata>

++++++ openSUSE-release-tools-20190911.d06fa8af.obscpio -> 
openSUSE-release-tools-20190913.9d119079.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/PubSubConsumer.py 
new/openSUSE-release-tools-20190913.9d119079/PubSubConsumer.py
--- old/openSUSE-release-tools-20190911.d06fa8af/PubSubConsumer.py      
2019-09-11 12:46:39.000000000 +0200
+++ new/openSUSE-release-tools-20190913.9d119079/PubSubConsumer.py      
1970-01-01 01:00:00.000000000 +0100
@@ -1,397 +0,0 @@
-import functools
-import logging
-import pika
-import ssl
-import sys
-import time
-from datetime import datetime
-
-class PubSubConsumer(object):
-    """
-    Based on 
https://github.com/pika/pika/blob/master/examples/asynchronous_consumer_example.py
-
-    This is an example consumer that will handle unexpected interactions
-    with RabbitMQ such as channel and connection closures.
-
-    If RabbitMQ closes the connection, it will reopen it. You should
-    look at the output, as there are limited reasons why the connection may
-    be closed, which usually are tied to permission related issues or
-    socket timeouts.
-
-    If the channel is closed, it will indicate a problem with one of the
-    commands that were issued and that should surface in the output as well.
-
-    """
-
-    def __init__(self, amqp_prefix, logger):
-        """Create a new instance of the consumer class, passing in the AMQP
-        URL used to connect to RabbitMQ.
-
-        :param str amqp_url: The AMQP url to connect with
-
-        """
-        self._connection = None
-        self._channel = None
-        self._closing = False
-        self._consuming = False
-        self._consumer_tag = None
-        self._prefix = amqp_prefix
-        self._timer_id = None
-        self._run_until = None
-        self.logger = logger
-
-    def restart_timer(self):
-        interval = 300
-        if self._timer_id:
-            self._connection.ioloop.remove_timeout(self._timer_id)
-        else:
-            # check the initial state on first timer hit
-            # so be quick about it
-            interval = 0
-        self._timer_id = self._connection.ioloop.call_later(interval, 
self.still_alive)
-
-    def still_alive(self):
-        # output something so gocd doesn't consider it stalled
-        self.logger.info('Still alive: {}'.format(datetime.now().time()))
-        if self._run_until and time.time() > self._run_until:
-            self.stop()
-        else:
-            self.restart_timer()
-
-    def connect(self):
-        """This method connects to RabbitMQ, returning the connection handle.
-        When the connection is established, the on_connection_open method
-        will be invoked by pika.
-
-        :rtype: pika.SelectConnection
-
-        """
-        self.logger.info('Connecting to %s', self._prefix)
-        account = 'opensuse'
-        server = 'rabbit.opensuse.org'
-        if self._prefix == 'suse':
-            account = 'suse'
-            server = 'rabbit.suse.de'
-        credentials = pika.PlainCredentials(account, account)
-        context = ssl.create_default_context()
-        ssl_options = pika.SSLOptions(context, server)
-        parameters = pika.ConnectionParameters(server, 5671, '/', credentials, 
ssl_options=ssl_options, socket_timeout=10)
-        return pika.SelectConnection(parameters,
-                                     on_open_callback=self.on_connection_open)
-
-    def close_connection(self):
-        """This method closes the connection to RabbitMQ."""
-        self._consuming = False
-        if self._connection.is_closing or self._connection.is_closed:
-            self.logger.info('Connection is closing or already closed')
-        else:
-            self.logger.info('Closing connection')
-            self._connection.close()
-
-    def add_on_connection_close_callback(self):
-        """This method adds an on close callback that will be invoked by pika
-        when RabbitMQ closes the connection to the publisher unexpectedly.
-
-        """
-        self.logger.debug('Adding connection close callback')
-        self._connection.add_on_close_callback(self.on_connection_closed)
-
-    def on_connection_closed(self, connection, reason):
-        """This method is invoked by pika when the connection to RabbitMQ is
-        closed unexpectedly. Since it is unexpected, we will reconnect to
-        RabbitMQ if it disconnects.
-
-        :param pika.connection.Connection connection: The closed connection obj
-        :param int reply_code: The server provided reply_code if given
-        :param str reply_text: The server provided reply_text if given
-
-        """
-        self._channel = None
-        if self._closing:
-            self._connection.ioloop.stop()
-        else:
-            self.logger.warning('Connection closed, reopening in 5 seconds: 
%s',
-                                reason)
-            self._connection.ioloop.call_later(5, self.reconnect)
-
-    def on_connection_open(self, unused_connection):
-        """This method is called by pika once the connection to RabbitMQ has
-        been established. It passes the handle to the connection object in
-        case we need it, but in this case, we'll just mark it unused.
-
-        :type unused_connection: pika.SelectConnection
-
-        """
-        self.logger.info('Connection opened')
-        self.add_on_connection_close_callback()
-        self.open_channel()
-
-    def reconnect(self):
-        """Will be invoked by the IOLoop timer if the connection is
-        closed. See the on_connection_closed method.
-
-        """
-        # This is the old connection IOLoop instance, stop its ioloop
-        self._connection.ioloop.stop()
-
-        if not self._closing:
-
-            # Create a new connection
-            self._connection = self.connect()
-
-            # There is now a new connection, needs a new ioloop to run
-            self._connection.ioloop.start()
-
-    def add_on_channel_close_callback(self):
-        """This method tells pika to call the on_channel_closed method if
-        RabbitMQ unexpectedly closes the channel.
-
-        """
-        self.logger.debug('Adding channel close callback')
-        self._channel.add_on_close_callback(self.on_channel_closed)
-
-    def on_channel_closed(self, channel, reason):
-        """Invoked by pika when RabbitMQ unexpectedly closes the channel.
-        Channels are usually closed if you attempt to do something that
-        violates the protocol, such as re-declare an exchange or queue with
-        different parameters. In this case, we'll close the connection
-        to shutdown the object.
-
-        :param pika.channel.Channel: The closed channel
-        :param int reply_code: The numeric reason the channel was closed
-        :param str reply_text: The text reason the channel was closed
-
-        """
-        self.logger.info('Channel %i was closed: %s',
-                            channel, reason)
-        self._connection.close()
-
-    def on_channel_open(self, channel):
-        """This method is invoked by pika when the channel has been opened.
-        The channel object is passed in so we can make use of it.
-
-        Since the channel is now open, we'll declare the exchange to use.
-
-        :param pika.channel.Channel channel: The channel object
-
-        """
-        self.logger.debug('Channel opened')
-        self._channel = channel
-        self.add_on_channel_close_callback()
-        self.setup_exchange('pubsub')
-
-    def setup_exchange(self, exchange_name):
-        """Setup the exchange on RabbitMQ by invoking the Exchange.Declare RPC
-        command. When it is complete, the on_exchange_declareok method will
-        be invoked by pika.
-
-        :param str|unicode exchange_name: The name of the exchange to declare
-
-        """
-        self.logger.debug('Declaring exchange %s', exchange_name)
-        self._channel.exchange_declare(exchange_name,
-                                       exchange_type='topic',
-                                       callback=self.on_exchange_declareok,
-                                       passive=True, durable=True)
-
-    def on_exchange_declareok(self, unused_frame):
-        """Invoked by pika when RabbitMQ has finished the Exchange.Declare RPC
-        command.
-
-        :param pika.Frame.Method unused_frame: Exchange.DeclareOk response 
frame
-
-        """
-        self.logger.debug('Exchange declared')
-        self._channel.queue_declare('', callback=self.on_queue_declareok, 
exclusive=True)
-
-    def on_queue_declareok(self, method_frame):
-        """Method invoked by pika when the Queue.Declare RPC call made in
-        setup_queue has completed. In this method we will bind the queue
-        and exchange together with the routing key by issuing the Queue.Bind
-        RPC command. When this command is complete, the on_bindok method will
-        be invoked by pika.
-
-        :param pika.frame.Method method_frame: The Queue.DeclareOk frame
-
-        """
-        self.queue_name = method_frame.method.queue
-        self.routing_keys_to_bind = self.routing_keys()
-        self.bind_queue_to_routing_key(self.routing_keys_to_bind.pop())
-
-    def routing_keys(self):
-        return ['#']
-
-    def bind_queue_to_routing_key(self, key):
-        self.logger.info('Binding %s to %s', key, self.queue_name)
-        self._channel.queue_bind(self.queue_name, 'pubsub', routing_key=key, 
callback=self.on_bindok)
-
-    def add_on_cancel_callback(self):
-        """Add a callback that will be invoked if RabbitMQ cancels the consumer
-        for some reason. If RabbitMQ does cancel the consumer,
-        on_consumer_cancelled will be invoked by pika.
-
-        """
-        self.logger.debug('Adding consumer cancellation callback')
-        self._channel.add_on_cancel_callback(self.on_consumer_cancelled)
-
-    def on_consumer_cancelled(self, method_frame):
-        """Invoked by pika when RabbitMQ sends a Basic.Cancel for a consumer
-        receiving messages.
-
-        :param pika.frame.Method method_frame: The Basic.Cancel frame
-
-        """
-        self.logger.info('Consumer was cancelled remotely, shutting down: %r',
-                         method_frame)
-        if self._channel:
-            self._channel.close()
-
-    def on_message(self, unused_channel, basic_deliver, properties, body):
-        """Invoked by pika when a message is delivered from RabbitMQ. The
-        channel is passed for your convenience. The basic_deliver object that
-        is passed in carries the exchange, routing key, delivery tag and
-        a redelivered flag for the message. The properties passed in is an
-        instance of BasicProperties with the message properties and the body
-        is the message that was sent.
-
-        :param pika.channel.Channel unused_channel: The channel object
-        :param pika.Spec.Basic.Deliver: basic_deliver method
-        :param pika.Spec.BasicProperties: properties
-        :param str|unicode body: The message body
-
-        """
-        self.logger.info('Received message # %s: %s %s',
-                         basic_deliver.delivery_tag, 
basic_deliver.routing_key, body)
-        self.acknowledge_message(basic_deliver.delivery_tag)
-
-    def acknowledge_message(self, delivery_tag):
-        """Acknowledge the message delivery from RabbitMQ by sending a
-        Basic.Ack RPC method for the delivery tag.
-
-        :param int delivery_tag: The delivery tag from the Basic.Deliver frame
-
-        """
-        self.logger.debug('Acknowledging message %s', delivery_tag)
-        self._channel.basic_ack(delivery_tag)
-
-    def on_cancelok(self, _unused_frame, userdata):
-        """This method is invoked by pika when RabbitMQ acknowledges the
-        cancellation of a consumer. At this point we will close the channel.
-        This will invoke the on_channel_closed method once the channel has been
-        closed, which will in-turn close the connection.
-        :param pika.frame.Method _unused_frame: The Basic.CancelOk frame
-        :param str|unicode userdata: Extra user data (consumer tag)
-        """
-        self._consuming = False
-        self.logger.debug(
-            'RabbitMQ acknowledged the cancellation of the consumer: %s',
-            userdata)
-        self.close_channel()
-
-    def close_channel(self):
-        """Call to close the channel with RabbitMQ cleanly by issuing the
-        Channel.Close RPC command.
-        """
-        self.logger.debug('Closing the channel')
-        self._channel.close()
-
-    def stop_consuming(self):
-        """Tell RabbitMQ that you would like to stop consuming by sending the
-        Basic.Cancel RPC command.
-
-        """
-        if self._channel:
-            self.logger.debug('Sending a Basic.Cancel RPC command to RabbitMQ')
-            cb = functools.partial(self.on_cancelok, 
userdata=self._consumer_tag)
-            self._channel.basic_cancel(self._consumer_tag, cb)
-
-    def start_consuming(self):
-        """This method sets up the consumer by first calling
-        add_on_cancel_callback so that the object is notified if RabbitMQ
-        cancels the consumer. It then issues the Basic.Consume RPC command
-        which returns the consumer tag that is used to uniquely identify the
-        consumer with RabbitMQ. We keep the value to use it when we want to
-        cancel consuming. The on_message method is passed in as a callback pika
-        will invoke when a message is fully received.
-
-        """
-        self.logger.debug('Issuing consumer related RPC commands')
-        self.add_on_cancel_callback()
-        self.restart_timer()
-        self._consumer_tag = self._channel.basic_consume(self.queue_name,
-                                                         self.on_message,
-                                                         auto_ack=False)
-        self._consuming = True
-
-    def on_bindok(self, unused_frame):
-        """Invoked by pika when the Queue.Bind method has completed. At this
-        point we will start consuming messages by calling start_consuming
-        which will invoke the needed RPC commands to start the process.
-
-        :param pika.frame.Method unused_frame: The Queue.BindOk response frame
-
-        """
-        self.logger.debug('Queue bound')
-        if len(self.routing_keys_to_bind):
-            self.bind_queue_to_routing_key(self.routing_keys_to_bind.pop())
-        else:
-            self.start_consuming()
-
-    def open_channel(self):
-        """Open a new channel with RabbitMQ by issuing the Channel.Open RPC
-        command. When RabbitMQ responds that the channel is open, the
-        on_channel_open callback will be invoked by pika.
-
-        """
-        self.logger.debug('Creating a new channel')
-        self._connection.channel(on_open_callback=self.on_channel_open)
-
-    def run(self, runtime=None):
-        """Run the example consumer by connecting to RabbitMQ and then
-        starting the IOLoop to block and allow the SelectConnection to operate.
-
-        """
-        if runtime:
-            self._run_until = time.time() + runtime
-        self._connection = self.connect()
-        self._connection.ioloop.start()
-
-    def stop(self):
-        """Cleanly shutdown the connection to RabbitMQ by stopping the consumer
-        with RabbitMQ. When RabbitMQ confirms the cancellation, on_cancelok
-        will be invoked by pika, which will then closing the channel and
-        connection. The IOLoop is started again because this method is invoked
-        when CTRL-C is pressed raising a KeyboardInterrupt exception. This
-        exception stops the IOLoop which needs to be running for pika to
-        communicate with RabbitMQ. All of the commands issued prior to starting
-        the IOLoop will be buffered but not processed.
-
-        """
-        self.logger.debug('Stopping')
-        if not self._closing:
-            self._closing = True
-            self.logger.debug('Stopping')
-            if self._consuming:
-                self.stop_consuming()
-                #self._connection.ioloop.start()
-            else:
-                self._connection.ioloop.stop()
-            self.logger.debug('Stopped')
-
-def main():
-    LOG_FORMAT = ('%(levelname) -10s %(asctime)s %(name) -30s %(funcName) '
-                  '-35s %(lineno) -5d: %(message)s')
-
-    logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
-    amqp_prefix = 'opensuse'
-    if len(sys.argv) > 1:
-        amqp_prefix = sys.argv[1]
-    example = PubSubConsumer(amqp_prefix, logging.getLogger(__name__))
-    try:
-        example.run()
-    except KeyboardInterrupt:
-        example.stop()
-
-
-if __name__ == '__main__':
-    main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/check_source.py 
new/openSUSE-release-tools-20190913.9d119079/check_source.py
--- old/openSUSE-release-tools-20190911.d06fa8af/check_source.py        
2019-09-11 12:46:39.000000000 +0200
+++ new/openSUSE-release-tools-20190913.9d119079/check_source.py        
2019-09-13 18:24:23.000000000 +0200
@@ -249,7 +249,7 @@
                             self.logger.info(f'found missing whitelist for 
warning: {entry}')
                             found_entries.add(entry)
                 except HTTPError as e:
-                    self.logger.error('ERROR in URL %s [%s]' % (url, e))
+                    self.logger.info('ERROR in URL %s [%s]' % (url, e))
         return found_entries
 
     def is_devel_project(self, source_project, target_project):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/config/manager_42/README.txt 
new/openSUSE-release-tools-20190913.9d119079/config/manager_42/README.txt
--- old/openSUSE-release-tools-20190911.d06fa8af/config/manager_42/README.txt   
1970-01-01 01:00:00.000000000 +0100
+++ new/openSUSE-release-tools-20190913.9d119079/config/manager_42/README.txt   
2019-09-13 18:24:23.000000000 +0200
@@ -0,0 +1 @@
+Create an OSRT:OriginManager config instead.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/config/manager_42/openSUSE:Backports:SLE-15-SP1.yml
 
new/openSUSE-release-tools-20190913.9d119079/config/manager_42/openSUSE:Backports:SLE-15-SP1.yml
--- 
old/openSUSE-release-tools-20190911.d06fa8af/config/manager_42/openSUSE:Backports:SLE-15-SP1.yml
    2019-09-11 12:46:39.000000000 +0200
+++ 
new/openSUSE-release-tools-20190913.9d119079/config/manager_42/openSUSE:Backports:SLE-15-SP1.yml
    1970-01-01 01:00:00.000000000 +0100
@@ -1,4 +0,0 @@
----
-from_prj: openSUSE:Backports:SLE-15-SP1
-project_preference_order:
-    - openSUSE:Leap:15.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/dist/obs/OSRT:OriginUpdateSkip.xml 
new/openSUSE-release-tools-20190913.9d119079/dist/obs/OSRT:OriginUpdateSkip.xml
--- 
old/openSUSE-release-tools-20190911.d06fa8af/dist/obs/OSRT:OriginUpdateSkip.xml 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/openSUSE-release-tools-20190913.9d119079/dist/obs/OSRT:OriginUpdateSkip.xml 
    2019-09-13 18:24:23.000000000 +0200
@@ -0,0 +1,5 @@
+<definition name="OriginUpdateSkip" namespace="OSRT">
+  <description>OriginManager skip updates</description>
+  <count>0</count>
+  <modifiable_by role="maintainer"/>
+</definition>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/gocd/checkers.opensuse.gocd.yaml 
new/openSUSE-release-tools-20190913.9d119079/gocd/checkers.opensuse.gocd.yaml
--- 
old/openSUSE-release-tools-20190911.d06fa8af/gocd/checkers.opensuse.gocd.yaml   
    2019-09-11 12:46:39.000000000 +0200
+++ 
new/openSUSE-release-tools-20190913.9d119079/gocd/checkers.opensuse.gocd.yaml   
    2019-09-13 18:24:23.000000000 +0200
@@ -127,27 +127,6 @@
             - staging-bot
             tasks:
             - script: ./leaper.py -A https://api.opensuse.org --verbose 
project openSUSE:Maintenance maintenance_incident
-  PackageHub15SP1.Manager42:
-    group: openSUSE.Checkers
-    lock_behavior: unlockWhenFinished
-    timer:
-      spec: 0 */30 * ? * *
-    environment_variables:
-      OSC_CONFIG: /home/go/config/oscrc-leaper
-    materials:
-      git:
-        git: https://github.com/openSUSE/openSUSE-release-tools.git
-    stages:
-    - Run:
-        approval: manual
-        jobs:
-          Run:
-            timeout: 0
-            resources:
-            - staging-bot
-            tasks:
-            - script: ./manager_42.py -A https://api.opensuse.org -c 
config/manager_42/openSUSE:Backports:SLE-15-SP1.yml
-
   Factory.Staging.Report:
     group: openSUSE.Checkers
     lock_behavior: unlockWhenFinished
@@ -256,7 +235,7 @@
       script:
         git: https://github.com/openSUSE/openSUSE-release-tools.git
     timer:
-      spec: 0 0 7 ? * *
+      spec: 0 0 7 ? * SUN
       only_on_changes: false
     stages:
     - Run:
@@ -269,6 +248,5 @@
               mkdir -p $tempdir/.osc-plugins
               ln -s $PWD/osc-origin.py $tempdir/.osc-plugins
               ln -s $PWD/osclib $tempdir/.osc-plugins
-              HOME=$tempdir osc -A https://api.opensuse.org origin -p 
openSUSE:Leap:15.2 update
-              HOME=$tempdir osc -A https://api.opensuse.org origin -p 
openSUSE:Leap:15.2:NonFree update
+              HOME=$tempdir osc -A https://api.opensuse.org origin update
               rm -rf $tempdir
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/gocd/checkers.suse.gocd.yaml 
new/openSUSE-release-tools-20190913.9d119079/gocd/checkers.suse.gocd.yaml
--- old/openSUSE-release-tools-20190911.d06fa8af/gocd/checkers.suse.gocd.yaml   
2019-09-11 12:46:39.000000000 +0200
+++ new/openSUSE-release-tools-20190913.9d119079/gocd/checkers.suse.gocd.yaml   
2019-09-13 18:24:23.000000000 +0200
@@ -95,7 +95,7 @@
       script:
         git: https://github.com/openSUSE/openSUSE-release-tools.git
     timer:
-      spec: 0 0 7 ? * *
+      spec: 0 0 7 ? * SUN
       only_on_changes: false
     stages:
     - Run:
@@ -108,7 +108,7 @@
               mkdir -p $tempdir/.osc-plugins
               ln -s $PWD/osc-origin.py $tempdir/.osc-plugins
               ln -s $PWD/osclib $tempdir/.osc-plugins
-              HOME=$tempdir osc -A https://api.suse.de origin -p 
SUSE:SLE-15-SP2:GA update
+              HOME=$tempdir osc -A https://api.suse.de origin update
               rm -rf $tempdir
   S15.SP2.Staging.Bot.Regular:
     group: SLE.Checkers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/gocd/checkers.suse.gocd.yaml.erb 
new/openSUSE-release-tools-20190913.9d119079/gocd/checkers.suse.gocd.yaml.erb
--- 
old/openSUSE-release-tools-20190911.d06fa8af/gocd/checkers.suse.gocd.yaml.erb   
    2019-09-11 12:46:39.000000000 +0200
+++ 
new/openSUSE-release-tools-20190913.9d119079/gocd/checkers.suse.gocd.yaml.erb   
    2019-09-13 18:24:23.000000000 +0200
@@ -95,7 +95,7 @@
       script:
         git: https://github.com/openSUSE/openSUSE-release-tools.git
     timer:
-      spec: 0 0 7 ? * *
+      spec: 0 0 7 ? * SUN
       only_on_changes: false
     stages:
     - Run:
@@ -108,7 +108,7 @@
               mkdir -p $tempdir/.osc-plugins
               ln -s $PWD/osc-origin.py $tempdir/.osc-plugins
               ln -s $PWD/osclib $tempdir/.osc-plugins
-              HOME=$tempdir osc -A https://api.suse.de origin -p 
SUSE:SLE-15-SP2:GA update
+              HOME=$tempdir osc -A https://api.suse.de origin update
               rm -rf $tempdir
   S15.SP2.Staging.Bot.Regular:
     group: SLE.Checkers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/gocd/monitors.gocd.yaml 
new/openSUSE-release-tools-20190913.9d119079/gocd/monitors.gocd.yaml
--- old/openSUSE-release-tools-20190911.d06fa8af/gocd/monitors.gocd.yaml        
2019-09-11 12:46:39.000000000 +0200
+++ new/openSUSE-release-tools-20190913.9d119079/gocd/monitors.gocd.yaml        
2019-09-13 18:24:23.000000000 +0200
@@ -20,9 +20,10 @@
           Run:
             timeout: 0
             resources:
-            - staging-bot
+            - monitor
             tasks:
             - script: |-
+                install -D /home/go/config/openqa-client.conf 
/home/go/.config/openqa/client.conf
                 export PYTHONPATH=$PWD/scripts
                 ./scripts/gocd/rabbit-openqa.py -A https://api.opensuse.org
   SUSE.openQA:
@@ -45,10 +46,11 @@
           Run:
             timeout: 0
             resources:
-            - staging-bot
+            - monitor
             tasks:
             # endless loop
             - script: |-
+                install -D /home/go/config/openqa-client.conf 
/home/go/.config/openqa/client.conf
                 export PYTHONPATH=$PWD/scripts
                 ./scripts/gocd/rabbit-openqa.py -A https://api.suse.de
   SUSE.Repo.Monitor:
@@ -71,7 +73,7 @@
           Run:
             timeout: 0
             resources:
-            - staging-bot
+            - monitor
             tasks:
             # endless loop
             - script: |-
@@ -101,7 +103,7 @@
           Run:
             timeout: 0
             resources:
-            - staging-bot
+            - monitor
             tasks:
             # endless loop
             - script: |-
@@ -111,3 +113,57 @@
                 git clone git://botmaster.suse.de/opensuse-repos.git
                 cd opensuse-repos
                 ../scripts/gocd/rabbit-repoid.py -A https://api.opensuse.org 
openSUSE:Factory openSUSE:Leap
+  openSUSE.OriginManagerUpdate:
+    group: Monitors
+    lock_behavior: unlockWhenFinished
+    timer:
+      spec: 0 */5 * ? * *
+    environment_variables:
+      OSC_CONFIG: /home/go/config/oscrc-origin-manager
+    materials:
+      script:
+        git: https://github.com/openSUSE/openSUSE-release-tools.git
+    stages:
+    - Run:
+        approval:
+          type: manual
+        jobs:
+          Run:
+            timeout: 0
+            resources:
+            - monitor
+            tasks:
+            - script: |-
+                tempdir=$(mktemp -d)
+                mkdir -p $tempdir/.osc-plugins
+                ln -s $PWD/osc-origin.py $tempdir/.osc-plugins
+                ln -s $PWD/osclib $tempdir/.osc-plugins
+                HOME=$tempdir osc -A https://api.opensuse.org origin update 
--listen --listen-seconds 43200
+                rm -rf $tempdir
+  SUSE.OriginManagerUpdate:
+    group: Monitors
+    lock_behavior: unlockWhenFinished
+    timer:
+      spec: 0 */5 * ? * *
+    environment_variables:
+      OSC_CONFIG: /home/go/config/oscrc-origin-manager
+    materials:
+      script:
+        git: https://github.com/openSUSE/openSUSE-release-tools.git
+    stages:
+    - Run:
+        approval:
+          type: manual
+        jobs:
+          Run:
+            timeout: 0
+            resources:
+            - monitor
+            tasks:
+            - script: |-
+                tempdir=$(mktemp -d)
+                mkdir -p $tempdir/.osc-plugins
+                ln -s $PWD/osc-origin.py $tempdir/.osc-plugins
+                ln -s $PWD/osclib $tempdir/.osc-plugins
+                HOME=$tempdir osc -A https://api.suse.de origin update 
--listen --listen-seconds 43200
+                rm -rf $tempdir
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/gocd/rabbit-openqa.py 
new/openSUSE-release-tools-20190913.9d119079/gocd/rabbit-openqa.py
--- old/openSUSE-release-tools-20190911.d06fa8af/gocd/rabbit-openqa.py  
2019-09-11 12:46:39.000000000 +0200
+++ new/openSUSE-release-tools-20190913.9d119079/gocd/rabbit-openqa.py  
2019-09-13 18:24:23.000000000 +0200
@@ -19,7 +19,7 @@
 from urllib.parse import quote_plus
 
 import requests
-from PubSubConsumer import PubSubConsumer
+from osclib.PubSubConsumer import PubSubConsumer
 
 class Project(object):
     def __init__(self, name):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/gocd/rabbit-repoid.py 
new/openSUSE-release-tools-20190913.9d119079/gocd/rabbit-repoid.py
--- old/openSUSE-release-tools-20190911.d06fa8af/gocd/rabbit-repoid.py  
2019-09-11 12:46:39.000000000 +0200
+++ new/openSUSE-release-tools-20190913.9d119079/gocd/rabbit-repoid.py  
2019-09-13 18:24:23.000000000 +0200
@@ -15,7 +15,7 @@
 from lxml import etree as ET
 
 from urllib.error import HTTPError
-from PubSubConsumer import PubSubConsumer
+from osclib.PubSubConsumer import PubSubConsumer
 
 class Listener(PubSubConsumer):
     def __init__(self, apiurl, amqp_prefix, namespaces):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/osc-origin.py 
new/openSUSE-release-tools-20190913.9d119079/osc-origin.py
--- old/openSUSE-release-tools-20190911.d06fa8af/osc-origin.py  2019-09-11 
12:46:39.000000000 +0200
+++ new/openSUSE-release-tools-20190913.9d119079/osc-origin.py  2019-09-13 
18:24:23.000000000 +0200
@@ -19,14 +19,13 @@
 from osclib.origin import origin_history
 from osclib.origin import origin_potentials
 from osclib.origin import origin_revision_state
+from osclib.origin import origin_updatable
 from osclib.origin import origin_update
 from osclib.sentry import sentry_init
 from osclib.util import mail_send
 from shutil import copyfile
 import sys
 import time
-import traceback
-from urllib.error import HTTPError
 import yaml
 
 OSRT_ORIGIN_LOOKUP_TTL = 60 * 60 * 24 * 7
@@ -36,6 +35,8 @@
 @cmdln.option('--dry', action='store_true', help='perform a dry-run where 
applicable')
 @cmdln.option('--force-refresh', action='store_true', help='force refresh of 
data')
 @cmdln.option('--format', default='plain', help='output format')
[email protected]('--listen', action='store_true', help='listen to events')
[email protected]('--listen-seconds', help='number of seconds to listen to events')
 @cmdln.option('--mail', action='store_true', help='mail report to 
<confg:mail-release-list>')
 @cmdln.option('--origins-only', action='store_true', help='list origins 
instead of expanded config')
 @cmdln.option('-p', '--project', help='project on which to operate')
@@ -63,7 +64,7 @@
         osc origin potentials [--format json|yaml] PACKAGE
         osc origin projects [--format json|yaml]
         osc origin report [--diff] [--force-refresh] [--mail]
-        osc origin update [PACKAGE...]
+        osc origin update [--listen] [--listen-seconds] [PACKAGE...]
     """
 
     if len(args) == 0:
@@ -84,7 +85,7 @@
 
     Cache.init()
     apiurl = self.get_api_url()
-    if command not in ['cron', 'projects']:
+    if command not in ['cron', 'projects', 'update']:
         if not opts.project:
             raise oscerr.WrongArgs('A project must be indicated.')
         config = config_load(apiurl, opts.project)
@@ -345,28 +346,34 @@
                   body, None, dry=opts.dry)
 
 def osrt_origin_update(apiurl, opts, *packages):
+    if opts.listen:
+        import logging
+        from osclib.origin_listener import OriginSourceChangeListener
+
+        logger = logging.getLogger()
+        logger.setLevel(logging.INFO)
+        listener = OriginSourceChangeListener(apiurl, logger, opts.project, 
opts.dry)
+        try:
+            runtime = int(opts.listen_seconds) if opts.listen_seconds else None
+            listener.run(runtime=runtime)
+        except KeyboardInterrupt:
+            listener.stop()
+
+        return
+
+    if not opts.project:
+        for project in origin_updatable(apiurl):
+            opts.project = project
+            osrt_origin_update(apiurl, opts, *packages)
+
+        return
+
     if len(packages) == 0:
         packages = package_list_kind_filtered(apiurl, opts.project)
 
-    return_value = 0
     for package in packages:
-        print('checking for updates to {}...'.format(package))
+        print('checking for updates to {}/{}...'.format(opts.project, package))
 
         request_future = origin_update(apiurl, opts.project, package)
-        if not request_future:
-            continue
-
-        print(request_future)
-        if opts.dry:
-            continue
-
-        try:
-            request_id = request_future.create()
-            if request_id:
-                print('-> created request {}'.format(request_id))
-        except HTTPError:
-            return_value = 1
-            traceback.print_exc()
-
-    if return_value != 0:
-        sys.exit(return_value)
+        if request_future:
+            request_future.print_and_create(opts.dry)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/osclib/PubSubConsumer.py 
new/openSUSE-release-tools-20190913.9d119079/osclib/PubSubConsumer.py
--- old/openSUSE-release-tools-20190911.d06fa8af/osclib/PubSubConsumer.py       
1970-01-01 01:00:00.000000000 +0100
+++ new/openSUSE-release-tools-20190913.9d119079/osclib/PubSubConsumer.py       
2019-09-13 18:24:23.000000000 +0200
@@ -0,0 +1,397 @@
+import functools
+import logging
+import pika
+import ssl
+import sys
+import time
+from datetime import datetime
+
+class PubSubConsumer(object):
+    """
+    Based on 
https://github.com/pika/pika/blob/master/examples/asynchronous_consumer_example.py
+
+    This is an example consumer that will handle unexpected interactions
+    with RabbitMQ such as channel and connection closures.
+
+    If RabbitMQ closes the connection, it will reopen it. You should
+    look at the output, as there are limited reasons why the connection may
+    be closed, which usually are tied to permission related issues or
+    socket timeouts.
+
+    If the channel is closed, it will indicate a problem with one of the
+    commands that were issued and that should surface in the output as well.
+
+    """
+
+    def __init__(self, amqp_prefix, logger):
+        """Create a new instance of the consumer class, passing in the AMQP
+        URL used to connect to RabbitMQ.
+
+        :param str amqp_url: The AMQP url to connect with
+
+        """
+        self._connection = None
+        self._channel = None
+        self._closing = False
+        self._consuming = False
+        self._consumer_tag = None
+        self._prefix = amqp_prefix
+        self._timer_id = None
+        self._run_until = None
+        self.logger = logger
+
+    def restart_timer(self):
+        interval = 300
+        if self._timer_id:
+            self._connection.ioloop.remove_timeout(self._timer_id)
+        else:
+            # check the initial state on first timer hit
+            # so be quick about it
+            interval = 0
+        self._timer_id = self._connection.ioloop.call_later(interval, 
self.still_alive)
+
+    def still_alive(self):
+        # output something so gocd doesn't consider it stalled
+        self.logger.info('Still alive: {}'.format(datetime.now().time()))
+        if self._run_until and time.time() > self._run_until:
+            self.stop()
+        else:
+            self.restart_timer()
+
+    def connect(self):
+        """This method connects to RabbitMQ, returning the connection handle.
+        When the connection is established, the on_connection_open method
+        will be invoked by pika.
+
+        :rtype: pika.SelectConnection
+
+        """
+        self.logger.info('Connecting to %s', self._prefix)
+        account = 'opensuse'
+        server = 'rabbit.opensuse.org'
+        if self._prefix == 'suse':
+            account = 'suse'
+            server = 'rabbit.suse.de'
+        credentials = pika.PlainCredentials(account, account)
+        context = ssl.create_default_context()
+        ssl_options = pika.SSLOptions(context, server)
+        parameters = pika.ConnectionParameters(server, 5671, '/', credentials, 
ssl_options=ssl_options, socket_timeout=10)
+        return pika.SelectConnection(parameters,
+                                     on_open_callback=self.on_connection_open)
+
+    def close_connection(self):
+        """This method closes the connection to RabbitMQ."""
+        self._consuming = False
+        if self._connection.is_closing or self._connection.is_closed:
+            self.logger.info('Connection is closing or already closed')
+        else:
+            self.logger.info('Closing connection')
+            self._connection.close()
+
+    def add_on_connection_close_callback(self):
+        """This method adds an on close callback that will be invoked by pika
+        when RabbitMQ closes the connection to the publisher unexpectedly.
+
+        """
+        self.logger.debug('Adding connection close callback')
+        self._connection.add_on_close_callback(self.on_connection_closed)
+
+    def on_connection_closed(self, connection, reason):
+        """This method is invoked by pika when the connection to RabbitMQ is
+        closed unexpectedly. Since it is unexpected, we will reconnect to
+        RabbitMQ if it disconnects.
+
+        :param pika.connection.Connection connection: The closed connection obj
+        :param int reply_code: The server provided reply_code if given
+        :param str reply_text: The server provided reply_text if given
+
+        """
+        self._channel = None
+        if self._closing:
+            self._connection.ioloop.stop()
+        else:
+            self.logger.warning('Connection closed, reopening in 5 seconds: 
%s',
+                                reason)
+            self._connection.ioloop.call_later(5, self.reconnect)
+
+    def on_connection_open(self, unused_connection):
+        """This method is called by pika once the connection to RabbitMQ has
+        been established. It passes the handle to the connection object in
+        case we need it, but in this case, we'll just mark it unused.
+
+        :type unused_connection: pika.SelectConnection
+
+        """
+        self.logger.info('Connection opened')
+        self.add_on_connection_close_callback()
+        self.open_channel()
+
+    def reconnect(self):
+        """Will be invoked by the IOLoop timer if the connection is
+        closed. See the on_connection_closed method.
+
+        """
+        # This is the old connection IOLoop instance, stop its ioloop
+        self._connection.ioloop.stop()
+
+        if not self._closing:
+
+            # Create a new connection
+            self._connection = self.connect()
+
+            # There is now a new connection, needs a new ioloop to run
+            self._connection.ioloop.start()
+
+    def add_on_channel_close_callback(self):
+        """This method tells pika to call the on_channel_closed method if
+        RabbitMQ unexpectedly closes the channel.
+
+        """
+        self.logger.debug('Adding channel close callback')
+        self._channel.add_on_close_callback(self.on_channel_closed)
+
+    def on_channel_closed(self, channel, reason):
+        """Invoked by pika when RabbitMQ unexpectedly closes the channel.
+        Channels are usually closed if you attempt to do something that
+        violates the protocol, such as re-declare an exchange or queue with
+        different parameters. In this case, we'll close the connection
+        to shutdown the object.
+
+        :param pika.channel.Channel: The closed channel
+        :param int reply_code: The numeric reason the channel was closed
+        :param str reply_text: The text reason the channel was closed
+
+        """
+        self.logger.info('Channel %i was closed: %s',
+                            channel, reason)
+        self._connection.close()
+
+    def on_channel_open(self, channel):
+        """This method is invoked by pika when the channel has been opened.
+        The channel object is passed in so we can make use of it.
+
+        Since the channel is now open, we'll declare the exchange to use.
+
+        :param pika.channel.Channel channel: The channel object
+
+        """
+        self.logger.debug('Channel opened')
+        self._channel = channel
+        self.add_on_channel_close_callback()
+        self.setup_exchange('pubsub')
+
+    def setup_exchange(self, exchange_name):
+        """Setup the exchange on RabbitMQ by invoking the Exchange.Declare RPC
+        command. When it is complete, the on_exchange_declareok method will
+        be invoked by pika.
+
+        :param str|unicode exchange_name: The name of the exchange to declare
+
+        """
+        self.logger.debug('Declaring exchange %s', exchange_name)
+        self._channel.exchange_declare(exchange_name,
+                                       exchange_type='topic',
+                                       callback=self.on_exchange_declareok,
+                                       passive=True, durable=True)
+
+    def on_exchange_declareok(self, unused_frame):
+        """Invoked by pika when RabbitMQ has finished the Exchange.Declare RPC
+        command.
+
+        :param pika.Frame.Method unused_frame: Exchange.DeclareOk response 
frame
+
+        """
+        self.logger.debug('Exchange declared')
+        self._channel.queue_declare('', callback=self.on_queue_declareok, 
exclusive=True)
+
+    def on_queue_declareok(self, method_frame):
+        """Method invoked by pika when the Queue.Declare RPC call made in
+        setup_queue has completed. In this method we will bind the queue
+        and exchange together with the routing key by issuing the Queue.Bind
+        RPC command. When this command is complete, the on_bindok method will
+        be invoked by pika.
+
+        :param pika.frame.Method method_frame: The Queue.DeclareOk frame
+
+        """
+        self.queue_name = method_frame.method.queue
+        self.routing_keys_to_bind = self.routing_keys()
+        self.bind_queue_to_routing_key(self.routing_keys_to_bind.pop())
+
+    def routing_keys(self):
+        return ['#']
+
+    def bind_queue_to_routing_key(self, key):
+        self.logger.info('Binding %s to %s', key, self.queue_name)
+        self._channel.queue_bind(self.queue_name, 'pubsub', routing_key=key, 
callback=self.on_bindok)
+
+    def add_on_cancel_callback(self):
+        """Add a callback that will be invoked if RabbitMQ cancels the consumer
+        for some reason. If RabbitMQ does cancel the consumer,
+        on_consumer_cancelled will be invoked by pika.
+
+        """
+        self.logger.debug('Adding consumer cancellation callback')
+        self._channel.add_on_cancel_callback(self.on_consumer_cancelled)
+
+    def on_consumer_cancelled(self, method_frame):
+        """Invoked by pika when RabbitMQ sends a Basic.Cancel for a consumer
+        receiving messages.
+
+        :param pika.frame.Method method_frame: The Basic.Cancel frame
+
+        """
+        self.logger.info('Consumer was cancelled remotely, shutting down: %r',
+                         method_frame)
+        if self._channel:
+            self._channel.close()
+
+    def on_message(self, unused_channel, basic_deliver, properties, body):
+        """Invoked by pika when a message is delivered from RabbitMQ. The
+        channel is passed for your convenience. The basic_deliver object that
+        is passed in carries the exchange, routing key, delivery tag and
+        a redelivered flag for the message. The properties passed in is an
+        instance of BasicProperties with the message properties and the body
+        is the message that was sent.
+
+        :param pika.channel.Channel unused_channel: The channel object
+        :param pika.Spec.Basic.Deliver: basic_deliver method
+        :param pika.Spec.BasicProperties: properties
+        :param str|unicode body: The message body
+
+        """
+        self.logger.info('Received message # %s: %s %s',
+                         basic_deliver.delivery_tag, 
basic_deliver.routing_key, body)
+        self.acknowledge_message(basic_deliver.delivery_tag)
+
+    def acknowledge_message(self, delivery_tag):
+        """Acknowledge the message delivery from RabbitMQ by sending a
+        Basic.Ack RPC method for the delivery tag.
+
+        :param int delivery_tag: The delivery tag from the Basic.Deliver frame
+
+        """
+        self.logger.debug('Acknowledging message %s', delivery_tag)
+        self._channel.basic_ack(delivery_tag)
+
+    def on_cancelok(self, _unused_frame, userdata):
+        """This method is invoked by pika when RabbitMQ acknowledges the
+        cancellation of a consumer. At this point we will close the channel.
+        This will invoke the on_channel_closed method once the channel has been
+        closed, which will in-turn close the connection.
+        :param pika.frame.Method _unused_frame: The Basic.CancelOk frame
+        :param str|unicode userdata: Extra user data (consumer tag)
+        """
+        self._consuming = False
+        self.logger.debug(
+            'RabbitMQ acknowledged the cancellation of the consumer: %s',
+            userdata)
+        self.close_channel()
+
+    def close_channel(self):
+        """Call to close the channel with RabbitMQ cleanly by issuing the
+        Channel.Close RPC command.
+        """
+        self.logger.debug('Closing the channel')
+        self._channel.close()
+
+    def stop_consuming(self):
+        """Tell RabbitMQ that you would like to stop consuming by sending the
+        Basic.Cancel RPC command.
+
+        """
+        if self._channel:
+            self.logger.debug('Sending a Basic.Cancel RPC command to RabbitMQ')
+            cb = functools.partial(self.on_cancelok, 
userdata=self._consumer_tag)
+            self._channel.basic_cancel(self._consumer_tag, cb)
+
+    def start_consuming(self):
+        """This method sets up the consumer by first calling
+        add_on_cancel_callback so that the object is notified if RabbitMQ
+        cancels the consumer. It then issues the Basic.Consume RPC command
+        which returns the consumer tag that is used to uniquely identify the
+        consumer with RabbitMQ. We keep the value to use it when we want to
+        cancel consuming. The on_message method is passed in as a callback pika
+        will invoke when a message is fully received.
+
+        """
+        self.logger.debug('Issuing consumer related RPC commands')
+        self.add_on_cancel_callback()
+        self.restart_timer()
+        self._consumer_tag = self._channel.basic_consume(self.queue_name,
+                                                         self.on_message,
+                                                         auto_ack=False)
+        self._consuming = True
+
+    def on_bindok(self, unused_frame):
+        """Invoked by pika when the Queue.Bind method has completed. At this
+        point we will start consuming messages by calling start_consuming
+        which will invoke the needed RPC commands to start the process.
+
+        :param pika.frame.Method unused_frame: The Queue.BindOk response frame
+
+        """
+        self.logger.debug('Queue bound')
+        if len(self.routing_keys_to_bind):
+            self.bind_queue_to_routing_key(self.routing_keys_to_bind.pop())
+        else:
+            self.start_consuming()
+
+    def open_channel(self):
+        """Open a new channel with RabbitMQ by issuing the Channel.Open RPC
+        command. When RabbitMQ responds that the channel is open, the
+        on_channel_open callback will be invoked by pika.
+
+        """
+        self.logger.debug('Creating a new channel')
+        self._connection.channel(on_open_callback=self.on_channel_open)
+
+    def run(self, runtime=None):
+        """Run the example consumer by connecting to RabbitMQ and then
+        starting the IOLoop to block and allow the SelectConnection to operate.
+
+        """
+        if runtime:
+            self._run_until = time.time() + runtime
+        self._connection = self.connect()
+        self._connection.ioloop.start()
+
+    def stop(self):
+        """Cleanly shutdown the connection to RabbitMQ by stopping the consumer
+        with RabbitMQ. When RabbitMQ confirms the cancellation, on_cancelok
+        will be invoked by pika, which will then closing the channel and
+        connection. The IOLoop is started again because this method is invoked
+        when CTRL-C is pressed raising a KeyboardInterrupt exception. This
+        exception stops the IOLoop which needs to be running for pika to
+        communicate with RabbitMQ. All of the commands issued prior to starting
+        the IOLoop will be buffered but not processed.
+
+        """
+        self.logger.debug('Stopping')
+        if not self._closing:
+            self._closing = True
+            self.logger.debug('Stopping')
+            if self._consuming:
+                self.stop_consuming()
+                #self._connection.ioloop.start()
+            else:
+                self._connection.ioloop.stop()
+            self.logger.debug('Stopped')
+
+def main():
+    LOG_FORMAT = ('%(levelname) -10s %(asctime)s %(name) -30s %(funcName) '
+                  '-35s %(lineno) -5d: %(message)s')
+
+    logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
+    amqp_prefix = 'opensuse'
+    if len(sys.argv) > 1:
+        amqp_prefix = sys.argv[1]
+    example = PubSubConsumer(amqp_prefix, logging.getLogger(__name__))
+    try:
+        example.run()
+    except KeyboardInterrupt:
+        example.stop()
+
+
+if __name__ == '__main__':
+    main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/osclib/core.py 
new/openSUSE-release-tools-20190913.9d119079/osclib/core.py
--- old/openSUSE-release-tools-20190911.d06fa8af/osclib/core.py 2019-09-11 
12:46:39.000000000 +0200
+++ new/openSUSE-release-tools-20190913.9d119079/osclib/core.py 2019-09-13 
18:24:23.000000000 +0200
@@ -32,6 +32,7 @@
 from osclib.conf import Config
 from osclib.memoize import memoize
 import subprocess
+import traceback
 
 BINARY_REGEX = 
r'(?:.*::)?(?P<filename>(?P<name>.*)-(?P<version>[^-]+)-(?P<release>[^-]+)\.(?P<arch>[^-\.]+))'
 RPM_REGEX = BINARY_REGEX + r'\.rpm'
@@ -204,6 +205,17 @@
 
     return project, package
 
+@memoize(session=True)
+def devel_projects(apiurl, project):
+    devel_projects = set()
+
+    root = search(apiurl, 'package', "@project='{}' and 
devel/@project!=''".format(project))
+    for devel_project in root.xpath('package/devel/@project'):
+        if devel_project != project:
+            devel_projects.add(devel_project)
+
+    return sorted(devel_projects)
+
 def request_age(request):
     if isinstance(request, Request):
         created = request.statehistory[0].when
@@ -652,14 +664,28 @@
 
     return str(root.xpath('(//version)[last()]/text()')[0])
 
-def project_attribute_list(apiurl, attribute, value=None):
+def project_attribute_list(apiurl, attribute, locked=None):
     xpath = 'attribute/@name="{}"'.format(attribute)
-    if value is not None:
-        xpath += '="{}"'.format(value)
-
     root = search(apiurl, 'project', xpath)
-    for project in root.findall('project'):
-        yield project.get('name')
+    for project in root.xpath('project/@name'):
+        # Locked not exposed via OBS xpath engine.
+        if locked is not None and project_locked(apiurl, project) != locked:
+            continue
+
+        yield project
+
+# OBS xpath engine does not support multiple attribute queries nor negation. As
+# such both must be done client-side.
+def project_attributes_list(apiurl, attributes, attributes_not=None, 
locked=None):
+    projects = set()
+
+    for attribute in attributes:
+        projects.update(project_attribute_list(apiurl, attribute, locked))
+
+    for attribute in attributes_not:
+        projects.difference_update(project_attribute_list(apiurl, attribute, 
locked))
+
+    return list(projects)
 
 @memoize(session=True)
 def project_remote_list(apiurl):
@@ -976,6 +1002,23 @@
     def create(self):
         return self.create_function()
 
+    def create_tolerant(self):
+        try:
+            return self.create()
+        except HTTPError:
+            traceback.print_exc()
+
+        return False
+
+    def print_and_create(self, dry=False):
+        if dry:
+            print(self)
+            return None
+
+        request_id = self.create_tolerant()
+        print('{} = {}'.format(self, request_id))
+        return request_id
+
     def __str__(self):
         return self.description
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/osclib/origin.py 
new/openSUSE-release-tools-20190913.9d119079/osclib/origin.py
--- old/openSUSE-release-tools-20190911.d06fa8af/osclib/origin.py       
2019-09-11 12:46:39.000000000 +0200
+++ new/openSUSE-release-tools-20190913.9d119079/osclib/origin.py       
2019-09-13 18:24:23.000000000 +0200
@@ -5,10 +5,12 @@
 from osclib.conf import Config
 from osclib.core import attribute_value_load
 from osclib.core import devel_project_get
+from osclib.core import devel_projects
 from osclib.core import entity_exists
 from osclib.core import package_source_hash
 from osclib.core import package_source_hash_history
 from osclib.core import package_version
+from osclib.core import project_attributes_list
 from osclib.core import project_remote_apiurl
 from osclib.core import request_action_key
 from osclib.core import request_action_list_source
@@ -629,3 +631,47 @@
                                      target_project, package, message=message, 
revision=action.src_rev)
 
     return False
+
+@memoize(session=True)
+def origin_updatable(apiurl):
+    """ List of origin managed projects that can be updated. """
+    projects = project_attributes_list(apiurl, [
+        'OSRT:OriginConfig',
+    ], [
+        'OBS:Maintained', # Submitting maintenance updates not currently 
supported.
+        'OSRT:OriginUpdateSkip',
+    ], locked=False)
+
+    for project in projects:
+        updatable = False
+
+        # Look for at least one origin that allows automatic updates.
+        config = config_load(apiurl, project)
+        for origin, values in config_origin_generator(config['origins'], 
skip_workarounds=True):
+            if values['automatic_updates']:
+                updatable = True
+                break
+
+        if not updatable:
+            projects.remove(project)
+
+    return projects
+
+@memoize(session=True)
+def origin_updatable_map(apiurl, pending=None):
+    origins = {}
+    for project in origin_updatable(apiurl):
+        config = config_load(apiurl, project)
+        for origin, values in config_origin_generator(config['origins'], 
skip_workarounds=True):
+            if pending is not None and values['pending_submission_allow'] != 
pending:
+                continue
+
+            if origin == '<devel>':
+                for devel in devel_projects(apiurl, project):
+                    origins.setdefault(devel, set())
+                    origins[devel].add(project)
+            else:
+                origins.setdefault(origin, set())
+                origins[origin].add(project)
+
+    return origins
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20190911.d06fa8af/osclib/origin_listener.py 
new/openSUSE-release-tools-20190913.9d119079/osclib/origin_listener.py
--- old/openSUSE-release-tools-20190911.d06fa8af/osclib/origin_listener.py      
1970-01-01 01:00:00.000000000 +0100
+++ new/openSUSE-release-tools-20190913.9d119079/osclib/origin_listener.py      
2019-09-13 18:24:23.000000000 +0200
@@ -0,0 +1,139 @@
+import json
+from osclib.core import package_kind
+from osclib.core import project_remote_list
+from osclib.origin import origin_updatable_map
+from osclib.origin import origin_update
+from osclib.PubSubConsumer import PubSubConsumer
+import threading
+
+
+class OriginSourceChangeListener(PubSubConsumer):
+    def __init__(self, apiurl, logger, project=None, dry=False):
+        self.apiurl = apiurl
+        self.project = project
+        self.dry = dry
+        self.listeners = {}
+
+        amqp_prefix = 'suse' if self.apiurl.endswith('suse.de') else 'opensuse'
+        super().__init__(amqp_prefix, logger)
+
+    def run(self, runtime=None):
+        super().run(runtime=runtime)
+
+        for listener in self.listeners.values():
+            listener.run(runtime=runtime)
+
+    def stop(self):
+        super().stop()
+
+        for listener in self.listeners.values():
+            listener.stop()
+
+    def start_consuming(self):
+        super().start_consuming()
+
+        self.check_remotes()
+
+    def routing_keys(self):
+        return [self._prefix + k for k in [
+            '.obs.package.update',
+            '.obs.request.create',
+        ]]
+
+    def on_message(self, unused_channel, method, properties, body):
+        super().on_message(unused_channel, method, properties, body)
+
+        payload = json.loads(body)
+        if method.routing_key == '{}.obs.package.update'.format(self._prefix):
+            self.on_message_package_update(payload)
+        elif method.routing_key == 
'{}.obs.request.create'.format(self._prefix):
+            self.on_message_request_create(payload)
+        else:
+            raise Exception('Unrequested message: 
{}'.format(method.routing_key))
+
+    def on_message_package_update(self, payload):
+        origins = self.origin_updatable_map()
+        self.update_consider(origins, payload['project'], payload['package'])
+
+    def on_message_request_create(self, payload):
+        origins = self.origin_updatable_map(pending=True)
+        for action in payload['actions']:
+            # The following code demonstrates the quality of the data 
structure.
+            # The base structure is inconsistent enough and yet the event data
+            # structure manages to be different from XML structure (for no
+            # reason) and even more inconsistent at that.
+            if action['type'] == 'delete':
+                if not action.get('targetpackage'):
+                    continue
+
+                project = action['targetproject']
+                package = action['targetpackage']
+            elif action['type'] == 'maintenance_incident':
+                project = action['target_releaseproject']
+                if not action.get('targetpackage'):
+                    package = action['sourcepackage']
+                else:
+                    repository_suffix_length = len(project) + 1 # 
package.project
+                    package = 
action['targetpackage'][:-repository_suffix_length]
+            elif action['type'] == 'maintenance_release':
+                project = action['targetproject']
+                repository_suffix_length = len(project) + 1 # package.project
+                package = action['sourcepackage'][:-repository_suffix_length]
+            elif action['type'] == 'submit':
+                project = action['targetproject']
+                package = action['targetpackage']
+            else:
+                # Unsupported action type.
+                continue
+
+            self.update_consider(origins, project, package)
+
+    def check_remotes(self):
+        origins = self.origin_updatable_map()
+        remotes = project_remote_list(self.apiurl)
+        for remote, apiurl in remotes.items():
+            for origin in origins:
+                if origin.startswith(remote + ':') and apiurl not in 
self.listeners:
+                    self.logger.info('starting remote listener due to {} 
origin'.format(origin))
+                    self.listeners[apiurl] = 
OriginSourceChangeListenerRemote(apiurl, self, remote)
+                    threading.Thread(target=self.listeners[apiurl].run).start()
+
+    def origin_updatable_map(self, pending=None):
+        return origin_updatable_map(self.apiurl, pending=pending)
+
+    def update_consider(self, origins, origin_project, package):
+        if origin_project not in origins:
+            self.logger.info('skipped irrelevant origin: 
{}'.format(origin_project))
+            return
+
+        for project in origins[origin_project]:
+            if self.project and project != self.project:
+                self.logger.info('skipping filtered target project: 
{}'.format(project))
+                continue
+
+            kind = package_kind(self.apiurl, project, package)
+            if kind == 'source':
+                request_future = origin_update(self.apiurl, project, package)
+                if request_future:
+                    request_future.print_and_create(self.dry)
+            else:
+                # This eliminates the possibility for deletes by listener.
+                self.logger.info('skipped updating non-existant package 
{}/{}'.format(project, package))
+
+class OriginSourceChangeListenerRemote(OriginSourceChangeListener):
+    def __init__(self, apiurl, parent, prefix):
+        self.parent = parent
+        self.prefix = prefix
+
+        super().__init__(apiurl, self.parent.logger)
+        self._run_until = self.parent._run_until
+
+    def check_remotes(self):
+        pass
+
+    def origin_updatable_map(self, pending=None):
+        return self.parent.origin_updatable_map(pending=pending)
+
+    def update_consider(self, origins, origin_project, package):
+        origin_project = '{}:{}'.format(self.prefix, origin_project)
+        self.parent.update_consider(origins, origin_project, package)

++++++ openSUSE-release-tools.obsinfo ++++++
--- /var/tmp/diff_new_pack.Eytaky/_old  2019-09-16 10:52:14.027158000 +0200
+++ /var/tmp/diff_new_pack.Eytaky/_new  2019-09-16 10:52:14.027158000 +0200
@@ -1,5 +1,5 @@
 name: openSUSE-release-tools
-version: 20190911.d06fa8af
-mtime: 1568198799
-commit: d06fa8afb3cacc877feaf99e1698afdc363910ee
+version: 20190913.9d119079
+mtime: 1568391863
+commit: 9d119079582b04b205b4f6e8d500262a8feee07f
 


Reply via email to