Title: [287045] trunk/Tools
Revision
287045
Author
[email protected]
Date
2021-12-14 13:31:59 -0800 (Tue, 14 Dec 2021)

Log Message

[reporelaypy] Update checkout with hook instead of polling
https://bugs.webkit.org/show_bug.cgi?id=234243
<rdar://problem/86413065>

Reviewed by Dewei Zhu.

* Tools/Scripts/libraries/reporelaypy/reporelaypy/__init__.py: Bump version,
export HookProcessor and HookReceiver.
* Tools/Scripts/libraries/reporelaypy/reporelaypy/checkout.py:
(Checkout.update_for): If a branch is new, we need to track it.
* Tools/Scripts/libraries/reporelaypy/reporelaypy/database.py:
(Database.__init__): Use self.host and self.password.
* Tools/Scripts/libraries/reporelaypy/reporelaypy/hooks.py: Added.
(HookProcessor): Class to process received hooks.
(HookReceiver): Class to receive hooks and queue them for processing.
* Tools/Scripts/libraries/reporelaypy/reporelaypy/tests/hooks_unittest.py: Added.
(HooksUnittest):
(HooksUnittest.setUp):
(HooksUnittest.test_receive):
(HooksUnittest.test_invalid):
(HooksUnittest.test_process):
(HooksUnittest.test_hmac):
(HooksUnittest.test_invalid_hmac):
* Tools/Scripts/libraries/reporelaypy/reporelaypy/webserver.py: Add hook routes,
if hooks are enabled.
* Tools/Scripts/libraries/reporelaypy/run: Allow caller to enable hooks.
* Tools/Scripts/libraries/reporelaypy/setup.py: Bump version.

Canonical link: https://commits.webkit.org/245242@main

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (287044 => 287045)


--- trunk/Tools/ChangeLog	2021-12-14 21:16:48 UTC (rev 287044)
+++ trunk/Tools/ChangeLog	2021-12-14 21:31:59 UTC (rev 287045)
@@ -1,3 +1,33 @@
+2021-12-13  Jonathan Bedard  <[email protected]>
+
+        [reporelaypy] Update checkout with hook instead of polling
+        https://bugs.webkit.org/show_bug.cgi?id=234243
+        <rdar://problem/86413065>
+
+        Reviewed by Dewei Zhu.
+
+        * Scripts/libraries/reporelaypy/reporelaypy/__init__.py: Bump version,
+        export HookProcessor and HookReceiver.
+        * Scripts/libraries/reporelaypy/reporelaypy/checkout.py:
+        (Checkout.update_for): If a branch is new, we need to track it.
+        * Scripts/libraries/reporelaypy/reporelaypy/database.py:
+        (Database.__init__): Use self.host and self.password.
+        * Scripts/libraries/reporelaypy/reporelaypy/hooks.py: Added.
+        (HookProcessor): Class to process received hooks.
+        (HookReceiver): Class to receive hooks and queue them for processing.
+        * Scripts/libraries/reporelaypy/reporelaypy/tests/hooks_unittest.py: Added.
+        (HooksUnittest):
+        (HooksUnittest.setUp):
+        (HooksUnittest.test_receive):
+        (HooksUnittest.test_invalid):
+        (HooksUnittest.test_process):
+        (HooksUnittest.test_hmac):
+        (HooksUnittest.test_invalid_hmac):
+        * Scripts/libraries/reporelaypy/reporelaypy/webserver.py: Add hook routes,
+        if hooks are enabled.
+        * Scripts/libraries/reporelaypy/run: Allow caller to enable hooks.
+        * Scripts/libraries/reporelaypy/setup.py: Bump version.
+
 2021-12-14  Alex Christensen  <[email protected]>
 
         Add _WKContentRuleListAction.redirected and .modifiedHeaders

Modified: trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/__init__.py (287044 => 287045)


--- trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/__init__.py	2021-12-14 21:16:48 UTC (rev 287044)
+++ trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/__init__.py	2021-12-14 21:31:59 UTC (rev 287045)
@@ -44,7 +44,7 @@
         "Please install webkitcorepy with `pip install webkitcorepy --extra-index-url <package index URL>`"
     )
 
-version = Version(0, 2, 0)
+version = Version(0, 3, 0)
 
 import webkitflaskpy
 
@@ -51,6 +51,7 @@
 from reporelaypy.checkout import Checkout
 from reporelaypy.database import Database
 from reporelaypy.checkoutroute import CheckoutRoute, Redirector
+from reporelaypy.hooks import HookProcessor, HookReceiver
 
 AutoInstall.register(Package('fakeredis', Version(1, 5, 2)))
 AutoInstall.register(Package('hiredis', Version(1, 1, 0)))

Modified: trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/checkout.py (287044 => 287045)


--- trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/checkout.py	2021-12-14 21:16:48 UTC (rev 287044)
+++ trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/checkout.py	2021-12-14 21:31:59 UTC (rev 287045)
@@ -164,19 +164,25 @@
                 return ref == line.split()[0]
         return False
 
-    def update_for(self, branch=None, remote='origin'):
+    def update_for(self, branch=None, remote='origin', track=False):
         if not self.repository:
             sys.stderr.write("Cannot update '{}', clone still pending...\n".format(branch))
             return None
 
         branch = branch or self.repository.default_branch
-        if branch == self.repository.default_branch:
+        if not self.repository.prod_branches.match(branch):
+            return False
+        elif track and branch not in self.repository.branches_for(remote=remote):
+            run(
+                [self.repository.executable(), 'branch', '--track', branch, 'remotes/{}/{}'.format(remote, branch)],
+                cwd=self.repository.root_path,
+            )
+            self.repository.cache.populate(branch=branch)
+        elif branch == self.repository.default_branch:
             self.repository.pull(remote=remote)
             self.repository.cache.populate(branch=branch)
             return True
-        if not self.repository.prod_branches.match(branch):
-            return False
-        if self.is_updated(branch, remote=remote):
+        elif not track and self.is_updated(branch, remote=remote):
             return True
 
         run(

Modified: trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/database.py (287044 => 287045)


--- trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/database.py	2021-12-14 21:16:48 UTC (rev 287044)
+++ trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/database.py	2021-12-14 21:31:59 UTC (rev 287045)
@@ -46,9 +46,9 @@
         self.host = host or Environment.instance().get(self.HOST_ENV)
         self.password = password or Environment.instance().get(self.PASSWORD_ENV)
 
-        if host:
+        if self.host:
             import redis
-            self._redis = redis.Redis(host=host, password=password)
+            self._redis = redis.StrictRedis(host=self.host, password=self.password)
         else:
             import fakeredis
             self._redis = fakeredis.FakeStrictRedis()

Added: trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/hooks.py (0 => 287045)


--- trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/hooks.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/hooks.py	2021-12-14 21:31:59 UTC (rev 287045)
@@ -0,0 +1,154 @@
+# Copyright (C) 2021 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import hashlib
+import hmac
+import json
+
+from flask import current_app, json as fjson, request
+from reporelaypy import Database
+from webkitflaskpy import AuthedBlueprint
+from webkitcorepy import Environment, string_utils
+
+TIMEOUT = 30 * 60
+
+
+class HookProcessor(object):
+    INBOUND_KEY = 'inbound-hooks'
+    WORKER_HOOKS = 'worker-hooks'
+    TYPES = ('push',)
+
+    @classmethod
+    def is_valid(cls, type, data):
+        if type not in cls.TYPES or not isinstance(data, dict):
+            return False
+        return type == 'push' and data.get('ref')
+
+    def __init__(self, checkout, database=None, num_workers=1, worker_index=0, callbacks=None):
+        self.checkout = checkout
+        self.database = database or Database()
+        self.num_workers = num_workers
+        self.worker_index = worker_index
+        self.callbacks = callbacks or dict()
+
+    def process_hook(self, type, data):
+        if not self.is_valid(type, data):
+            return None
+
+        self.callbacks.get(type, lambda _: _)(data)
+
+    def process_worker_hook(self, type, data):
+        if not self.is_valid(type, data):
+            return None
+
+        branch = data.get('ref')
+        if type == 'push' and branch:
+            try:
+                if branch.startswith('refs/heads/'):
+                    branch = branch[len('refs/heads/'):]
+                self.checkout.update_for(branch, track=True)
+            except BaseException as e:
+                sys.stderr.write('{}\n'.format(e))
+
+    def process_hooks(self):
+        with self.database.lock(name='lock_{}'.format(self.INBOUND_KEY), timeout=60):
+            for key in self.database.scan_iter('{}:*'.format(self.INBOUND_KEY)):
+                data = ""
+                self.process_hook(type=data.get('type'), data=""
+
+                encoded = json.dumps(data)
+                digest = hashlib.md5()
+                digest.update(string_utils.encode(encoded))
+                digest = digest.hexdigest()
+                for i in range(self.num_workers):
+                    self.database.set('{}-{}:{}'.format(HookProcessor.WORKER_HOOKS, i, digest), encoded, ex=TIMEOUT)
+                self.database.delete(string_utils.decode(key))
+
+        for key in self.database.scan_iter('{}-{}:*'.format(self.WORKER_HOOKS, self.worker_index)):
+            data = ""
+            self.process_worker_hook(type=data.get('type'), data=""
+            self.database.delete(string_utils.decode(key))
+
+
+class HookReceiver(AuthedBlueprint):
+    SECRET_ENV = 'HOOK_SECRET'
+
+    def __init__(self, import_name=__name__, auth_decorator=None, database=None, debug=False, secret=None):
+        super(HookReceiver, self).__init__('hooks', import_name, url_prefix='/hooks', auth_decorator=auth_decorator)
+
+        self.database = database or Database()
+        self.secret = secret
+
+        self.add_url_rule('', 'inbound', self.inbound, methods=('POST',))
+        if debug:
+            self.add_url_rule('', 'received', self.received, methods=('GET',))
+
+    def inbound(self):
+        if self.secret and request.headers.get('X-Hub-Signature-256', '') != 'sha256={}'.format(hmac.new(
+            string_utils.encode(self.secret), request.data, hashlib.sha256,
+        ).hexdigest()):
+            return current_app.response_class(
+                fjson.dumps(dict(
+                    status='Unauthorized Hook',
+                    message='HMAC verification failed failed',
+                ), indent=4),
+                mimetype='application/json',
+                status=403,
+            )
+
+        type = request.headers.get('X-GitHub-Event', '')
+        data = "" force=False)
+        if HookProcessor.is_valid(type, data):
+            encoded = json.dumps(dict(type=type, data=""
+            digest = hashlib.md5()
+            digest.update(string_utils.encode(encoded))
+
+            self.database.set('{}:{}'.format(HookProcessor.INBOUND_KEY, digest.hexdigest()), encoded, ex=TIMEOUT)
+
+            return current_app.response_class(
+                fjson.dumps(dict(
+                    status='Success',
+                    message='Hook queued for processing',
+                ), indent=4),
+                mimetype='application/json',
+                status=202,
+            )
+
+        return current_app.response_class(
+            fjson.dumps(dict(
+                status='Unrecognized Hook',
+                message='Provided hook is in unrecognized format',
+            ), indent=4),
+            mimetype='application/json',
+            status=400,
+        )
+
+    def received(self):
+        result = [
+            json.loads(self.database.get(string_utils.decode(key)))
+            for key in self.database.scan_iter('{}:*'.format(HookProcessor.INBOUND_KEY))
+        ]
+        return current_app.response_class(
+            fjson.dumps(result, indent=4),
+            mimetype='application/json',
+            status=200,
+        )

Added: trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/tests/hooks_unittest.py (0 => 287045)


--- trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/tests/hooks_unittest.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/tests/hooks_unittest.py	2021-12-14 21:31:59 UTC (rev 287045)
@@ -0,0 +1,136 @@
+# Copyright (C) 2021 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import json
+import unittest
+
+from reporelaypy import Checkout, Database, HookProcessor, HookReceiver
+from webkitcorepy import testing, OutputCapture
+from webkitflaskpy import mock_app
+from webkitscmpy import mocks, Commit
+
+
+class HooksUnittest(testing.PathTestCase):
+    basepath = 'mock/repository'
+
+    def setUp(self):
+        super(HooksUnittest, self).setUp()
+        os.mkdir(os.path.join(self.path, '.git'))
+
+    @mock_app
+    def test_receive(self, app=None, client=None):
+        app.register_blueprint(HookReceiver(debug=True))
+        response = client.get('/hooks')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), [])
+
+        response = client.post('/hooks', json=dict(ref='refs/heads/main'), headers={'X-GitHub-Event': 'push'})
+        self.assertEqual(response.status_code, 202)
+        self.assertEqual(response.json(), dict(
+            status='Success',
+            message='Hook queued for processing',
+        ))
+
+        response = client.get('/hooks')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), [dict(data="" type='push')])
+
+    @mock_app
+    def test_invalid(self, app=None, client=None):
+        app.register_blueprint(HookReceiver(debug=True))
+        response = client.get('/hooks')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), [])
+
+        response = client.post('/hooks', json=['invalid'], headers={'X-GitHub-Event': 'push'})
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), dict(
+            status='Unrecognized Hook',
+            message='Provided hook is in unrecognized format',
+        ))
+
+    @mock_app
+    def test_process(self, app=None, client=None):
+        with OutputCapture(), mocks.local.Git(self.path) as repo:
+            database = Database()
+            app.register_blueprint(HookReceiver(database=database, debug=True))
+
+            response = client.post('/hooks', json=dict(ref='refs/heads/main'), headers={'X-GitHub-Event': 'push'})
+            self.assertEqual(response.status_code, 202)
+
+            processor = HookProcessor(
+                Checkout(path=self.path, url="" sentinal=False),
+                database=database,
+            )
+            processor.process_hooks()
+
+            response = client.get('/hooks')
+            self.assertEqual(response.status_code, 200)
+            self.assertEqual(response.json(), [])
+
+    @mock_app
+    def test_process_branch(self, app=None, client=None):
+        with OutputCapture(), mocks.local.Git(self.path) as repo:
+            database = Database()
+            app.register_blueprint(HookReceiver(database=database, debug=True))
+
+            response = client.post('/hooks', json=dict(ref='refs/heads/branch-a'), headers={'X-GitHub-Event': 'push'})
+            self.assertEqual(response.status_code, 202)
+
+            processor = HookProcessor(
+                Checkout(path=self.path, url="" sentinal=False),
+                database=database,
+            )
+            processor.process_hooks()
+
+            response = client.get('/hooks')
+            self.assertEqual(response.status_code, 200)
+            self.assertEqual(response.json(), [])
+
+    @mock_app
+    def test_hmac(self, app=None, client=None):
+        app.register_blueprint(HookReceiver(debug=True, secret='secret'))
+
+        response = client.post(
+            '/hooks', json=dict(ref='refs/heads/main'),
+            headers={
+                'X-Hub-Signature-256': 'sha256=36a089f93b92e972b714e8c0f008873a206c690a8aee946a787eeb0f23e131b2',
+                'X-GitHub-Event': 'push',
+            },
+        )
+        self.assertEqual(response.status_code, 202)
+        self.assertEqual(response.json(), dict(
+            status='Success',
+            message='Hook queued for processing',
+        ))
+
+    @mock_app
+    def test_invalid_hmac(self, app=None, client=None):
+        app.register_blueprint(HookReceiver(debug=True, secret='secret'))
+
+        response = client.post('/hooks', json=dict(ref='refs/heads/main'), headers={'X-GitHub-Event': 'push'})
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), dict(
+            status='Unauthorized Hook',
+            message='HMAC verification failed failed',
+        ))

Modified: trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/webserver.py (287044 => 287045)


--- trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/webserver.py	2021-12-14 21:16:48 UTC (rev 287044)
+++ trunk/Tools/Scripts/libraries/reporelaypy/reporelaypy/webserver.py	2021-12-14 21:31:59 UTC (rev 287045)
@@ -20,6 +20,7 @@
 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+import json
 import os
 
 autoinstall_path = os.environ.get('AUTOINSTALL_PATH')
@@ -28,7 +29,7 @@
     AutoInstall.set_directory(autoinstall_path)
 
 from flask import Flask, current_app, json as fjson
-from reporelaypy import Checkout, CheckoutRoute, Database, Redirector
+from reporelaypy import Checkout, CheckoutRoute, Database, Redirector, HookReceiver
 
 app = Flask(__name__)
 
@@ -39,7 +40,16 @@
     import_name=__name__, database=database,
 )
 
+hook_args = json.loads(os.environ.get('HOOKS', '{}'))
+if hook_args.get('enabled', False):
+    hook_routes = HookReceiver(
+        import_name=__name__, database=database,
+        debug=hook_args.get('debug', False), secret=os.environ.get(HookReceiver.SECRET_ENV),
+    )
+else:
+    hook_routes = None
 
+
 @app.route('/__health')
 def health():
     return 'ready' if checkout.repository else 'cloning'
@@ -46,6 +56,8 @@
 
 
 app.register_blueprint(checkout_routes)
+if hook_routes:
+    app.register_blueprint(hook_routes)
 
 
 if __name__ == '__main__':

Modified: trunk/Tools/Scripts/libraries/reporelaypy/run (287044 => 287045)


--- trunk/Tools/Scripts/libraries/reporelaypy/run	2021-12-14 21:16:48 UTC (rev 287044)
+++ trunk/Tools/Scripts/libraries/reporelaypy/run	2021-12-14 21:31:59 UTC (rev 287045)
@@ -38,7 +38,7 @@
     sys.path.insert(0, scripts)
     import webkitpy
 
-from reporelaypy import Checkout, Database, Redirector
+from reporelaypy import Checkout, Database, HookProcessor, HookReceiver, Redirector
 from webkitcorepy import arguments, AutoInstall
 from whichcraft import which
 
@@ -101,6 +101,16 @@
         help='Base URL to redirect user to for commit information.',
     )
 
+    group = parser.add_argument_group('Hooks')
+    group.add_argument(
+        '--hooks', '--no-hooks', action="" dest='hooks', default=False,
+        help='Enable or disable hook end-points (disabled by default)',
+    )
+    group.add_argument(
+        '--debug', '--no-debug', action="" dest='hooks_debug', default=True,
+        help='Enable endpoint to report hooks being processed (enabled by default)',
+    )
+
     args = parser.parse_args(args=args)
 
     database = Database(host=args.redis_host, password=args.redis_password)
@@ -140,25 +150,30 @@
             redirector = Redirector(url)
             print('    {}: {}'.format(redirector.name, redirector.url))
 
-    env = dict(
+    passenv = dict(
         PYTHONPATH=':'.join(sys.path),
         CHECKOUT=json.dumps(checkout, cls=Checkout.Encoder),
-        REDIRECTORS=json.dumps([Redirector(url) for url in args.redirector or []], cls=Redirector.Encoder)
+        REDIRECTORS=json.dumps([Redirector(url) for url in args.redirector or []], cls=Redirector.Encoder),
+        HOOKS=json.dumps({'enabled': args.hooks, 'debug': args.hooks_debug}),
     )
 
     if AutoInstall.directory:
-        env['AUTOINSTALL_PATH'] = AutoInstall.directory
+        passenv['AUTOINSTALL_PATH'] = AutoInstall.directory
     if database.host:
-        env[database.HOST_ENV] = database.host
+        passenv[database.HOST_ENV] = database.host
     if database.password:
-        env[database.PASSWORD_ENV] = database.password
+        passenv[database.PASSWORD_ENV] = database.password
     if database.default_expiration:
-        env[database.EXPIRATION_ENV] = str(database.default_expiration)
+        passenv[database.EXPIRATION_ENV] = str(database.default_expiration)
+    if os.environ.get(HookReceiver.SECRET_ENV):
+        passenv[HookReceiver.SECRET_ENV] = os.environ.get(HookReceiver.SECRET_ENV)
 
+    processor = HookProcessor(checkout=checkout, database=database) if args.hooks else None
+
     with subprocess.Popen(
         [which('gunicorn'), 'reporelaypy.webserver:app'],
         cwd=os.path.dirname(os.path.dirname(reporelaypy.__file__)),
-        env=env,
+        env=passenv,
     ) as webserver:
         last_poll = time.time()
         last_pull = time.time()
@@ -169,7 +184,7 @@
                     break
                 last_poll = time.time()
             if last_pull + args.update_interval < time.time():
-                checkout.update_all()
+                processor.process_hooks() if processor else checkout.update_all()
                 last_pull = time.time()
             time.sleep(math.gcd(args.poll, args.update_interval))
 

Modified: trunk/Tools/Scripts/libraries/reporelaypy/setup.py (287044 => 287045)


--- trunk/Tools/Scripts/libraries/reporelaypy/setup.py	2021-12-14 21:16:48 UTC (rev 287044)
+++ trunk/Tools/Scripts/libraries/reporelaypy/setup.py	2021-12-14 21:31:59 UTC (rev 287045)
@@ -30,7 +30,7 @@
 
 setup(
     name='reporelaypy',
-    version='0.2.0',
+    version='0.3.0',
     description='Library for visualizing, processing and storing test results.',
     long_description=readme(),
     classifiers=[
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to