From b7ccea8d32b033e50f6ec23d30a0d8d9dffec269 Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Thu, 23 Nov 2023 15:49:15 +0100 Subject: [PATCH 01/68] data: Add missing parent_buildid key-value pair to Buildset msg --- master/buildbot/data/buildsets.py | 1 + master/buildbot/test/unit/data/test_buildsets.py | 1 + ...d-missing-buildset-parent-buildid-for-data-api-message.bugfix | 1 + 3 files changed, 3 insertions(+) create mode 100644 newsfragments/add-missing-buildset-parent-buildid-for-data-api-message.bugfix diff --git a/master/buildbot/data/buildsets.py b/master/buildbot/data/buildsets.py index c58ff4b2703a..4eeeb3b7e22e 100644 --- a/master/buildbot/data/buildsets.py +++ b/master/buildbot/data/buildsets.py @@ -173,6 +173,7 @@ def addBuildset(self, waited_for, scheduler=None, sourcestamps=None, reason='', "bsid": bsid, "external_idstring": external_idstring, "reason": reason, + "parent_buildid": parent_buildid, "submitted_at": submitted_at, "complete": False, "complete_at": None, diff --git a/master/buildbot/test/unit/data/test_buildsets.py b/master/buildbot/test/unit/data/test_buildsets.py index db2c131faa30..eb5d91f0a921 100644 --- a/master/buildbot/test/unit/data/test_buildsets.py +++ b/master/buildbot/test/unit/data/test_buildsets.py @@ -221,6 +221,7 @@ def _buildsetMessage(self, bsid, external_idstring='extid', "complete": False, "complete_at": None, "external_idstring": external_idstring, + "parent_buildid": None, "reason": reason, "results": None, "scheduler": scheduler, diff --git a/newsfragments/add-missing-buildset-parent-buildid-for-data-api-message.bugfix b/newsfragments/add-missing-buildset-parent-buildid-for-data-api-message.bugfix new file mode 100644 index 000000000000..50d6145cfc66 --- /dev/null +++ b/newsfragments/add-missing-buildset-parent-buildid-for-data-api-message.bugfix @@ -0,0 +1 @@ +Rebuilt buildsets Data API messages now have "parent_buildid" key-value pair (:issue `7222`). From 9afcfc7ffbfe92180dbd5513959497adad37414e Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Thu, 14 Dec 2023 12:42:49 +0200 Subject: [PATCH 02/68] pkg: Don't use announce() for logging setuptools.Command() does not document this function. It also crashes with an exception on Python 3.9 for example, as underlying distutils does not understand the level=logging.INFO argument. Fixes 91b32a1a6e0024f3c7e0c22a4e536607c881d0f6. --- newsfragments/www-package-build-older-python.bugfix | 1 + pkg/buildbot_pkg.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 newsfragments/www-package-build-older-python.bugfix diff --git a/newsfragments/www-package-build-older-python.bugfix b/newsfragments/www-package-build-older-python.bugfix new file mode 100644 index 000000000000..299751f1e7d4 --- /dev/null +++ b/newsfragments/www-package-build-older-python.bugfix @@ -0,0 +1 @@ +Fix web frontend package build on certain Python versions (e.g. 3.9). diff --git a/pkg/buildbot_pkg.py b/pkg/buildbot_pkg.py index b96a190e9b47..d2b76553de1f 100644 --- a/pkg/buildbot_pkg.py +++ b/pkg/buildbot_pkg.py @@ -227,8 +227,7 @@ def run(self): ] for command in commands: - self.announce('Running command: {}'.format(str(" ".join(command))), - level=logging.INFO) + logging.info('Running command: {}'.format(str(" ".join(command)))) subprocess.check_call(command, shell=shell) self.copy_tree(os.path.join(package, 'static'), os.path.join( From 3df4784e09b19a58a4cd0fe8da33a5e60d01731f Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sun, 17 Dec 2023 15:43:00 +0200 Subject: [PATCH 03/68] asyncio: Fix crash in deferred_await() asyncio.get_event_loop() would throw if there's no loop associated with the thread. --- master/buildbot/asyncio.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/master/buildbot/asyncio.py b/master/buildbot/asyncio.py index 6c91a5a80fa7..6fc437a41989 100644 --- a/master/buildbot/asyncio.py +++ b/master/buildbot/asyncio.py @@ -26,7 +26,12 @@ def deferred_await(self): # if a deferred is awaited from a asyncio loop context, we must return # the future wrapper, but if it is awaited from normal twisted loop # we must return self. - if isinstance(asyncio.get_event_loop(), AsyncIOLoopWithTwisted): + try: + loop = asyncio.get_event_loop() + except RuntimeError: + return self + + if isinstance(loop, AsyncIOLoopWithTwisted): return self.asFuture(asyncio.get_event_loop()) return self From 318e605f572cdf24cf109c4eb6e090e599d9620d Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sun, 17 Dec 2023 15:43:01 +0200 Subject: [PATCH 04/68] www: Use notFound() on Twisted 22.10 and newer --- master/buildbot/www/service.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/master/buildbot/www/service.py b/master/buildbot/www/service.py index f07c95cd90fc..1036b81a1537 100644 --- a/master/buildbot/www/service.py +++ b/master/buildbot/www/service.py @@ -19,7 +19,9 @@ from binascii import hexlify import jwt +from packaging.version import parse as parse_version +import twisted from twisted.application import strports from twisted.cred.portal import IRealm from twisted.cred.portal import Portal @@ -275,7 +277,13 @@ def refresh_base_plugin_name(self, new_config): def configPlugins(self, root, new_config): plugin_root = root if self.base_plugin_name == 'base_react': - plugin_root = resource.NoResource() + current_version = parse_version(twisted.__version__) + if current_version < parse_version("22.10.0"): + from twisted.web.resource import NoResource + plugin_root = NoResource() + else: + from twisted.web.pages import notFound + plugin_root = notFound() root.putChild(b"plugins", plugin_root) known_plugins = set(new_config.www.get('plugins', {})) | set([self.base_plugin_name]) From e29b417d716cc3124a862149ab1d06508965df1d Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sun, 17 Dec 2023 15:43:02 +0200 Subject: [PATCH 05/68] test: Fix deprecation warning coming from autobahn --- master/buildbot/test/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/master/buildbot/test/__init__.py b/master/buildbot/test/__init__.py index 5e89e85eb4a1..af487313b8da 100644 --- a/master/buildbot/test/__init__.py +++ b/master/buildbot/test/__init__.py @@ -135,3 +135,7 @@ # boto3 shows this warning when on old Python warnings.filterwarnings('ignore', ".*Boto3 will no longer support Python .*", category=Warning) + +# autobahn is not updated for Twisted 22.04 and newer +warnings.filterwarnings("ignore", "twisted.web.resource.NoResource was deprecated in", + category=DeprecationWarning) From f7ea308f82f424aaf5493c9bc19274ba3693e5f8 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sun, 17 Dec 2023 15:43:03 +0200 Subject: [PATCH 06/68] master: Bump maximum allowed Twisted version to 23.10.0 --- master/setup.py | 2 +- newsfragments/allow-twisted-dep-23-10.bugfix | 1 + requirements-ci.txt | 2 +- requirements-ciworker.txt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 newsfragments/allow-twisted-dep-23-10.bugfix diff --git a/master/setup.py b/master/setup.py index 8fe7cc51d3c7..3c4d614bdbee 100755 --- a/master/setup.py +++ b/master/setup.py @@ -467,7 +467,7 @@ def define_plugin_entries(groups): if not py_38: raise RuntimeError("Buildbot master requires at least Python-3.8") -twisted_ver = ">= 18.7.0, <=22.10.0" +twisted_ver = ">= 18.7.0, <=23.10.0" bundle_version = version.split("-")[0] diff --git a/newsfragments/allow-twisted-dep-23-10.bugfix b/newsfragments/allow-twisted-dep-23-10.bugfix new file mode 100644 index 000000000000..383df9578992 --- /dev/null +++ b/newsfragments/allow-twisted-dep-23-10.bugfix @@ -0,0 +1 @@ +Fix support for Twisted 23.10 diff --git a/requirements-ci.txt b/requirements-ci.txt index a7121fe33837..16bdd122bc6e 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -117,7 +117,7 @@ tomli==2.0.1 tomlkit==0.12.3 towncrier==21.9.0 treq==22.2.0 -Twisted==22.4.0 +Twisted==23.10.0 txaio==23.1.1 txrequests==0.9.6 types-PyYAML==6.0.12.12 diff --git a/requirements-ciworker.txt b/requirements-ciworker.txt index 13be5669fc4a..ffef235688ef 100644 --- a/requirements-ciworker.txt +++ b/requirements-ciworker.txt @@ -24,7 +24,7 @@ PyHamcrest==1.9.0 # pyup: ignore psutil==5.9.6 pycparser==2.21; python_version >= "3.6" six==1.16.0 -Twisted==22.4.0; python_version >= "3.6" +Twisted==23.10.0; python_version >= "3.6" Twisted==20.3.0; python_version < "3.6" # pyup: ignore txaio==23.1.1; python_version >= "3.6" typing-extensions==4.8.0; python_version >= "3.7" From 4ea3314b43be86292f47a418599ed7196697303b Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sun, 17 Dec 2023 15:43:04 +0200 Subject: [PATCH 07/68] bbtravis: Add test with Twisted 22.4.0 --- .bbtravis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.bbtravis.yml b/.bbtravis.yml index e6b0e7665763..d84954283742 100644 --- a/.bbtravis.yml +++ b/.bbtravis.yml @@ -36,6 +36,7 @@ matrix: # include "ci" string into the name of the status that is eventually submitted to Github, so # that the codecov.io service would wait until this build is finished before creating report. - env: PYTHON=3.9 TWISTED=18.7.0 SQLALCHEMY=latest NUM_CPU=2 TESTS=trial + - env: PYTHON=3.9 TWISTED=22.4.0 SQLALCHEMY=latest NUM_CPU=2 TESTS=trial - env: PYTHON=3.8 TWISTED=latest SQLALCHEMY=latest NUM_CPU=2 TESTS=ci/coverage - env: PYTHON=3.9 TWISTED=latest SQLALCHEMY=latest NUM_CPU=2 TESTS=trial - env: PYTHON=3.10 TWISTED=latest SQLALCHEMY=latest NUM_CPU=2 TESTS=trial From f86d83034226e57882a6d4b28807b7e7a13bf062 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sun, 17 Dec 2023 15:43:05 +0200 Subject: [PATCH 08/68] Disable unclosed event loop warnings in test --- master/buildbot/test/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/master/buildbot/test/__init__.py b/master/buildbot/test/__init__.py index af487313b8da..df7c82eca1f0 100644 --- a/master/buildbot/test/__init__.py +++ b/master/buildbot/test/__init__.py @@ -139,3 +139,6 @@ # autobahn is not updated for Twisted 22.04 and newer warnings.filterwarnings("ignore", "twisted.web.resource.NoResource was deprecated in", category=DeprecationWarning) + +# Buildbot shows this warning after upgrading to Twisted 23.10 +warnings.filterwarnings('ignore', ".*unclosed event loop.*", category=Warning) From dc752bedc30acbe462f486a2661c3753e6ce18e2 Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Tue, 5 Dec 2023 20:57:21 +0100 Subject: [PATCH 09/68] Improve security of tarfile extraction addressed by PEP 706 See PEP 706 - Filter for tarfile.extractall (https://peps.python.org/pep-0706/) Python 3.12 report this as warning: Python 3.14 will, by default, filter extracted tar archives and reject files or modify their metadata. Use the filter argument to control this behavior Fixes #7294 --- master/buildbot/process/remotetransfer.py | 5 ++++- master/buildbot/test/integration/test_upgrade.py | 5 ++++- newsfragments/tarfile-pep706.bugfix | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 newsfragments/tarfile-pep706.bugfix diff --git a/master/buildbot/process/remotetransfer.py b/master/buildbot/process/remotetransfer.py index 76a13faadf3c..8ac5f888d281 100644 --- a/master/buildbot/process/remotetransfer.py +++ b/master/buildbot/process/remotetransfer.py @@ -126,7 +126,10 @@ def remote_unpack(self): # Unpack archive and clean up after self with tarfile.open(name=self.tarname, mode=mode) as archive: - archive.extractall(path=self.destroot) + if hasattr(tarfile, 'data_filter'): + archive.extractall(path=self.destroot, filter='data') + else: + archive.extractall(path=self.destroot) os.remove(self.tarname) diff --git a/master/buildbot/test/integration/test_upgrade.py b/master/buildbot/test/integration/test_upgrade.py index 66669b1b3409..40867e4fabcc 100644 --- a/master/buildbot/test/integration/test_upgrade.py +++ b/master/buildbot/test/integration/test_upgrade.py @@ -77,7 +77,10 @@ def setUpUpgradeTest(self): with tarfile.open(tarball) as tf: prefixes = set() for inf in tf: - tf.extract(inf) + if hasattr(tarfile, 'data_filter'): + tf.extract(inf, filter='data') + else: + tf.extract(inf) prefixes.add(inf.name.split('/', 1)[0]) # (note that tf.extractall isn't available in py2.4) diff --git a/newsfragments/tarfile-pep706.bugfix b/newsfragments/tarfile-pep706.bugfix new file mode 100644 index 000000000000..0d398f780dde --- /dev/null +++ b/newsfragments/tarfile-pep706.bugfix @@ -0,0 +1 @@ +Improved security of tarfile extraction to help avoid CVE-2007-4559. See more details in https://peps.python.org/pep-0706/. Buildbot uses filter='data' now. (:issue:`7294`) From 4f30bd3b7422ce1dadb32774146bfd57f016d87f Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Thu, 21 Dec 2023 23:22:01 +0200 Subject: [PATCH 10/68] worker: Work around bug in reactor.spawnProcess on Twisted 23.10 Twisted 23.10 and some other versions implement spawnProcess using os.posix_spawnp(). If spawnProcess() is passed env=None, then for os.posix_spawnp() it is changed to empty environment. As a result, kubectl is started with empty environment and can't find its configuration. --- master/buildbot/util/kubeclientservice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/master/buildbot/util/kubeclientservice.py b/master/buildbot/util/kubeclientservice.py index ab786c20d866..a7d99d495b65 100644 --- a/master/buildbot/util/kubeclientservice.py +++ b/master/buildbot/util/kubeclientservice.py @@ -155,7 +155,8 @@ def reconfigService(self, proxy_port=8001, namespace="default"): self.pp, self.kube_ctl_proxy_cmd[0], self.kube_ctl_proxy_cmd + ["-p", str(self.proxy_port)], - env=None) + env=os.environ + ) self.kube_proxy_output = yield self.pp.got_output_deferred def stopService(self): From 75c442f86ba2ab8a89a9543b92855d4bb5e3e597 Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Thu, 21 Dec 2023 15:11:45 +0200 Subject: [PATCH 11/68] docs: Add Crossbar configuration example --- master/docs/manual/configuration/global.rst | 94 ++++++++++++++++++--- 1 file changed, 84 insertions(+), 10 deletions(-) diff --git a/master/docs/manual/configuration/global.rst b/master/docs/manual/configuration/global.rst index ef92e3264f11..020d48ff5f10 100644 --- a/master/docs/manual/configuration/global.rst +++ b/master/docs/manual/configuration/global.rst @@ -154,16 +154,7 @@ Wamp This is a MQ implementation using the `wamp `_ protocol. This implementation uses `Python Autobahn `_ wamp client library, and is fully asynchronous (no use of threads). -To use this implementation, you need a wamp router like `Crossbar `_. - -Please refer to Crossbar documentation for more details, but the default Crossbar setup will just work with Buildbot, provided you use the example ``mq`` configuration above, and start Crossbar with: - -.. code-block:: bash - - # of course, you should work in a virtualenv... - pip install crossbar - crossbar init - crossbar start +To use this implementation, you need a wamp router like Crossbar. The implementation does not yet support wamp authentication. This MQ allows buildbot to run in multi-master mode. @@ -182,6 +173,89 @@ For example, if a change is received, but the master shuts down before the sched You must use a router with very reliable connection to the master. If for some reason, the wamp connection is lost, then the master will stop, and should be restarted via a process manager. +.. _mq-Crossbar: + +Crossbar +++++++++ + +The default Crossbar setup will just work with Buildbot, provided you use the example ``mq`` +configuration below, and start Crossbar with: + +.. code-block:: bash + + # of course, you should work in a virtualenv... + pip install crossbar + crossbar init + crossbar start + +.crossbar/config.json: + +.. code-block:: bash + + { + "version": 2, + "controller": {}, + "workers": [ + { + "type": "router", + "realms": [ + { + "name": "test_realm", + "roles": [ + { + "name": "anonymous", + "permissions": [ + { + "uri": "", + "match": "prefix", + "allow": { + "call": true, + "register": true, + "publish": true, + "subscribe": true + }, + "disclose": { + "caller": false, + "publisher": false + }, + "cache": true + } + ] + } + ] + } + ], + "transports": [ + { + "type": "web", + "endpoint": { + "type": "tcp", + "port": 1245 + }, + "paths": { + "ws": { + "type": "websocket" + } + } + } + ] + } + ] + } + +Buildbot can be configured to use Crossbar by the following: + +.. code-block:: bash + + c["mq"] = { + "type" : "wamp", + "router_url": "ws://localhost:1245/ws", + "realm": "test_realm", + "wamp_debug_level" : "warn" + } + +Please refer to `Crossbar `_ documentation for +more details. .. bb:cfg:: multiMaster From 15cc3678935555775893c5e861a3158790603a65 Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Fri, 22 Dec 2023 14:52:06 +0200 Subject: [PATCH 12/68] www: Fix React error message when its frontend is not installed --- master/buildbot/www/service.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/master/buildbot/www/service.py b/master/buildbot/www/service.py index 1036b81a1537..33b825085113 100644 --- a/master/buildbot/www/service.py +++ b/master/buildbot/www/service.py @@ -306,8 +306,10 @@ def setupSite(self, new_config): self.reconfigurableResources = [] - # we're going to need at least the base plugin (buildbot-www) + # we're going to need at least the base plugin (buildbot-www or buildbot-www-react) if self.base_plugin_name not in self.apps: + if self.base_plugin_name == 'base_react': + raise RuntimeError("could not find buildbot-www-react; is it installed?") raise RuntimeError("could not find buildbot-www; is it installed?") root = self.apps.get(self.base_plugin_name).resource From da3d620d90c5bb9c172ab69c9d9cc78e08cb9a1f Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Wed, 23 Aug 2023 22:10:33 +0200 Subject: [PATCH 13/68] Update SQLAlchemy from 1.4.40 to 1.4.50 1.4.49 is first fully compatible with Python 3.12 1.4.50 is latest one in 1.4.x series Changelogs: - https://github.com/sqlalchemy/sqlalchemy/releases/tag/rel_1_4_49 - https://github.com/sqlalchemy/sqlalchemy/releases/tag/rel_1_4_50 --- requirements-ci.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-ci.txt b/requirements-ci.txt index 16bdd122bc6e..a580e86aa47c 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -108,7 +108,7 @@ singledispatch==4.1.0 six==1.16.0 snowballstemmer==2.2.0 # Buildbot supports only sqlalchemy >= 1.3.0, < 1.5 -SQLAlchemy==1.4.40 # pyup: ignore +SQLAlchemy==1.4.50 # pyup: ignore sqlparse==0.4.4 termcolor==2.3.0 testtools==2.7.1 From c00b38056218cbbb2d7876de8977aa452dd754b6 Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Fri, 8 Dec 2023 22:14:04 +0100 Subject: [PATCH 14/68] test: Disable sqlalchemy generated warning --- master/buildbot/test/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/master/buildbot/test/__init__.py b/master/buildbot/test/__init__.py index df7c82eca1f0..be7a914cd295 100644 --- a/master/buildbot/test/__init__.py +++ b/master/buildbot/test/__init__.py @@ -19,6 +19,7 @@ from unittest import mock import setuptools # force import setuptools before any other distutils imports +from sqlalchemy.exc import RemovedIn20Warning from buildbot import monkeypatches from buildbot.test.util.warnings import assertProducesWarning # noqa pylint: disable=wrong-import-position @@ -142,3 +143,12 @@ # Buildbot shows this warning after upgrading to Twisted 23.10 warnings.filterwarnings('ignore', ".*unclosed event loop.*", category=Warning) + +# Ignore sqlalchemy 1.5 warning +# sqlalchemy.exc.RemovedIn20Warning: Deprecated API features detected! These feature(s) are not +# compatible with SQLAlchemy 2.0. To prevent incompatible upgrades prior to updating applications, +# ensure requirements files are pinned to "sqlalchemy<2.0". Set environment variable +# SQLALCHEMY_WARN_20=1 to show all deprecation warnings. Set environment variable +# SQLALCHEMY_SILENCE_UBER_WARNING=1 to silence this message. (Background on SQLAlchemy 2.0 +# at: https://sqlalche.me/e/b8d9) (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9) +warnings.filterwarnings("ignore", category=RemovedIn20Warning) From 9c8760d9cf3b791887f052511e8b18ffb8721ab9 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sat, 23 Dec 2023 08:52:09 +0200 Subject: [PATCH 15/68] github: Disable fail-fast setting for Windows checks too This allows to see clear impact of a failure instead of guessing which checks would have succeeded or failed if they were not canceled. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42d1af8ae355..8f853998ec80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,6 +101,7 @@ jobs: runs-on: windows-2019 strategy: + fail-fast: false matrix: python-version: ["3.8", "3.9", "3.10", "3.11"] From eacd168fe7e6141693c8c7a3cec8b97398ee02fd Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sat, 23 Dec 2023 08:57:52 +0200 Subject: [PATCH 16/68] github: Don't run database tests on each PR Buildbot internal infrastructure now supports many more CPUs and CPUs per job which results in Github tests being a bottleneck for many PRs. The database tests are duplicated within Buildbot test configuration anyway. Therefore it's enough to run tests on Github after PR merge. --- .github/workflows/ci.yml | 87 ------------------------------------- .github/workflows/cidb.yml | 88 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 87 deletions(-) create mode 100644 .github/workflows/cidb.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f853998ec80..f6904bb64782 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,93 +9,6 @@ permissions: contents: read jobs: - db: - name: DB / ${{ matrix.name }} - runs-on: ubuntu-20.04 - - strategy: - fail-fast: false - matrix: - include: - - name: MySQL 5 - database: mysql:5 - connection: 'mysql+mysqldb://buildbot:buildbot@127.0.0.1:3306/bbtest?storage_engine=InnoDB' - check: mysqladmin ping - - - name: MySQL latest - database: mysql:latest - connection: 'mysql+mysqldb://buildbot:buildbot@127.0.0.1:3306/bbtest?storage_engine=InnoDB' - check: mysqladmin ping - - - name: PostgreSQL 9 / psycopg2 - database: postgres:9 - connection: 'postgresql+psycopg2://buildbot:buildbot@127.0.0.1:5432/bbtest' - check: pg_isready - - - name: PostgreSQL 9 / pg8000 - database: postgres:9 - connection: 'postgresql+pg8000://buildbot:buildbot@127.0.0.1:5432/bbtest' - check: pg_isready - - - name: PostgreSQL latest / psycopg2 - database: postgres:latest - connection: 'postgresql+psycopg2://buildbot:buildbot@127.0.0.1:5432/bbtest' - check: pg_isready - - - name: PostgreSQL latest / pg8000 - database: postgres:latest - connection: 'postgresql+pg8000://buildbot:buildbot@127.0.0.1:5432/bbtest' - check: pg_isready - - env: - BUILDBOT_TEST_DB_URL: ${{ matrix.connection }} - - services: - database: - image: ${{ matrix.database }} - env: - MYSQL_USER: buildbot - MYSQL_PASSWORD: buildbot - MYSQL_DATABASE: bbtest - MYSQL_ALLOW_EMPTY_PASSWORD: yes - POSTGRES_USER: buildbot - POSTGRES_PASSWORD: buildbot - POSTGRES_DB: bbtest - ports: - - '3306:3306' - - '5432:5432' - options: --health-cmd "${{ matrix.check }}" --health-interval 10s --health-timeout 5s --health-retries 10 - - steps: - - uses: actions/checkout@v2 - - - run: sudo apt-get install aspell aspell-en enchant iamerican ispell - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements-ci.txt', 'requirements-cidb.txt') }} - restore-keys: ${{ runner.os }}-pip- - - - run: pip install -U pip wheel - - run: pip install -r requirements-ci.txt -r requirements-cidb.txt - - # run real db tests under coverage to have several merging coverage report - # https://github.com/codecov/support/wiki/Merging-Reports - - run: coverage run --rcfile=.coveragerc $(which trial) --reporter=text --rterrors buildbot.test buildbot_worker.test - - - run: | - curl -Os https://uploader.codecov.io/v0.4.1/linux/codecov - echo "32cb14b5f3aaacd67f4c1ff55d82f037d3cd10c8e7b69c051f27391d2e66e15c codecov" | sha256sum --check - chmod +x ./codecov - CODECOV_TOKEN="b80c80d7-689d-46d7-b1aa-59168bb4c9a9" ./codecov - win: name: Win / python ${{ matrix.python-version }} runs-on: windows-2019 diff --git a/.github/workflows/cidb.yml b/.github/workflows/cidb.yml new file mode 100644 index 000000000000..e7bdd07ae0d1 --- /dev/null +++ b/.github/workflows/cidb.yml @@ -0,0 +1,88 @@ +name: CI (DB) +on: + push: + branches: + - master + - release + +permissions: + contents: read + +jobs: + db: + name: DB / ${{ matrix.name }} + runs-on: ubuntu-20.04 + + strategy: + fail-fast: false + matrix: + include: + - name: MySQL 5 + database: mysql:5 + connection: 'mysql+mysqldb://buildbot:buildbot@127.0.0.1:3306/bbtest?storage_engine=InnoDB' + check: mysqladmin ping + + - name: MySQL latest + database: mysql:latest + connection: 'mysql+mysqldb://buildbot:buildbot@127.0.0.1:3306/bbtest?storage_engine=InnoDB' + check: mysqladmin ping + + - name: PostgreSQL 9 / psycopg2 + database: postgres:9 + connection: 'postgresql+psycopg2://buildbot:buildbot@127.0.0.1:5432/bbtest' + check: pg_isready + + - name: PostgreSQL 9 / pg8000 + database: postgres:9 + connection: 'postgresql+pg8000://buildbot:buildbot@127.0.0.1:5432/bbtest' + check: pg_isready + + - name: PostgreSQL latest / psycopg2 + database: postgres:latest + connection: 'postgresql+psycopg2://buildbot:buildbot@127.0.0.1:5432/bbtest' + check: pg_isready + + - name: PostgreSQL latest / pg8000 + database: postgres:latest + connection: 'postgresql+pg8000://buildbot:buildbot@127.0.0.1:5432/bbtest' + check: pg_isready + + env: + BUILDBOT_TEST_DB_URL: ${{ matrix.connection }} + + services: + database: + image: ${{ matrix.database }} + env: + MYSQL_USER: buildbot + MYSQL_PASSWORD: buildbot + MYSQL_DATABASE: bbtest + MYSQL_ALLOW_EMPTY_PASSWORD: yes + POSTGRES_USER: buildbot + POSTGRES_PASSWORD: buildbot + POSTGRES_DB: bbtest + ports: + - '3306:3306' + - '5432:5432' + options: --health-cmd "${{ matrix.check }}" --health-interval 10s --health-timeout 5s --health-retries 10 + + steps: + - uses: actions/checkout@v2 + + - run: sudo apt-get install aspell aspell-en enchant iamerican ispell + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements-ci.txt', 'requirements-cidb.txt') }} + restore-keys: ${{ runner.os }}-pip- + + - run: pip install -U pip wheel + - run: pip install -r requirements-ci.txt -r requirements-cidb.txt + - run: $(which trial) --reporter=text --rterrors buildbot.test buildbot_worker.test From 0e1927b7d132159a663bf864220720c29ff343ab Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Thu, 10 Aug 2023 21:44:36 +0200 Subject: [PATCH 17/68] Conditional update of graphql-core from 3.2.3 to 3.3.0a3 for Python 3.12 --- requirements-ci.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements-ci.txt b/requirements-ci.txt index a580e86aa47c..74349ea4bb00 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -49,7 +49,8 @@ extras==1.0.0 fixtures==4.1.0 funcsigs==1.0.2 future==0.18.3 -graphql-core==3.2.3 +graphql-core==3.3.0a3; python_version >= "3.12" # pyup: ignore (temporary switch to PRE-RELEASE version; remove this once 3.3.0 or newer is released as RELEASE version) +graphql-core==3.2.3; python_version < "3.12" greenlet==3.0.1 hvac==1.2.1 hyperlink==21.0.0 From 43f8f98ed2d9f598c057f02956d19536e2f31c54 Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Sun, 19 Nov 2023 21:05:23 +0100 Subject: [PATCH 18/68] plugins: importlib.metadata use new entry_points interface dict interface is dropped since Python 3.12 File "d:\a\buildbot\buildbot\master\buildbot\test\integration\interop\test_commandmixin.py", line 49, in test_commandmixin yield self.setup_config() File "C:\hostedtoolcache\windows\Python\3.12.0\x64\Lib\site-packages\twisted\internet\defer.py", line 2000, in _inlineCallbacks result = context.run(gen.send, result) File "d:\a\buildbot\buildbot\master\buildbot\test\integration\interop\test_commandmixin.py", line 37, in setup_config schedulers.AnyBranchScheduler(name="sched", builderNames=["testy"]) File "d:\a\buildbot\buildbot\master\buildbot\plugins\db.py", line 291, in __getattr__ raise AttributeError(str(e)) from e builtins.AttributeError: Unknown component name: AnyBranchScheduler --- master/buildbot/plugins/db.py | 7 ++--- master/buildbot/test/__init__.py | 4 --- master/buildbot/test/util/www.py | 3 ++- master/buildbot/util/importlib_compat.py | 33 ++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 master/buildbot/util/importlib_compat.py diff --git a/master/buildbot/plugins/db.py b/master/buildbot/plugins/db.py index f08833e9993b..15c6ebf8740c 100644 --- a/master/buildbot/plugins/db.py +++ b/master/buildbot/plugins/db.py @@ -25,6 +25,7 @@ from buildbot.errors import PluginDBError from buildbot.interfaces import IPlugin +from buildbot.util.importlib_compat import entry_points_get # Base namespace for Buildbot specific plugins _NAMESPACE_BASE = 'buildbot' @@ -34,8 +35,8 @@ def find_distribution_info(entry_point_name, entry_point_group): for distribution in distributions(): # each distribution can have many entry points try: - for ep in distribution.entry_points: - if ep.name == entry_point_name and ep.group == entry_point_group: + for ep in entry_points_get(distribution.entry_points, entry_point_group): + if ep.name == entry_point_name: return (distribution.metadata['Name'], distribution.metadata['Version']) except KeyError as exc: raise PluginDBError("Plugin info was found, but it is invalid.") from exc @@ -242,7 +243,7 @@ def _load_entry(self, entry): def _tree(self): if self._real_tree is None: self._real_tree = _NSNode() - entries = entry_points().get(self._group, []) + entries = entry_points_get(entry_points(), self._group) for entry in entries: self._real_tree.add(entry.name, _PluginEntry(self._group, entry, diff --git a/master/buildbot/test/__init__.py b/master/buildbot/test/__init__.py index be7a914cd295..bd72ad116828 100644 --- a/master/buildbot/test/__init__.py +++ b/master/buildbot/test/__init__.py @@ -129,10 +129,6 @@ warnings.filterwarnings('ignore', "'pipes' is deprecated and slated for removal in Python 3.13", category=DeprecationWarning) -# shown on Python 3.7 on Windows -warnings.filterwarnings('ignore', "SelectableGroups dict interface is deprecated. Use select.", - category=DeprecationWarning) - # boto3 shows this warning when on old Python warnings.filterwarnings('ignore', ".*Boto3 will no longer support Python .*", category=Warning) diff --git a/master/buildbot/test/util/www.py b/master/buildbot/test/util/www.py index df3ab34443c6..dc312828842e 100644 --- a/master/buildbot/test/util/www.py +++ b/master/buildbot/test/util/www.py @@ -29,6 +29,7 @@ from buildbot.test.fake import fakemaster from buildbot.util import bytes2unicode from buildbot.util import unicode2bytes +from buildbot.util.importlib_compat import entry_points_get from buildbot.www import auth from buildbot.www import authz @@ -123,7 +124,7 @@ def getSession(self): class RequiresWwwMixin: # mix this into a TestCase to skip if buildbot-www is not installed - if not [ep for ep in entry_points().get('buildbot.www', []) if ep.name == 'base']: + if not [ep for ep in entry_points_get(entry_points(), 'buildbot.www') if ep.name == 'base']: if 'BUILDBOT_TEST_REQUIRE_WWW' in os.environ: raise RuntimeError('$BUILDBOT_TEST_REQUIRE_WWW is set but ' 'buildbot-www is not installed') diff --git a/master/buildbot/util/importlib_compat.py b/master/buildbot/util/importlib_compat.py new file mode 100644 index 000000000000..9c0aa3eecbe7 --- /dev/null +++ b/master/buildbot/util/importlib_compat.py @@ -0,0 +1,33 @@ +# This file is part of Buildbot. Buildbot is free software: you can +# redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright Buildbot Team Members + +# This module is for backward compatibility of importlib. + + +def entry_points_get(entry_points, group): + """ Since Python 3.12 dictionary access is removed and replaced by new interface. + see: https://github.com/python/cpython/issues/97781 + """ + if hasattr(entry_points, "select"): + return entry_points.select(group=group) + else: + if isinstance(entry_points, list): + filtered_entry_points = [] + for ep in entry_points: + if ep.group == group: + filtered_entry_points.append(ep) + return filtered_entry_points + else: + return entry_points.get(group, []) From 7987480f271732e190e3184e2e9babff231277fe Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Mon, 20 Nov 2023 23:07:10 +0100 Subject: [PATCH 19/68] test: Disable twisted generated warning on Python 3.12 --- master/buildbot/test/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/master/buildbot/test/__init__.py b/master/buildbot/test/__init__.py index bd72ad116828..878de08a8909 100644 --- a/master/buildbot/test/__init__.py +++ b/master/buildbot/test/__init__.py @@ -148,3 +148,9 @@ # SQLALCHEMY_SILENCE_UBER_WARNING=1 to silence this message. (Background on SQLAlchemy 2.0 # at: https://sqlalche.me/e/b8d9) (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9) warnings.filterwarnings("ignore", category=RemovedIn20Warning) + +# This warning is generated by twisted with Python 3.12. +# Should be fixed by https://github.com/twisted/twisted/pull/12027 +warnings.filterwarnings('ignore', r"the \(type, exc, tb\) signature of throw\(\) is deprecated, " + "use the single-arg signature instead", + category=DeprecationWarning) From 688cfb808c1507ef35170fc4a53cfd75d98d2651 Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Mon, 27 Nov 2023 21:31:44 +0100 Subject: [PATCH 20/68] Replace deprecated usage of utcnow and utcfromtimestamp from datetime Fixes #7223 --- master/buildbot/__init__.py | 4 ++-- master/buildbot/changes/gerritchangesource.py | 4 ++-- master/buildbot/changes/mail.py | 2 +- master/buildbot/configurators/janitor.py | 2 +- master/buildbot/schedulers/timed.py | 3 ++- .../test/unit/changes/test_gerritchangesource.py | 6 +++--- master/buildbot/test/unit/test_util.py | 2 +- .../test/unit/util/test_httpclientservice.py | 2 +- master/buildbot/test/unit/www/test_service.py | 2 +- master/buildbot/www/rest.py | 2 +- master/buildbot/www/service.py | 2 +- pkg/buildbot_pkg.py | 4 ++-- worker/buildbot_worker/__init__.py | 12 ++++++++++-- 13 files changed, 28 insertions(+), 19 deletions(-) diff --git a/master/buildbot/__init__.py b/master/buildbot/__init__.py index c7640ce56a1a..0681ee8d5165 100644 --- a/master/buildbot/__init__.py +++ b/master/buildbot/__init__.py @@ -53,7 +53,7 @@ def mTimeVersion(init_file): for root, _, files in os.walk(cwd): for f in files: m = max(os.path.getmtime(os.path.join(root, f)), m) - d = datetime.datetime.utcfromtimestamp(m) + d = datetime.datetime.fromtimestamp(m, datetime.timezone.utc) return d.strftime("%Y.%m.%d") @@ -80,7 +80,7 @@ def getVersionFromArchiveId(git_archive_id='$Format:%ct %d$'): # archived revision is not tagged, use the commit date tstamp = git_archive_id.strip().split()[0] - d = datetime.datetime.utcfromtimestamp(int(tstamp)) + d = datetime.datetime.fromtimestamp(int(tstamp), datetime.timezone.utc) return d.strftime('%Y.%m.%d') return None diff --git a/master/buildbot/changes/gerritchangesource.py b/master/buildbot/changes/gerritchangesource.py index 3971173603d6..308188e035f0 100644 --- a/master/buildbot/changes/gerritchangesource.py +++ b/master/buildbot/changes/gerritchangesource.py @@ -543,7 +543,7 @@ def reconfigService(self, @staticmethod def now(): """patchable now (datetime is not patchable as builtin)""" - return datetime.datetime.utcnow() + return datetime.datetime.now(datetime.timezone.utc) @defer.inlineCallbacks def poll(self): @@ -553,7 +553,7 @@ def poll(self): # the last event time to some historical look-back last_event = self.now() - datetime.timedelta(days=self._first_fetch_lookback) else: - last_event = datetime.datetime.utcfromtimestamp(last_event_ts) + last_event = datetime.datetime.fromtimestamp(last_event_ts, datetime.timezone.utc) last_event_formatted = last_event.strftime("%Y-%m-%d %H:%M:%S") if self.debug: diff --git a/master/buildbot/changes/mail.py b/master/buildbot/changes/mail.py index e9907c9aa5bc..6f39f3880a5a 100644 --- a/master/buildbot/changes/mail.py +++ b/master/buildbot/changes/mail.py @@ -116,7 +116,7 @@ def parse(self, m, prefix=None): else: when = mktime_tz(dateTuple) - theTime = datetime.datetime.utcfromtimestamp(float(when)) + theTime = datetime.datetime.fromtimestamp(float(when), datetime.timezone.utc) rev = theTime.strftime('%Y-%m-%d %H:%M:%S') catRE = re.compile(r'^Category:\s*(\S.*)') diff --git a/master/buildbot/configurators/janitor.py b/master/buildbot/configurators/janitor.py index adfe81121867..27924edb2950 100644 --- a/master/buildbot/configurators/janitor.py +++ b/master/buildbot/configurators/janitor.py @@ -32,7 +32,7 @@ def now(): """patchable now (datetime is not patchable as builtin)""" - return datetime.datetime.utcnow() + return datetime.datetime.now(datetime.timezone.utc) class LogChunksJanitor(BuildStep): diff --git a/master/buildbot/schedulers/timed.py b/master/buildbot/schedulers/timed.py index a36fbb92689d..3318d28c1bf1 100644 --- a/master/buildbot/schedulers/timed.py +++ b/master/buildbot/schedulers/timed.py @@ -286,7 +286,8 @@ def now(self): def current_utc_offset(self, tm): return ( - datetime.datetime.fromtimestamp(tm) - datetime.datetime.utcfromtimestamp(tm) + datetime.datetime.fromtimestamp(tm).replace(tzinfo=datetime.timezone.utc) + - datetime.datetime.fromtimestamp(tm, datetime.timezone.utc) ).total_seconds() @defer.inlineCallbacks diff --git a/master/buildbot/test/unit/changes/test_gerritchangesource.py b/master/buildbot/test/unit/changes/test_gerritchangesource.py index 7ecf075373df..580b34238694 100644 --- a/master/buildbot/test/unit/changes/test_gerritchangesource.py +++ b/master/buildbot/test/unit/changes/test_gerritchangesource.py @@ -688,10 +688,10 @@ def test_lineReceived_patchset_created(self): fakedb.Object(id=self.OBJECTID, name='GerritEventLogPoller:gerrit', class_name='GerritEventLogPoller')]) yield self.newChangeSource(get_files=True) - self.changesource.now = lambda: datetime.datetime.utcfromtimestamp( - self.NOW_TIMESTAMP) + self.changesource.now = lambda: datetime.datetime.fromtimestamp( + self.NOW_TIMESTAMP, datetime.timezone.utc) thirty_days_ago = ( - datetime.datetime.utcfromtimestamp(self.NOW_TIMESTAMP) + datetime.datetime.fromtimestamp(self.NOW_TIMESTAMP, datetime.timezone.utc) - datetime.timedelta(days=30)) self._http.expect(method='get', ep='/plugins/events-log/events/', params={'t1': thirty_days_ago.strftime("%Y-%m-%d %H:%M:%S")}, diff --git a/master/buildbot/test/unit/test_util.py b/master/buildbot/test/unit/test_util.py index dfd4e1f42a0a..8e2eb482a9d6 100644 --- a/master/buildbot/test/unit/test_util.py +++ b/master/buildbot/test/unit/test_util.py @@ -204,7 +204,7 @@ def test_UTC(self): datetime.timedelta(0)) self.assertEqual(util.UTC.dst(datetime.datetime.now()), datetime.timedelta(0)) - self.assertEqual(util.UTC.tzname(datetime.datetime.utcnow()), "UTC") + self.assertEqual(util.UTC.tzname(datetime.datetime.now(datetime.timezone.utc)), "UTC") def test_epoch2datetime(self): self.assertEqual(util.epoch2datetime(0), diff --git a/master/buildbot/test/unit/util/test_httpclientservice.py b/master/buildbot/test/unit/util/test_httpclientservice.py index ce06f3331fe6..a7c21ae781f8 100644 --- a/master/buildbot/test/unit/util/test_httpclientservice.py +++ b/master/buildbot/test/unit/util/test_httpclientservice.py @@ -395,7 +395,7 @@ def test_put_content_with_json(self): @defer.inlineCallbacks def test_put_content_with_json_datetime(self): exp_content_json = {"json_received": {"a": 'b', "ts": 12}} - dt = datetime.datetime.utcfromtimestamp(12) + dt = datetime.datetime.fromtimestamp(12, datetime.timezone.utc) self.expect('post', '/', json={"a": 'b', "ts": dt}, content_json=exp_content_json) res = yield self._http.post('/', json={"a": 'b', "ts": dt}) diff --git a/master/buildbot/test/unit/www/test_service.py b/master/buildbot/test/unit/www/test_service.py index f70870858dfe..1798502f34a3 100644 --- a/master/buildbot/test/unit/www/test_service.py +++ b/master/buildbot/test/unit/www/test_service.py @@ -246,7 +246,7 @@ def test_getSession_from_correct_jwt(self): def test_getSession_from_expired_jwt(self): # expired one week ago - exp = datetime.datetime.utcnow() - datetime.timedelta(weeks=1) + exp = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(weeks=1) exp = calendar.timegm(datetime.datetime.timetuple(exp)) payload = {'user_info': {'some': 'payload'}, 'exp': exp} uid = jwt.encode(payload, self.SECRET, algorithm=service.SESSION_SECRET_ALGORITHM) diff --git a/master/buildbot/www/rest.py b/master/buildbot/www/rest.py index 0685f8bbade9..830907791228 100644 --- a/master/buildbot/www/rest.py +++ b/master/buildbot/www/rest.py @@ -337,7 +337,7 @@ def writeError(msg, errcode=404, jsonrpccode=None): # set up caching if self.cache_seconds: - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.timezone.utc) expires = now + datetime.timedelta(seconds=self.cache_seconds) expiresBytes = unicode2bytes( expires.strftime("%a, %d %b %Y %H:%M:%S GMT")) diff --git a/master/buildbot/www/service.py b/master/buildbot/www/service.py index 33b825085113..f684b2f86d59 100644 --- a/master/buildbot/www/service.py +++ b/master/buildbot/www/service.py @@ -132,7 +132,7 @@ def uid(self): This should actually only be used for cookie generation """ - exp = datetime.datetime.utcnow() + self.expDelay + exp = datetime.datetime.now(datetime.timezone.utc) + self.expDelay claims = { 'user_info': self.user_info, # Note that we use JWT standard 'exp' field to implement session expiration diff --git a/pkg/buildbot_pkg.py b/pkg/buildbot_pkg.py index d2b76553de1f..5c3dff68cbcd 100644 --- a/pkg/buildbot_pkg.py +++ b/pkg/buildbot_pkg.py @@ -80,7 +80,7 @@ def mTimeVersion(init_file): for root, dirs, files in os.walk(cwd): for f in files: m = max(os.path.getmtime(os.path.join(root, f)), m) - d = datetime.datetime.utcfromtimestamp(m) + d = datetime.datetime.fromtimestamp(m, datetime.timezone.utc) return d.strftime("%Y.%m.%d") @@ -107,7 +107,7 @@ def getVersionFromArchiveId(git_archive_id='$Format:%ct %d$'): # archived revision is not tagged, use the commit date tstamp = git_archive_id.strip().split()[0] - d = datetime.datetime.utcfromtimestamp(int(tstamp)) + d = datetime.datetime.fromtimestamp(int(tstamp), datetime.timezone.utc) return d.strftime('%Y.%m.%d') return None diff --git a/worker/buildbot_worker/__init__.py b/worker/buildbot_worker/__init__.py index 6f92fcd2aed8..d715dbcc3e05 100644 --- a/worker/buildbot_worker/__init__.py +++ b/worker/buildbot_worker/__init__.py @@ -24,6 +24,7 @@ import datetime import os import re +import sys from subprocess import PIPE from subprocess import STDOUT from subprocess import Popen @@ -59,7 +60,11 @@ def mTimeVersion(init_file): for root, _, files in os.walk(cwd): for f in files: m = max(os.path.getmtime(os.path.join(root, f)), m) - d = datetime.datetime.utcfromtimestamp(m) + + if sys.version_info >= (3, 3): + d = datetime.datetime.fromtimestamp(m, datetime.timezone.utc) + else: + d = datetime.datetime.utcfromtimestamp(m) return d.strftime("%Y.%m.%d") @@ -86,7 +91,10 @@ def getVersionFromArchiveId(git_archive_id='$Format:%ct %d$'): # archived revision is not tagged, use the commit date tstamp = git_archive_id.strip().split()[0] - d = datetime.datetime.utcfromtimestamp(int(tstamp)) + if sys.version_info >= (3, 3): + d = datetime.datetime.fromtimestamp(int(tstamp), datetime.timezone.utc) + else: + d = datetime.datetime.utcfromtimestamp(int(tstamp)) return d.strftime('%Y.%m.%d') return None From c0e56bcf7be2bb6aa3536b437902b0809ecdcf5c Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Fri, 1 Dec 2023 20:28:27 +0100 Subject: [PATCH 21/68] test: Disable graphql-core generated warning on Python 3.12 --- master/buildbot/test/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/master/buildbot/test/__init__.py b/master/buildbot/test/__init__.py index 878de08a8909..a26fcdcc9946 100644 --- a/master/buildbot/test/__init__.py +++ b/master/buildbot/test/__init__.py @@ -154,3 +154,8 @@ warnings.filterwarnings('ignore', r"the \(type, exc, tb\) signature of throw\(\) is deprecated, " "use the single-arg signature instead", category=DeprecationWarning) + +# This warning is generated by graphql-core with Python 3.12. +# See https://github.com/graphql-python/graphql-core/issues/211 +warnings.filterwarnings('ignore', "'typing.ByteString' is deprecated and slated for removal in " + "Python 3.14", category=DeprecationWarning) From 52a61c1e2e04fa4dab565143ed74b723351bf630 Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Tue, 5 Dec 2023 21:47:40 +0100 Subject: [PATCH 22/68] test: Disable datetime timezone warnings on Python 3.12 --- master/buildbot/test/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/master/buildbot/test/__init__.py b/master/buildbot/test/__init__.py index a26fcdcc9946..f89eb29cf80d 100644 --- a/master/buildbot/test/__init__.py +++ b/master/buildbot/test/__init__.py @@ -159,3 +159,9 @@ # See https://github.com/graphql-python/graphql-core/issues/211 warnings.filterwarnings('ignore', "'typing.ByteString' is deprecated and slated for removal in " "Python 3.14", category=DeprecationWarning) + +# When using Python 3.12, this generates some dependent package +warnings.filterwarnings('ignore', r"datetime.datetime.utcnow\(\) is deprecated and scheduled for " + r"removal in a future version. Use timezone-aware objects to represent " + r"datetimes in UTC: datetime.datetime.now\(datetime.UTC\).", + category=DeprecationWarning) From 967a8626ab884775553cfe2d497abb4cec131b15 Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Wed, 6 Dec 2023 00:00:03 +0100 Subject: [PATCH 23/68] tests: Address deprecation warning in shutil.rmtree(onerror=...) Ref: https://docs.python.org/3.12/library/shutil.html#shutil.rmtree https://github.com/python/cpython/issues/102828 --- master/buildbot/util/private_tempdir.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/master/buildbot/util/private_tempdir.py b/master/buildbot/util/private_tempdir.py index ee6c4eae3d07..b2dba0b46a63 100644 --- a/master/buildbot/util/private_tempdir.py +++ b/master/buildbot/util/private_tempdir.py @@ -16,6 +16,7 @@ import os import shutil import stat +import sys import tempfile @@ -46,5 +47,8 @@ def remove_readonly(func, path, _): """ os.chmod(path, stat.S_IWRITE) func(path) - shutil.rmtree(self.name, onerror=remove_readonly) + if sys.version_info >= (3, 12): + shutil.rmtree(self.name, onexc=remove_readonly) + else: + shutil.rmtree(self.name, onerror=remove_readonly) self._cleanup_needed = False From 6e3c48e15763ec59a83c80e066fe58d2be2f751e Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Wed, 6 Dec 2023 17:13:39 +0100 Subject: [PATCH 24/68] Fix asyncio changes in Python 3.12 Problem observed: - failing tests buildbot.test.integration.test_worker_proxy - failing with stack traces like [ERROR] Traceback (most recent call last): File "C:\P\.venv\Lib\site-packages\twisted\internet\defer.py", line 1996, in _inlineCallbacks result = context.run( File "C:\P\.venv\Lib\site-packages\twisted\python\failure.py", line 519, in throwExceptionIntoGenerator return g.throw(self.type, self.value, self.tb) File "C:\P\buildbot\master\buildbot\test\integration\interop\test_transfer.py", line 197, in test_no_exist_multiple_file_upload yield self.setup_config_single_step(step) File "C:\P\.venv\Lib\site-packages\twisted\internet\defer.py", line 1996, in _inlineCallbacks result = context.run( File "C:\P\.venv\Lib\site-packages\twisted\python\failure.py", line 519, in throwExceptionIntoGenerator return g.throw(self.type, self.value, self.tb) File "C:\P\buildbot\master\buildbot\test\integration\interop\test_transfer.py", line 130, in setup_config_single_step yield self.setup_master(c) File "C:\P\.venv\Lib\site-packages\twisted\internet\defer.py", line 1996, in _inlineCallbacks result = context.run( File "C:\P\.venv\Lib\site-packages\twisted\python\failure.py", line 519, in throwExceptionIntoGenerator return g.throw(self.type, self.value, self.tb) File "C:\P\buildbot\master\buildbot\test\integration\test_worker_proxy.py", line 159, in setup_master yield super().setup_master(config_dict, startWorker, File "C:\P\.venv\Lib\site-packages\twisted\internet\defer.py", line 2000, in _inlineCallbacks result = context.run(gen.send, result) File "C:\P\buildbot\master\buildbot\test\util\integration.py", line 249, in setup_master self.w = Worker( File "C:\P\buildbot\worker\buildbot_worker\pb.py", line 667, in __init__ proxy_endpoint = clientFromString(reactor, proxy_connection_string) File "C:\P\.venv\Lib\site-packages\twisted\internet\endpoints.py", line 2137, in clientFromString kwargs = _clientParsers[name](*args, **kwargs) File "C:\P\.venv\Lib\site-packages\twisted\internet\endpoints.py", line 1861, in _parseClientTCP kwargs["port"] = int(args[1]) builtins.ValueError: invalid literal for int() with base 10: 'test_worker_proxy_stdout_11208.txt' But real problem seems to be in start of proxy Exception Raised: There is no current event loop Traceback (most recent call last): File "C:\P\buildbot\master\buildbot\test\integration\test_worker_proxy.py", line 92, in run_proxy loop = asyncio.get_event_loop() ^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Python312\Lib\asyncio\events.py", line 693, in get_event_loop warnings.warn('There is no current event loop', DeprecationWarning: There is no current event loop Fix is inspired by https://github.com/danigm/jupyter_client/blob/9c1ddf4a50a71181e60be7045d954a3fafa91357/jupyter_client/utils.py#L14-L29 --- .../test/integration/test_worker_proxy.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/master/buildbot/test/integration/test_worker_proxy.py b/master/buildbot/test/integration/test_worker_proxy.py index 3ac22cd51b2f..f62551bdca61 100644 --- a/master/buildbot/test/integration/test_worker_proxy.py +++ b/master/buildbot/test/integration/test_worker_proxy.py @@ -18,6 +18,7 @@ import os import signal import socket +import sys from twisted.internet import defer @@ -89,12 +90,21 @@ def run_proxy(queue): try: try: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() except RuntimeError: - # We can get RuntimeError due to current thread being not main thread on Python 3.8. - # It's not clear why that happens, so work around it. - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) + # https://github.com/python/cpython/issues/83710 + if sys.version_info <= (3, 10, 8): + # Workaround for bugs.python.org/issue39529. + try: + loop = asyncio.get_event_loop_policy().get_event_loop() + except RuntimeError: + # We can get RuntimeError due to current thread being not main thread + # on Python 3.8. It's not clear why that happens, so work around it. + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + else: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) coro = asyncio.start_server(handle_client, host="127.0.0.1") server = loop.run_until_complete(coro) From 119645f3a4122eb8878937cdc4b3336d3ba4af64 Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Fri, 15 Dec 2023 01:00:31 +0100 Subject: [PATCH 25/68] test_asyncio_gather: Fix test [ERROR] Traceback (most recent call last): File "C:\P\.venv\Lib\site-packages\twisted\internet\defer.py", line 1996, in _inlineCallbacks result = context.run( File "C:\P\.venv\Lib\site-packages\twisted\python\failure.py", line 519, in throwExceptionIntoGenerator return g.throw(self.type, self.value, self.tb) File "C:\P\buildbot\master\buildbot\test\unit\test_asyncio.py", line 83, in test_asyncio_gather yield as_deferred(f1) File "C:\P\.venv\Lib\site-packages\twisted\internet\defer.py", line 1248, in adapt extracted: _SelfResultT | Failure = result.result() File "C:\P\buildbot\master\buildbot\test\unit\test_asyncio.py", line 73, in main_coro await asyncio.gather(*dl) File "C:\Python312\Lib\asyncio\tasks.py", line 674, in _wrap_awaitable return await awaitable builtins.TypeError: __await__() returned non-iterator of type '_asyncio.Future' buildbot.test.unit.test_asyncio.TestAsyncioTestLoop.test_asyncio_gather --- master/buildbot/test/unit/test_asyncio.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/master/buildbot/test/unit/test_asyncio.py b/master/buildbot/test/unit/test_asyncio.py index e6e18b98d836..355edc4ee73f 100644 --- a/master/buildbot/test/unit/test_asyncio.py +++ b/master/buildbot/test/unit/test_asyncio.py @@ -40,8 +40,7 @@ async def coro1(): d1.callback(None) return defer.Deferred.fromFuture(f) - @defer.inlineCallbacks - def test_asyncio_gather(self): + async def test_asyncio_gather(self): self.calls = 0 async def coro1(): From 8aaa9abcb3bd7280d1ab9cb829b0d81a0839b2a8 Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Mon, 18 Dec 2023 08:11:14 +0100 Subject: [PATCH 26/68] test: Disable deprecation warning of os.fork on Python3.12 Temporary workaround for #7276 --- master/buildbot/test/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/master/buildbot/test/__init__.py b/master/buildbot/test/__init__.py index f89eb29cf80d..ce6c13358107 100644 --- a/master/buildbot/test/__init__.py +++ b/master/buildbot/test/__init__.py @@ -165,3 +165,11 @@ r"removal in a future version. Use timezone-aware objects to represent " r"datetimes in UTC: datetime.datetime.now\(datetime.UTC\).", category=DeprecationWarning) + +# Python3.12 generates deprecation warnings like: +# "This process (pid=6558) is multi-threaded, use of fork() may lead to deadlocks in the child." +# Tracked in https://github.com/buildbot/buildbot/issues/7276 +warnings.filterwarnings("ignore", + r"This process \(pid=\d+\) is multi-threaded, use of fork\(\) may lead " + r"to deadlocks in the child\.", + category=DeprecationWarning) From 8205bd1e55fd35f5a974f3d063b05fa2e9d1454d Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 26 Dec 2023 17:46:42 +0200 Subject: [PATCH 27/68] Update spelling allowlist --- common/code_spelling_ignore_words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/code_spelling_ignore_words.txt b/common/code_spelling_ignore_words.txt index da90e620d452..20c4a15210a2 100644 --- a/common/code_spelling_ignore_words.txt +++ b/common/code_spelling_ignore_words.txt @@ -682,6 +682,7 @@ ie iff i'm impl +importlib ina incrementing indextemplates From 829236070d94a9cbdce433706a1b5ffe26408f3c Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 26 Dec 2023 16:42:05 +0200 Subject: [PATCH 28/68] asyncio: Don't override twisted.internet.defer.Deferred.__await__ This causes hard to diagnose test failures on Python 3.12. trial worker process just dies without any output and debugging requires adding additional debugging statements to Twisted code. defer.Deferred.__await__ is not a documented extension point so it may break at any point in the future. --- master/buildbot/asyncio.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/master/buildbot/asyncio.py b/master/buildbot/asyncio.py index 6fc437a41989..9c0f8a9e939a 100644 --- a/master/buildbot/asyncio.py +++ b/master/buildbot/asyncio.py @@ -22,23 +22,6 @@ from twisted.internet import defer -def deferred_await(self): - # if a deferred is awaited from a asyncio loop context, we must return - # the future wrapper, but if it is awaited from normal twisted loop - # we must return self. - try: - loop = asyncio.get_event_loop() - except RuntimeError: - return self - - if isinstance(loop, AsyncIOLoopWithTwisted): - return self.asFuture(asyncio.get_event_loop()) - return self - - -defer.Deferred.__await__ = deferred_await - - def as_deferred(f): return asyncio.get_event_loop().as_deferred(f) From c72e4ce833a0ad47dd670762179907ccce8cfe86 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 26 Dec 2023 17:56:24 +0200 Subject: [PATCH 29/68] bbtravis: Work around test failure on Python 3.12 Buildbot already works around a potential bug in Twisted: https://github.com/twisted/twisted/issues/11840. Unfortunately, the workaround done in is no longer enough on Python 3.12 dcbc28d1431e1aae892f7fdc0e266253365bc11e. Seems like reinstalling Buildbot master again fixes the issue. --- .bbtravis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.bbtravis.yml b/.bbtravis.yml index d84954283742..badec0280e0d 100644 --- a/.bbtravis.yml +++ b/.bbtravis.yml @@ -86,6 +86,9 @@ install: - /tmp/bbvenv/bin/pip install -U pip wheel - condition: TESTS not in ("dev_virtualenv", "smokes", "e2e_react_whl", "e2e_react_tgz", "trial_worker") cmd: /tmp/bbvenv/bin/pip install -r requirements-ci.txt + # On python 3.12 workaround done in dcbc28d1431e1aae892f7fdc0e266253365bc11e is no longer enough. + - condition: TESTS not in ("dev_virtualenv", "smokes", "e2e_react_whl", "e2e_react_tgz", "trial_worker") + cmd: /tmp/bbvenv/bin/pip install -e master -e worker - condition: TESTS == "dev_virtualenv" cmd: /tmp/bbvenv/bin/pip install -r requirements-ci.txt -r requirements-ciworker.txt -r requirements-cidocs.txt - condition: TESTS == "trial_worker" From ace19ac6f5a465669d302742c80eee920364682f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Dec 2023 16:46:03 +0100 Subject: [PATCH 30/68] bbtravis: also test on python 3.12 --- .bbtravis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.bbtravis.yml b/.bbtravis.yml index badec0280e0d..fddc6c14573e 100644 --- a/.bbtravis.yml +++ b/.bbtravis.yml @@ -41,6 +41,7 @@ matrix: - env: PYTHON=3.9 TWISTED=latest SQLALCHEMY=latest NUM_CPU=2 TESTS=trial - env: PYTHON=3.10 TWISTED=latest SQLALCHEMY=latest NUM_CPU=2 TESTS=trial - env: PYTHON=3.11 TWISTED=latest SQLALCHEMY=latest NUM_CPU=2 TESTS=trial + - env: PYTHON=3.12 TWISTED=latest SQLALCHEMY=latest NUM_CPU=2 TESTS=trial - env: PYTHON=3.9 TWISTED=latest SQLALCHEMY=latest TESTS=dev_virtualenv From 14082b90484bfce141bdc1da161e6909a726a3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 3 Dec 2023 16:30:05 +0100 Subject: [PATCH 31/68] add python 3.12 to ci matrix This version was released in october, so we should also support this in buildbot --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6904bb64782..901b8e2aa265 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - uses: "actions/checkout@v2" From 15ceb4dae886da490898f54b757062f0891f0112 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 26 Dec 2023 19:07:01 +0200 Subject: [PATCH 32/68] Release notes for 3.10.1 --- master/docs/relnotes/index.rst | 13 +++++++++++++ ...ldset-parent-buildid-for-data-api-message.bugfix | 1 - newsfragments/allow-twisted-dep-23-10.bugfix | 1 - newsfragments/tarfile-pep706.bugfix | 1 - newsfragments/www-package-build-older-python.bugfix | 1 - 5 files changed, 13 insertions(+), 4 deletions(-) delete mode 100644 newsfragments/add-missing-buildset-parent-buildid-for-data-api-message.bugfix delete mode 100644 newsfragments/allow-twisted-dep-23-10.bugfix delete mode 100644 newsfragments/tarfile-pep706.bugfix delete mode 100644 newsfragments/www-package-build-older-python.bugfix diff --git a/master/docs/relnotes/index.rst b/master/docs/relnotes/index.rst index fdcb740209e6..e336811c4668 100644 --- a/master/docs/relnotes/index.rst +++ b/master/docs/relnotes/index.rst @@ -8,6 +8,19 @@ Release Notes .. towncrier release notes start + +Buildbot ``3.10.1`` ( ``2023-12-26`` ) +====================================== + +Bug fixes +--------- + +- Fixed support for Twisted 23.10 and Python 3.12. +- Fixed Data API to have "parent_buildid" key-value pair in messages for rebuilt buildsets (:issue `7222`). +- Improved security of tarfile extraction to help avoid CVE-2007-4559. See more details in https://peps.python.org/pep-0706/. Buildbot uses filter='data' now. (:issue:`7294`) +- Fixed web frontend package build on certain Python versions (e.g. 3.9). + + Buildbot ``3.10.0`` ( ``2023-12-04`` ) ====================================== diff --git a/newsfragments/add-missing-buildset-parent-buildid-for-data-api-message.bugfix b/newsfragments/add-missing-buildset-parent-buildid-for-data-api-message.bugfix deleted file mode 100644 index 50d6145cfc66..000000000000 --- a/newsfragments/add-missing-buildset-parent-buildid-for-data-api-message.bugfix +++ /dev/null @@ -1 +0,0 @@ -Rebuilt buildsets Data API messages now have "parent_buildid" key-value pair (:issue `7222`). diff --git a/newsfragments/allow-twisted-dep-23-10.bugfix b/newsfragments/allow-twisted-dep-23-10.bugfix deleted file mode 100644 index 383df9578992..000000000000 --- a/newsfragments/allow-twisted-dep-23-10.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix support for Twisted 23.10 diff --git a/newsfragments/tarfile-pep706.bugfix b/newsfragments/tarfile-pep706.bugfix deleted file mode 100644 index 0d398f780dde..000000000000 --- a/newsfragments/tarfile-pep706.bugfix +++ /dev/null @@ -1 +0,0 @@ -Improved security of tarfile extraction to help avoid CVE-2007-4559. See more details in https://peps.python.org/pep-0706/. Buildbot uses filter='data' now. (:issue:`7294`) diff --git a/newsfragments/www-package-build-older-python.bugfix b/newsfragments/www-package-build-older-python.bugfix deleted file mode 100644 index 299751f1e7d4..000000000000 --- a/newsfragments/www-package-build-older-python.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix web frontend package build on certain Python versions (e.g. 3.9). From 1ff3a45131e054cf095081c29474b7ebb8b8b5b3 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 26 Dec 2023 19:40:11 +0200 Subject: [PATCH 33/68] bbtravis: Add e2e tests for Python 3.12 --- .bbtravis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.bbtravis.yml b/.bbtravis.yml index fddc6c14573e..e38d1247af1d 100644 --- a/.bbtravis.yml +++ b/.bbtravis.yml @@ -32,6 +32,8 @@ matrix: - env: PYTHON=3.9 TWISTED=latest SQLALCHEMY=latest TESTS=smokes NUM_CPU=4 MEMORY_SIZE=2G - env: PYTHON=3.9 TWISTED=latest SQLALCHEMY=latest TESTS=e2e_react_whl NUM_CPU=2 MEMORY_SIZE=2G - env: PYTHON=3.9 TWISTED=latest SQLALCHEMY=latest TESTS=e2e_react_tgz NUM_CPU=2 MEMORY_SIZE=2G + - env: PYTHON=3.12 TWISTED=latest SQLALCHEMY=latest TESTS=e2e_react_whl NUM_CPU=2 MEMORY_SIZE=2G + - env: PYTHON=3.12 TWISTED=latest SQLALCHEMY=latest TESTS=e2e_react_tgz NUM_CPU=2 MEMORY_SIZE=2G # include "ci" string into the name of the status that is eventually submitted to Github, so # that the codecov.io service would wait until this build is finished before creating report. From e8e6293323ef72c31b6b164ee4d08114a6d20eb6 Mon Sep 17 00:00:00 2001 From: Pavol Misik Date: Tue, 26 Dec 2023 19:12:00 +0100 Subject: [PATCH 34/68] Declare Python3.12 compatibility in generated packages of master and worker. --- master/setup.py | 1 + newsfragments/setup-python312-compat.bugfix | 1 + worker/setup.py | 1 + 3 files changed, 3 insertions(+) create mode 100644 newsfragments/setup-python312-compat.bugfix diff --git a/master/setup.py b/master/setup.py index 3c4d614bdbee..8bec42a0cdd1 100755 --- a/master/setup.py +++ b/master/setup.py @@ -154,6 +154,7 @@ def define_plugin_entries(groups): 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', ], 'packages': [ diff --git a/newsfragments/setup-python312-compat.bugfix b/newsfragments/setup-python312-compat.bugfix new file mode 100644 index 000000000000..19092013e17b --- /dev/null +++ b/newsfragments/setup-python312-compat.bugfix @@ -0,0 +1 @@ +setup: Declare Python3.12 compatibility in generated packages of master and worker diff --git a/worker/setup.py b/worker/setup.py index e474591ac442..10ba634295bf 100755 --- a/worker/setup.py +++ b/worker/setup.py @@ -106,6 +106,7 @@ def make_release_tree(self, base_dir, files): 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', ], 'packages': [ From 23e1bbfb70cff72fba59e94e09747a882f506c79 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:29:50 +0200 Subject: [PATCH 35/68] Remove unneeded multiple assignments This makes code easier to read --- master/buildbot/changes/base.py | 3 ++- master/buildbot/changes/mail.py | 3 ++- master/buildbot/data/connector.py | 5 ++--- master/buildbot/data/resultspec.py | 16 +++++++++----- master/buildbot/db/changes.py | 3 ++- master/buildbot/db/pool.py | 3 ++- master/buildbot/locks.py | 3 ++- master/buildbot/mq/simple.py | 3 ++- master/buildbot/process/properties.py | 20 ++++++++++++------ master/buildbot/process/workerforbuilder.py | 6 ++++-- master/buildbot/reporters/telegram.py | 21 +++++++++++++------ master/buildbot/reporters/utils.py | 3 ++- master/buildbot/reporters/words.py | 3 ++- master/buildbot/test/fake/latent.py | 9 ++++++-- master/buildbot/test/fake/machine.py | 6 ++++-- master/buildbot/test/fake/step.py | 4 +++- master/buildbot/test/fake/worker.py | 3 ++- master/buildbot/test/fakedb/base.py | 6 ++++-- master/buildbot/test/fakedb/changes.py | 3 ++- .../test/integration/test_worker_comm.py | 6 ++++-- .../buildbot/test/unit/config/test_master.py | 3 ++- .../buildbot/test/unit/data/test_logchunks.py | 3 ++- master/buildbot/test/unit/db/test_workers.py | 8 +++++-- .../test/unit/scripts/test_checkconfig.py | 6 ++++-- .../test/unit/util/test_lineboundaries.py | 3 ++- master/buildbot/test/unit/worker/test_base.py | 6 ++++-- .../test/unit/worker/test_protocols_pb.py | 8 ++++--- master/buildbot/test/util/www.py | 3 ++- master/buildbot/util/_notifier.py | 3 ++- master/buildbot/util/eventual.py | 6 ++++-- master/buildbot/util/lineboundaries.py | 3 ++- master/buildbot/util/path_expand_user.py | 3 ++- master/buildbot/worker/latent.py | 3 ++- .../buildbot/worker/protocols/manager/base.py | 3 ++- .../test/unit/test_bot_Worker.py | 3 ++- .../test/util/test_lineboundaries.py | 3 ++- worker/buildbot_worker/util/_notifier.py | 3 ++- 37 files changed, 135 insertions(+), 64 deletions(-) diff --git a/master/buildbot/changes/base.py b/master/buildbot/changes/base.py index 3dc23ae81ba3..33593b6e3f17 100644 --- a/master/buildbot/changes/base.py +++ b/master/buildbot/changes/base.py @@ -76,7 +76,8 @@ def checkConfig(self, name=None, pollInterval=60 * 10, pollAtLaunch=False, @defer.inlineCallbacks def reconfigService(self, name=None, pollInterval=60 * 10, pollAtLaunch=False, pollRandomDelayMin=0, pollRandomDelayMax=0): - self.pollInterval, prevPollInterval = pollInterval, self.pollInterval + prevPollInterval = self.pollInterval + self.pollInterval = pollInterval self.pollAtLaunch = pollAtLaunch self.pollRandomDelayMin = pollRandomDelayMin self.pollRandomDelayMax = pollRandomDelayMax diff --git a/master/buildbot/changes/mail.py b/master/buildbot/changes/mail.py index 6f39f3880a5a..1df79c8dd9ec 100644 --- a/master/buildbot/changes/mail.py +++ b/master/buildbot/changes/mail.py @@ -61,7 +61,8 @@ def messageReceived(self, filename): with self.moveToCurDir(filename) as f: chtuple = self.parse_file(f, self.prefix) - src, chdict = None, None + src = None + chdict = None if chtuple: src, chdict = chtuple if chdict: diff --git a/master/buildbot/data/connector.py b/master/buildbot/data/connector.py index cc202b7ff899..2f6f13c19c5c 100644 --- a/master/buildbot/data/connector.py +++ b/master/buildbot/data/connector.py @@ -186,9 +186,8 @@ def checkFields(fields, negOk=False): raise exceptions.InvalidQueryParameter(f"no such field '{k}'") limit = offset = order = fields = None - filters, properties = [], [] - limit = offset = order = fields = None - filters, properties = [], [] + filters = [] + properties = [] for arg in req_args: argStr = bytes2unicode(arg) if argStr == 'order': diff --git a/master/buildbot/data/resultspec.py b/master/buildbot/data/resultspec.py index 025b6df5e8e1..1da28f09d4fa 100644 --- a/master/buildbot/data/resultspec.py +++ b/master/buildbot/data/resultspec.py @@ -346,7 +346,8 @@ def applyToSQLQuery(self, query): return query, count_query def thd_execute(self, conn, q, dictFromRow): - offset, limit = self.offset, self.limit + offset = self.offset + limit = self.limit q, qc = self.applyToSQLQuery(q) res = conn.execute(q) rv = [dictFromRow(row) for row in res.fetchall()] @@ -354,7 +355,9 @@ def thd_execute(self, conn, q, dictFromRow): if qc is not None and (offset or limit): total = conn.execute(qc).scalar() rv = base.ListResult(rv) - rv.offset, rv.total, rv.limit = offset, total, limit + rv.offset = offset + rv.total = total + rv.limit = limit return rv def apply(self, data): @@ -385,10 +388,12 @@ def includeFields(d): # if pagination was applied, then fields, etc. must be empty assert not fields and not order and not filters, \ "endpoint must apply fields, order, and filters if it performs pagination" - offset, total = data.offset, data.total + offset = data.offset + total = data.total limit = data.limit else: - offset, total = None, None + offset = None + total = None limit = None if fields: @@ -445,7 +450,8 @@ def keyFunc(elem, order=self.order): limit = self.limit rv = base.ListResult(data) - rv.offset, rv.total = offset, total + rv.offset = offset + rv.total = total rv.limit = limit return rv diff --git a/master/buildbot/db/changes.py b/master/buildbot/db/changes.py index 6c3a7c6a227b..f3a25972e883 100644 --- a/master/buildbot/db/changes.py +++ b/master/buildbot/db/changes.py @@ -327,7 +327,8 @@ def thd(conn): 'change_properties', 'changes', 'change_users'): remaining = ids_to_delete[:] while remaining: - batch, remaining = remaining[:100], remaining[100:] + batch = remaining[:100] + remaining = remaining[100:] table = self.db.model.metadata.tables[table_name] conn.execute( table.delete(table.c.changeid.in_(batch))) diff --git a/master/buildbot/db/pool.py b/master/buildbot/db/pool.py index 5eea4d083df3..91109cdc987c 100644 --- a/master/buildbot/db/pool.py +++ b/master/buildbot/db/pool.py @@ -55,7 +55,8 @@ def wrap(callable, *args, **kwargs): locals = frame.f_locals # invent a unique ID for the description - id, _debug_id = _debug_id, _debug_id + 1 + id = _debug_id + _debug_id = _debug_id + 1 descr = f"{name}-{id:08x}" diff --git a/master/buildbot/locks.py b/master/buildbot/locks.py index 29504b9670da..8d0d96837c84 100644 --- a/master/buildbot/locks.py +++ b/master/buildbot/locks.py @@ -84,7 +84,8 @@ def _find_waiting(self, requester): def isAvailable(self, requester, access): """ Return a boolean whether the lock is available for claiming """ debuglog(f"{self} isAvailable({requester}, {access}): self.owners={repr(self.owners)}") - num_excl, num_counting = self._claimed_excl, self._claimed_counting + num_excl = self._claimed_excl + num_counting = self._claimed_counting if not access.count: return True diff --git a/master/buildbot/mq/simple.py b/master/buildbot/mq/simple.py index 7e35361131b2..d4c4cef44542 100644 --- a/master/buildbot/mq/simple.py +++ b/master/buildbot/mq/simple.py @@ -90,7 +90,8 @@ def startConsuming(self, callback): self.active = True # invoke for every message that was missed - queue, self.queue = self.queue, [] + queue = self.queue + self.queue = [] for routingKey, data in queue: self.invoke(routingKey, data) diff --git a/master/buildbot/process/properties.py b/master/buildbot/process/properties.py index 57557f38aacd..2e92cd976749 100644 --- a/master/buildbot/process/properties.py +++ b/master/buildbot/process/properties.py @@ -321,7 +321,10 @@ class _OperatorRenderer(RenderableOperatorsMixin, util.ComparableMixin): compare_attrs = ('fn',) def __init__(self, v1, v2, cstr, comparator): - self.v1, self.v2, self.comparator, self.cstr = v1, v2, comparator, cstr + self.v1 = v1 + self.v2 = v2 + self.comparator = comparator + self.cstr = cstr @defer.inlineCallbacks def getRenderingFor(self, props): @@ -641,10 +644,12 @@ def _parse_prop(arg): try: prop, repl = arg.split(":", 1) except ValueError: - prop, repl = arg, None + prop = arg + repl = None if not Interpolate.identifier_re.match(prop): config.error(f"Property name must be alphanumeric for prop Interpolation '{arg}'") - prop = repl = None + prop = None + repl = None return _thePropertyDict, prop, repl @@ -653,7 +658,8 @@ def _parse_secret(arg): try: secret, repl = arg.split(":", 1) except ValueError: - secret, repl = arg, None + secret = arg + repl = None return _SecretIndexer(), secret, repl @staticmethod @@ -682,14 +688,16 @@ def _parse_worker(self, arg): try: prop, repl = arg.split(":", 1) except ValueError: - prop, repl = arg, None + prop = arg + repl = None return _theWorkerPropertyDict, prop, repl def _parse_kw(self, arg): try: kw, repl = arg.split(":", 1) except ValueError: - kw, repl = arg, None + kw = arg + repl = None if not Interpolate.identifier_re.match(kw): config.error(f"Keyword must be alphanumeric for kw Interpolation '{arg}'") kw = repl = None diff --git a/master/buildbot/process/workerforbuilder.py b/master/buildbot/process/workerforbuilder.py index 231eb4d968b0..d50b94fb87a0 100644 --- a/master/buildbot/process/workerforbuilder.py +++ b/master/buildbot/process/workerforbuilder.py @@ -118,12 +118,14 @@ def ping(self): return d def abortPingIfAny(self): - watchers, self.ping_watchers = self.ping_watchers, [] + watchers = self.ping_watchers + self.ping_watchers = [] for d in watchers: d.errback(PingException('aborted ping')) def _pong(self, res): - watchers, self.ping_watchers = self.ping_watchers, [] + watchers = self.ping_watchers + self.ping_watchers = [] for d in watchers: d.callback(res) diff --git a/master/buildbot/reporters/telegram.py b/master/buildbot/reporters/telegram.py index 5e434a41854f..78c66d6871d0 100644 --- a/master/buildbot/reporters/telegram.py +++ b/master/buildbot/reporters/telegram.py @@ -686,9 +686,13 @@ def process_update(self, update): try: data = self.query_cache[int(data)] except ValueError: - text, data, notify = data, {}, None + text = data + data = {} + notify = None except KeyError: - text, data, notify = None, {}, "Sorry, button is no longer valid!" + text = None + data = {} + notify = "Sorry, button is no longer valid!" if original_message: try: self.edit_keyboard( @@ -705,7 +709,9 @@ def process_update(self, update): except KeyError: notify = None else: - text, data, notify = data, {}, None + text = data + data = {} + notify = None data['tquery'] = query self.answer_query(query['id'], notify) message = { @@ -729,7 +735,8 @@ def process_update(self, update): contact = self.getContact(user=user, channel=chat) data['tmessage'] = message - template, contact.template = contact.template, None + template = contact.template + contact.template = None if text.startswith(self.commandPrefix): result = yield contact.handleMessage(text, **data) else: @@ -790,11 +797,13 @@ def send_message(self, chat, message, parse_mode='Markdown', reply_to_message_id = None # we only mark first message as a reply if len(message) <= 4096: - params['text'], message = message, None + params['text'] = message + message = None else: n = message[:4096].rfind('\n') n = n + 1 if n != -1 else 4096 - params['text'], message = message[:n].rstrip(), message[n:].lstrip() + params['text'] = message[:n].rstrip() + message = message[n:].lstrip() if not message and reply_markup is not None: params['reply_markup'] = reply_markup diff --git a/master/buildbot/reporters/utils.py b/master/buildbot/reporters/utils.py index ab630bc4db71..0a1551238d3b 100644 --- a/master/buildbot/reporters/utils.py +++ b/master/buildbot/reporters/utils.py @@ -72,7 +72,8 @@ def getDetailsForBuild(master, build, want_properties=False, want_steps=False, want_previous_build=False, want_logs=False, want_logs_content=False): buildrequest = yield master.data.get(("buildrequests", build['buildrequestid'])) buildset = yield master.data.get(("buildsets", buildrequest['buildsetid'])) - build['buildrequest'], build['buildset'] = buildrequest, buildset + build['buildrequest'] = buildrequest + build['buildset'] = buildset parentbuild = None parentbuilder = None diff --git a/master/buildbot/reporters/words.py b/master/buildbot/reporters/words.py index 20acff0d715a..f6a34155b21b 100644 --- a/master/buildbot/reporters/words.py +++ b/master/buildbot/reporters/words.py @@ -189,7 +189,8 @@ def workerEvent(key, msg): @defer.inlineCallbacks def unsubscribe_from_build_events(self): # Cancel all the subscriptions we have - old_list, self.subscribed = self.subscribed, [] + old_list = self.subscribed + self.subscribed = [] for handle in old_list: yield handle.stopConsuming() diff --git a/master/buildbot/test/fake/latent.py b/master/buildbot/test/fake/latent.py index 61fc56117a1d..90f6b1901b24 100644 --- a/master/buildbot/test/fake/latent.py +++ b/master/buildbot/test/fake/latent.py @@ -70,6 +70,9 @@ def __init__(self, case, name, kind=None, build_wait_timeout=600, self.auto_connect_worker = True self.auto_disconnect_worker = True + self._start_deferred = None + self._stop_deferred = None + self.kind = kind self._started_kind = None self._started_kind_deferred = None @@ -98,7 +101,8 @@ def auto_start(self, result): @defer.inlineCallbacks def start_instance(self, result): yield self.do_start_instance(result) - d, self._start_deferred = self._start_deferred, None + d = self._start_deferred + self._start_deferred = None d.callback(result) @defer.inlineCallbacks @@ -117,7 +121,8 @@ def auto_stop(self, result): @defer.inlineCallbacks def stop_instance(self, result): yield self.do_stop_instance() - d, self._stop_deferred = self._stop_deferred, None + d = self._stop_deferred + self._stop_deferred = None d.callback(result) @defer.inlineCallbacks diff --git a/master/buildbot/test/fake/machine.py b/master/buildbot/test/fake/machine.py index 1df425d72098..f2f7a7458914 100644 --- a/master/buildbot/test/fake/machine.py +++ b/master/buildbot/test/fake/machine.py @@ -44,7 +44,8 @@ def __init__(self, name, **kwargs): def start_machine(self, result): assert self.machine.state == MachineStates.STARTING - d, self._start_deferred = self._start_deferred, None + d = self._start_deferred + self._start_deferred = None if isinstance(result, Exception): d.errback(result) else: @@ -52,7 +53,8 @@ def start_machine(self, result): def stop_machine(self, result=True): assert self.machine.state == MachineStates.STOPPING - d, self._stop_deferred = self._stop_deferred, None + d = self._stop_deferred + self._stop_deferred = None if isinstance(result, Exception): d.errback(result) else: diff --git a/master/buildbot/test/fake/step.py b/master/buildbot/test/fake/step.py index d019bce80d4b..de6de64c745b 100644 --- a/master/buildbot/test/fake/step.py +++ b/master/buildbot/test/fake/step.py @@ -32,11 +32,13 @@ def __init__(self, **kwargs): self.step = ControllableBuildStep(self, **kwargs) self.running = False self.auto_finish_results = None + self._run_deferred = None def finish_step(self, result): assert self.running self.running = False - d, self._run_deferred = self._run_deferred, None + d = self._run_deferred + self._run_deferred = None d.callback(result) def auto_finish_step(self, result): diff --git a/master/buildbot/test/fake/worker.py b/master/buildbot/test/fake/worker.py index 077a86f1e8e8..7601d4250af7 100644 --- a/master/buildbot/test/fake/worker.py +++ b/master/buildbot/test/fake/worker.py @@ -190,6 +190,7 @@ def disconnect_worker(self): if self.remote_worker is None: return - self.remote_worker, worker = None, self.remote_worker + worker = self.remote_worker + self.remote_worker = None disconnect_master_side_worker(self.worker) yield worker.disownServiceParent() diff --git a/master/buildbot/test/fakedb/base.py b/master/buildbot/test/fakedb/base.py index 68594b0137ef..6f44972ceb87 100644 --- a/master/buildbot/test/fakedb/base.py +++ b/master/buildbot/test/fakedb/base.py @@ -31,7 +31,8 @@ def mapFilter(self, f, fieldMapping): def mapOrder(self, o, fieldMapping): if o.startswith('-'): - reverse, o = o[0], o[1:] + reverse = o[0] + o = o[1:] else: reverse = "" o = fieldMapping[o].split(".")[-1] @@ -50,7 +51,8 @@ def applicable(field): order = [self.mapOrder(o, rs.fieldMapping) for o in rs.order if applicable(o)] if len(filters) == len(rs.filters) and rs.order is not None and len(order) == len(rs.order): - offset, limit = rs.offset, rs.limit + offset = rs.offset + limit = rs.limit rs = resultspec.ResultSpec( filters=filters, order=order, limit=limit, offset=offset) return rs.apply(data) diff --git a/master/buildbot/test/fakedb/changes.py b/master/buildbot/test/fakedb/changes.py index a367e78dae25..c40365e54fd8 100644 --- a/master/buildbot/test/fakedb/changes.py +++ b/master/buildbot/test/fakedb/changes.py @@ -94,7 +94,8 @@ def insert_test_data(self, rows): elif isinstance(row, ChangeProperty): ch = self.changes[row.changeid] - n, vs = row.property_name, row.property_value + n = row.property_name + vs = row.property_value v, s = json.loads(vs) ch['properties'][n] = (v, s) diff --git a/master/buildbot/test/integration/test_worker_comm.py b/master/buildbot/test/integration/test_worker_comm.py index b76d54afb6e3..d7dbb8af10a0 100644 --- a/master/buildbot/test/integration/test_worker_comm.py +++ b/master/buildbot/test/integration/test_worker_comm.py @@ -78,7 +78,8 @@ def clear_persp(): def fire_deferreds(): self._detached = True - self._detach_deferreds, deferreds = None, self._detach_deferreds + deferreds = self._detach_deferreds + self._detach_deferreds = None for d in deferreds: d.callback(None) persp.broker.notifyOnDisconnect(fire_deferreds) @@ -136,7 +137,8 @@ def attached(self, conn): def detached(self): super().detached() - self.detach_d, d = None, self.detach_d + d = self.detach_d + self.detach_d = None d.callback(None) diff --git a/master/buildbot/test/unit/config/test_master.py b/master/buildbot/test/unit/config/test_master.py index 71ed464bb51d..9be0bb8cdf19 100644 --- a/master/buildbot/test/unit/config/test_master.py +++ b/master/buildbot/test/unit/config/test_master.py @@ -1073,7 +1073,8 @@ def lock(name): return lock return locks.LockAccess(lock, "counting", count=1) - b1, b2 = bldr('b1'), bldr('b2') + b1 = bldr('b1') + b2 = bldr('b2') self.cfg.builders = [b1, b2] if builder_lock: b1.locks.append(lock(builder_lock)) diff --git a/master/buildbot/test/unit/data/test_logchunks.py b/master/buildbot/test/unit/data/test_logchunks.py index 8013327706d3..c26b0f58c02c 100644 --- a/master/buildbot/test/unit/data/test_logchunks.py +++ b/master/buildbot/test/unit/data/test_logchunks.py @@ -102,7 +102,8 @@ def do_test_chunks(self, path, logid, expLines): {'logid': logid, 'firstline': f, 'content': expContent}) # truncated at EOF - f, length = len(expLines) - 2, len(expLines) + 10 + f = len(expLines) - 2 + length = len(expLines) + 10 result_spec = resultspec.ResultSpec(offset=f, limit=length - f + 1) logchunk = yield self.callGet(path, resultSpec=result_spec) self.validateData(logchunk) diff --git a/master/buildbot/test/unit/db/test_workers.py b/master/buildbot/test/unit/db/test_workers.py index ad415f2e6aea..14f1dba43409 100644 --- a/master/buildbot/test/unit/db/test_workers.py +++ b/master/buildbot/test/unit/db/test_workers.py @@ -71,12 +71,16 @@ class Tests(interfaces.InterfaceTests): BOGUS_NAME = 'bogus' - W1_NAME, W1_ID, W1_INFO = 'w1', 100, {'a': 1} + W1_NAME = "w1" + W1_ID = 100 + W1_INFO = {'a': 1} worker1_rows = [ fakedb.Worker(id=W1_ID, name=W1_NAME, info=W1_INFO), ] - W2_NAME, W2_ID, W2_INFO = 'w2', 200, {'a': 1, 'b': 2} + W2_NAME = "w2" + W2_ID = 200 + W2_INFO = {'a': 1, 'b': 2} worker2_rows = [ fakedb.Worker(id=W2_ID, name=W2_NAME, info=W2_INFO), ] diff --git a/master/buildbot/test/unit/scripts/test_checkconfig.py b/master/buildbot/test/unit/scripts/test_checkconfig.py index c3213ea37536..ebb18c9071d7 100644 --- a/master/buildbot/test/unit/scripts/test_checkconfig.py +++ b/master/buildbot/test/unit/scripts/test_checkconfig.py @@ -57,14 +57,16 @@ def do_test_load(self, config='', other_files=None, with open(fn, "w", encoding='utf-8') as f: f.write(contents) - old_stdout, old_stderr = sys.stdout, sys.stderr + old_stdout = sys.stdout + old_stderr = sys.stderr stdout = sys.stdout = StringIO() stderr = sys.stderr = StringIO() try: checkconfig._loadConfig( basedir=self.configdir, configFile="master.cfg", quiet=False) finally: - sys.stdout, sys.stderr = old_stdout, old_stderr + sys.stdout = old_stdout + sys.stderr = old_stderr if stdout_re: stdout = stdout.getvalue() self.assertTrue(stdout_re.search(stdout), stdout) diff --git a/master/buildbot/test/unit/util/test_lineboundaries.py b/master/buildbot/test/unit/util/test_lineboundaries.py index 2973072aa204..05502bbb7e24 100644 --- a/master/buildbot/test/unit/util/test_lineboundaries.py +++ b/master/buildbot/test/unit/util/test_lineboundaries.py @@ -116,7 +116,8 @@ def test_split_newlines(self): r"multi-character newlines, split across chunks, are converted" input = 'a\nb\r\nc\rd\n\re' for splitpoint in range(1, len(input) - 1): - a, b = input[:splitpoint], input[splitpoint:] + a = input[:splitpoint] + b = input[splitpoint:] yield self.lbf.append(a) yield self.lbf.append(b) yield self.lbf.flush() diff --git a/master/buildbot/test/unit/worker/test_base.py b/master/buildbot/test/unit/worker/test_base.py index a81ce4b5a187..0f08ad70adea 100644 --- a/master/buildbot/test/unit/worker/test_base.py +++ b/master/buildbot/test/unit/worker/test_base.py @@ -202,8 +202,10 @@ def test_constructor_secrets(self): @defer.inlineCallbacks def test_constructor_full(self): - lock1, lock2 = locks.MasterLock('lock1'), locks.MasterLock('lock2') - access1, access2 = lock1.access('counting'), lock2.access('counting') + lock1 = locks.MasterLock('lock1') + lock2 = locks.MasterLock('lock2') + access1 = lock1.access('counting') + access2 = lock2.access('counting') bs = yield self.createWorker('bot', 'pass', max_builds=2, diff --git a/master/buildbot/test/unit/worker/test_protocols_pb.py b/master/buildbot/test/unit/worker/test_protocols_pb.py index 17de5aa622e3..31aada8ab966 100644 --- a/master/buildbot/test/unit/worker/test_protocols_pb.py +++ b/master/buildbot/test/unit/worker/test_protocols_pb.py @@ -317,9 +317,11 @@ def test_remoteStartCommand(self): conn = pb.Connection(self.master, self.worker, self.mind) conn.remoteSetBuilderList(builders) - RCInstance, builder_name, commandID = base.RemoteCommandImpl( - ), "builder", None - remote_command, args = "command", {"args": 'args'} + RCInstance = base.RemoteCommandImpl() + builder_name = "builder" + commandID = None + remote_command = "command" + args = {"args": 'args'} conn.remoteStartCommand( RCInstance, builder_name, commandID, remote_command, args) diff --git a/master/buildbot/test/util/www.py b/master/buildbot/test/util/www.py index dc312828842e..c3405860439e 100644 --- a/master/buildbot/test/util/www.py +++ b/master/buildbot/test/util/www.py @@ -211,7 +211,8 @@ def assertRequest(self, content=None, contentJson=None, contentType=None, responseCode=None, contentDisposition=None, headers=None): if headers is None: headers = {} - got, exp = {}, {} + got = {} + exp = {} if content is not None: got['content'] = self.request.written exp['content'] = content diff --git a/master/buildbot/util/_notifier.py b/master/buildbot/util/_notifier.py index dc586d2157a6..afa023319dc7 100644 --- a/master/buildbot/util/_notifier.py +++ b/master/buildbot/util/_notifier.py @@ -34,7 +34,8 @@ def wait(self): def notify(self, result): if self._waiters: - waiters, self._waiters = self._waiters, [] + waiters = self._waiters + self._waiters = [] for waiter in waiters: waiter.callback(result) diff --git a/master/buildbot/util/eventual.py b/master/buildbot/util/eventual.py index b24184e4aa3f..a581fed8b38f 100644 --- a/master/buildbot/util/eventual.py +++ b/master/buildbot/util/eventual.py @@ -42,7 +42,8 @@ def _turn(self): # flush all the messages that are currently in the queue. If anything # gets added to the queue while we're doing this, those events will # be put off until the next turn. - events, self._events = self._events, [] + events = self._events + self._events = [] for cb, args, kwargs in events: try: cb(*args, **kwargs) @@ -52,7 +53,8 @@ def _turn(self): if self._events and not self._timer: self._timer = self._reactor.callLater(0, self._turn) if not self._events: - observers, self._flushObservers = self._flushObservers, [] + observers = self._flushObservers + self._flushObservers = [] for o in observers: o.callback(None) diff --git a/master/buildbot/util/lineboundaries.py b/master/buildbot/util/lineboundaries.py index 82487a4d45bc..ffa9368ae044 100644 --- a/master/buildbot/util/lineboundaries.py +++ b/master/buildbot/util/lineboundaries.py @@ -70,7 +70,8 @@ def adjust_line(self, text): i = text.rfind('\n') if i >= 0: i = i + 1 - text, self.partialLine = text[:i], text[i:] + self.partialLine = text[i:] + text = text[:i] else: self.partialLine = text return None diff --git a/master/buildbot/util/path_expand_user.py b/master/buildbot/util/path_expand_user.py index 4dcf6fa9c374..99640cbf9047 100644 --- a/master/buildbot/util/path_expand_user.py +++ b/master/buildbot/util/path_expand_user.py @@ -75,7 +75,8 @@ def nt_expanduser(path, worker_environ): tilde = '~' if not path.startswith(tilde): return path - i, n = 1, len(path) + i = 1 + n = len(path) while i < n and path[i] not in ntpath._get_bothseps(path): i += 1 diff --git a/master/buildbot/worker/latent.py b/master/buildbot/worker/latent.py index 2abdb6d10589..3d04b1b69256 100644 --- a/master/buildbot/worker/latent.py +++ b/master/buildbot/worker/latent.py @@ -542,7 +542,8 @@ def insubstantiate(self, fast=False, force_substantiation_build=None): States.INSUBSTANTIATING_SUBSTANTIATING] if self.state == States.INSUBSTANTIATING_SUBSTANTIATING: - build, self.substantiation_build = self.substantiation_build, None + build = self.substantiation_build + self.substantiation_build = None self.state = States.SUBSTANTIATING self._substantiate(build) else: # self.state == States.INSUBSTANTIATING: diff --git a/master/buildbot/worker/protocols/manager/base.py b/master/buildbot/worker/protocols/manager/base.py index fbeaf8cf443c..ad77e6a893ee 100644 --- a/master/buildbot/worker/protocols/manager/base.py +++ b/master/buildbot/worker/protocols/manager/base.py @@ -120,7 +120,8 @@ def startService(self): def stopService(self): # stop listening on the port when shut down assert self.port - port, self.port = self.port, None + port = self.port + self.port = None yield port.stopListening() yield super().stopService() diff --git a/worker/buildbot_worker/test/unit/test_bot_Worker.py b/worker/buildbot_worker/test/unit/test_bot_Worker.py index 02c74a01fe05..9de71a0c1516 100644 --- a/worker/buildbot_worker/test/unit/test_bot_Worker.py +++ b/worker/buildbot_worker/test/unit/test_bot_Worker.py @@ -50,7 +50,8 @@ def __init__(self, on_keepalive=None): def perspective_keepalive(self): if self.on_keepalive: - on_keepalive, self.on_keepalive = self.on_keepalive, None + on_keepalive = self.on_keepalive + self.on_keepalive = None on_keepalive() diff --git a/worker/buildbot_worker/test/util/test_lineboundaries.py b/worker/buildbot_worker/test/util/test_lineboundaries.py index d0aca0702ced..1bcdedbd4d9b 100644 --- a/worker/buildbot_worker/test/util/test_lineboundaries.py +++ b/worker/buildbot_worker/test/util/test_lineboundaries.py @@ -88,7 +88,8 @@ def test_split_newlines(self): input = 'a\nb\r\nc\rd\n\re' for splitpoint in range(1, len(input) - 1): - a, b = input[:splitpoint], input[splitpoint:] + a = input[:splitpoint] + b = input[splitpoint:] lines_info = [] lines_info.append(self.lbf.append(a, 2.0)) lines_info.append(self.lbf.append(b, 2.0)) diff --git a/worker/buildbot_worker/util/_notifier.py b/worker/buildbot_worker/util/_notifier.py index 9531bfee4923..9fa511c9e8f7 100644 --- a/worker/buildbot_worker/util/_notifier.py +++ b/worker/buildbot_worker/util/_notifier.py @@ -35,7 +35,8 @@ def wait(self): return d def notify(self, result): - waiters, self._waiters = self._waiters, [] + waiters = self._waiters + self._waiters = [] for waiter in waiters: waiter.callback(result) From afbb6bf6624393ec8aee2ebd5c924cc377a0506a Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:38:31 +0200 Subject: [PATCH 36/68] e2e: Disable several flaky tests --- smokes-react/tests/buildsnavigation.spec.ts | 3 ++- smokes-react/tests/reason_force.spec.ts | 4 +++- smokes-react/tests/rebuilds.spec.ts | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/smokes-react/tests/buildsnavigation.spec.ts b/smokes-react/tests/buildsnavigation.spec.ts index 92d249fb13fb..3457c06a0a11 100644 --- a/smokes-react/tests/buildsnavigation.spec.ts +++ b/smokes-react/tests/buildsnavigation.spec.ts @@ -50,7 +50,8 @@ test.describe('previousnextlink', function() { test.describe('forceandstop', function() { test('should create a build with a dedicated reason and stop it during execution', - async ({page}) => { + async ({page, browserName}) => { + test.skip(browserName === 'webkit', 'https://github.com/buildbot/buildbot/issues/7307'); await BuilderPage.gotoForce(page, "slowruntests", "force"); await ForcePage.clickStartButtonAndWaitRedirectToBuild(page); diff --git a/smokes-react/tests/reason_force.spec.ts b/smokes-react/tests/reason_force.spec.ts index 865d56eaaa35..e94c4cc8882c 100644 --- a/smokes-react/tests/reason_force.spec.ts +++ b/smokes-react/tests/reason_force.spec.ts @@ -42,7 +42,9 @@ test.describe('force and cancel', function() { await ForcePage.clickCancelButton(page); }); - test('should create a build with a dedicated reason and Start it', async ({page}) => { + test('should create a build with a dedicated reason and Start it', + async ({page, browserName}) => { + test.skip(browserName === 'webkit', 'https://github.com/buildbot/buildbot/issues/7355'); await BuilderPage.gotoBuildersList(page); await BuilderPage.goto(page, "runtests"); await BuilderPage.gotoForce(page, "runtests", "force"); diff --git a/smokes-react/tests/rebuilds.spec.ts b/smokes-react/tests/rebuilds.spec.ts index 00461007f520..0a599ed7ca72 100644 --- a/smokes-react/tests/rebuilds.spec.ts +++ b/smokes-react/tests/rebuilds.spec.ts @@ -25,7 +25,9 @@ test.describe('rebuilds', function() { await HomePage.waitAllBuildsFinished(page); }); - test('should navigate to a dedicated build and to use the rebuild button', async ({page}) => { + test('should navigate to a dedicated build and to use the rebuild button', + async ({page, browserName}) => { + test.skip(browserName === 'webkit', 'https://github.com/buildbot/buildbot/issues/7308'); await BuilderPage.gotoBuildersList(page); await BuilderPage.goto(page, "runtests"); const lastbuild = await BuilderPage.getLastFinishedBuildNumber(page); From 6ce6a082cc098b21dbbdc31fdfc3019d8f9e4417 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:29:56 +0200 Subject: [PATCH 37/68] reporters: Implement get_responsible_users_for_buildset() --- master/buildbot/reporters/utils.py | 14 +++++++++++ .../test/unit/reporters/test_utils.py | 23 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/master/buildbot/reporters/utils.py b/master/buildbot/reporters/utils.py index 0a1551238d3b..34681913670b 100644 --- a/master/buildbot/reporters/utils.py +++ b/master/buildbot/reporters/utils.py @@ -222,6 +222,20 @@ def getResponsibleUsersForBuild(master, buildid): return blamelist +# perhaps we need data api for users with buildsets/:id/users +@defer.inlineCallbacks +def get_responsible_users_for_buildset(master, buildsetid): + props = yield master.data.get(("buildsets", buildsetid, "properties")) + + # TODO: This currently does not track what changes were in the buildset. getChangesForBuild() + # would walk the change graph until it finds last successful build and uses the authors of + # the changes as blame list. Probably this needs to be done here too + owner = props.get("owner", None) + if owner: + return [owner[0]] + return [] + + def getURLForBuild(master, builderid, build_number): prefix = master.config.buildbotURL return prefix + f"#/builders/{builderid}/builds/{build_number}" diff --git a/master/buildbot/test/unit/reporters/test_utils.py b/master/buildbot/test/unit/reporters/test_utils.py index a3c3256dd928..46ef17f1802b 100644 --- a/master/buildbot/test/unit/reporters/test_utils.py +++ b/master/buildbot/test/unit/reporters/test_utils.py @@ -437,6 +437,29 @@ def test_getResponsibleUsersForBuildWithOwners(self): res = yield utils.getResponsibleUsersForBuild(self.master, 20) self.assertEqual(sorted(res), sorted(["me@foo", "him", "her"])) + @defer.inlineCallbacks + def test_get_responsible_users_for_buildset_with_owner(self): + self.setupDb() + + self.db.insert_test_data( + [ + fakedb.BuildsetProperty( + buildsetid=98, + property_name="owner", + property_value='["buildset_owner", "fakedb"]' + ), + ] + ) + + res = yield utils.get_responsible_users_for_buildset(self.master, 98) + self.assertEqual(sorted(res), sorted(["buildset_owner"])) + + @defer.inlineCallbacks + def test_get_responsible_users_for_buildset_no_owner(self): + self.setupDb() + res = yield utils.get_responsible_users_for_buildset(self.master, 99) + self.assertEqual(sorted(res), sorted([])) + @defer.inlineCallbacks def test_getPreviousBuild(self): self.setupDb() From b74c2b86f843d2c126c40ff9e4aa3a4aa8bc1e1b Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:29:57 +0200 Subject: [PATCH 38/68] reporters: Add buildset info to report generator dictionaries --- .../reporters/generators/buildrequest.py | 1 + .../buildbot/reporters/generators/buildset.py | 7 ++- master/buildbot/reporters/generators/utils.py | 1 + .../buildbot/reporters/generators/worker.py | 1 + .../buildbot/test/unit/reporters/test_base.py | 8 ++- .../unit/reporters/test_bitbucketserver.py | 2 +- .../unit/reporters/test_generators_build.py | 56 ++++++++++++++----- .../reporters/test_generators_buildrequest.py | 6 ++ .../reporters/test_generators_buildset.py | 28 +++++++--- master/buildbot/test/util/reporter.py | 3 + .../configuration/reporters/reporter_base.rst | 3 + .../report-generator-reports-buildset.feature | 1 + 12 files changed, 88 insertions(+), 29 deletions(-) create mode 100644 newsfragments/report-generator-reports-buildset.feature diff --git a/master/buildbot/reporters/generators/buildrequest.py b/master/buildbot/reporters/generators/buildrequest.py index 24c70c93e9cb..ab1b46eabe3d 100644 --- a/master/buildbot/reporters/generators/buildrequest.py +++ b/master/buildbot/reporters/generators/buildrequest.py @@ -88,6 +88,7 @@ def buildrequest_message(self, master, build): 'type': buildmsg['type'], 'results': build['results'], 'builds': [build], + "buildset": build["buildset"], 'users': list(users), 'patches': patches, 'logs': [] diff --git a/master/buildbot/reporters/generators/buildset.py b/master/buildbot/reporters/generators/buildset.py index 46139dd9b74e..0b378480b64e 100644 --- a/master/buildbot/reporters/generators/buildset.py +++ b/master/buildbot/reporters/generators/buildset.py @@ -68,12 +68,11 @@ def generate(self, master, reporter, key, message): if not builds: return None - report = yield self.buildset_message(self.formatter, master, reporter, builds, - buildset['results']) + report = yield self.buildset_message(self.formatter, master, reporter, builds, buildset) return report @defer.inlineCallbacks - def buildset_message(self, formatter, master, reporter, builds, results): + def buildset_message(self, formatter, master, reporter, builds, buildset): # The given builds must refer to builds from a single buildset patches = [] logs = [] @@ -81,6 +80,7 @@ def buildset_message(self, formatter, master, reporter, builds, results): subject = None msgtype = None users = set() + results = buildset["results"] for build in builds: patches.extend(self._get_patches_for_build(build)) @@ -115,6 +115,7 @@ def buildset_message(self, formatter, master, reporter, builds, results): 'type': msgtype, 'results': results, 'builds': builds, + "buildset": buildset, 'users': list(users), 'patches': patches, 'logs': logs diff --git a/master/buildbot/reporters/generators/utils.py b/master/buildbot/reporters/generators/utils.py index 15804496017e..901d39bba9c2 100644 --- a/master/buildbot/reporters/generators/utils.py +++ b/master/buildbot/reporters/generators/utils.py @@ -197,6 +197,7 @@ def build_message(self, formatter, master, reporter, build): 'type': buildmsg['type'], 'results': results, 'builds': [build], + "buildset": build["buildset"], 'users': list(users), 'patches': patches, 'logs': logs diff --git a/master/buildbot/reporters/generators/worker.py b/master/buildbot/reporters/generators/worker.py index 6af399b65f30..74ad55a4d6b1 100644 --- a/master/buildbot/reporters/generators/worker.py +++ b/master/buildbot/reporters/generators/worker.py @@ -62,6 +62,7 @@ def generate(self, master, reporter, key, worker): 'type': msg['type'], 'results': None, 'builds': None, + "buildset": None, 'users': worker['notify'], 'patches': None, 'logs': None, diff --git a/master/buildbot/test/unit/reporters/test_base.py b/master/buildbot/test/unit/reporters/test_base.py index 439e651c87e3..d8fcda40b87e 100644 --- a/master/buildbot/test/unit/reporters/test_base.py +++ b/master/buildbot/test/unit/reporters/test_base.py @@ -55,9 +55,10 @@ def setupNotifier(self, generators): return mn @defer.inlineCallbacks - def setupBuildMessage(self, **kwargs): + def setup_build_message(self, **kwargs): build = yield self.insert_build_finished(FAILURE) + buildset = yield self.get_inserted_buildset() formatter = mock.Mock(spec=MessageFormatter) formatter.format_message_for_build.return_value = { @@ -74,7 +75,7 @@ def setupBuildMessage(self, **kwargs): mn = yield self.setupNotifier(generators=[generator]) yield mn._got_event(('builds', 20, 'finished'), build) - return (mn, build, formatter) + return (mn, build, buildset, formatter) def setup_mock_generator(self, events_filter): gen = mock.Mock() @@ -88,7 +89,7 @@ def test_check_config_raises_error_when_generators_not_list(self): @defer.inlineCallbacks def test_buildMessage_nominal(self): - mn, build, formatter = yield self.setupBuildMessage(mode=("failing",)) + mn, build, buildset, formatter = yield self.setup_build_message(mode=("failing",)) formatter.format_message_for_build.assert_called_with(self.master, build, is_buildset=False, mode=('failing',), users=['me@foo']) @@ -99,6 +100,7 @@ def test_buildMessage_nominal(self): 'type': 'text', 'results': FAILURE, 'builds': [build], + "buildset": buildset, 'users': ['me@foo'], 'patches': [], 'logs': [] diff --git a/master/buildbot/test/unit/reporters/test_bitbucketserver.py b/master/buildbot/test/unit/reporters/test_bitbucketserver.py index 73a3201e3124..e17499778bf1 100644 --- a/master/buildbot/test/unit/reporters/test_bitbucketserver.py +++ b/master/buildbot/test/unit/reporters/test_bitbucketserver.py @@ -579,7 +579,7 @@ def test_reporter_without_pullrequest(self): def test_reporter_with_buildset(self): yield self.setupReporter(generator_class=BuildSetStatusGenerator) yield self.setupBuildResults(SUCCESS) - buildset = yield self.master.data.get(('buildsets', 98)) + buildset = yield self.get_inserted_buildset() self._http.expect( "post", EXPECTED_API, diff --git a/master/buildbot/test/unit/reporters/test_generators_build.py b/master/buildbot/test/unit/reporters/test_generators_build.py index 6375f3c701c8..1a7bb1ca6ae9 100644 --- a/master/buildbot/test/unit/reporters/test_generators_build.py +++ b/master/buildbot/test/unit/reporters/test_generators_build.py @@ -60,13 +60,14 @@ def setup_generator(self, results=SUCCESS, message=None, db_args=None, **kwargs) db_args = {} build = yield self.insert_build_finished_get_props(results, **db_args) + buildset = yield self.get_inserted_buildset() g = BuildStatusGenerator(**kwargs) g.formatter = Mock(spec=g.formatter) g.formatter.format_message_for_build.return_value = message - return (g, build) + return g, build, buildset @defer.inlineCallbacks def build_message(self, g, build, results=SUCCESS): @@ -86,7 +87,7 @@ def generate(self, g, key, build): @defer.inlineCallbacks def test_build_message_nominal(self): - g, build = yield self.setup_generator(mode=("change",)) + g, build, buildset = yield self.setup_generator(mode=("change",)) report = yield self.build_message(g, build) g.formatter.format_message_for_build.assert_called_with(self.master, build, @@ -99,6 +100,7 @@ def test_build_message_nominal(self): 'type': 'text', 'results': SUCCESS, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -106,7 +108,7 @@ def test_build_message_nominal(self): @defer.inlineCallbacks def test_build_message_no_result(self): - g, build = yield self.setup_generator(results=None, mode=("change",)) + g, build, buildset = yield self.setup_generator(results=None, mode=("change",)) report = yield self.build_message(g, build, results=None) g.formatter.format_message_for_build.assert_called_with(self.master, build, @@ -119,6 +121,7 @@ def test_build_message_no_result(self): 'type': 'text', 'results': None, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -137,7 +140,11 @@ def test_build_message_no_result_formatter_no_subject(self): "subject": None, # deprecated unspecified subject } - g, build = yield self.setup_generator(results=None, message=message, mode=("change",)) + g, build, buildset = yield self.setup_generator( + results=None, + message=message, + mode=("change",) + ) report = yield self.build_message(g, build, results=None) g.formatter.format_message_for_build.assert_called_with(self.master, build, @@ -150,6 +157,7 @@ def test_build_message_no_result_formatter_no_subject(self): 'type': 'text', 'results': None, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -157,7 +165,7 @@ def test_build_message_no_result_formatter_no_subject(self): @defer.inlineCallbacks def test_build_message_addLogs(self): - g, build = yield self.setup_generator(mode=("change",), add_logs=True) + g, build, _ = yield self.setup_generator(mode=("change",), add_logs=True) report = yield self.build_message(g, build) self.assertEqual(report['logs'][0]['logid'], 60) @@ -165,8 +173,11 @@ def test_build_message_addLogs(self): @defer.inlineCallbacks def test_build_message_add_patch(self): - g, build = yield self.setup_generator(mode=("change",), add_patch=True, - db_args={'insert_patch': True}) + g, build, _ = yield self.setup_generator( + mode=("change",), + add_patch=True, + db_args={"insert_patch": True} + ) report = yield self.build_message(g, build) patch_dict = { @@ -181,14 +192,17 @@ def test_build_message_add_patch(self): @defer.inlineCallbacks def test_build_message_add_patch_no_patch(self): - g, build = yield self.setup_generator(mode=("change",), add_patch=True, - db_args={'insert_patch': False}) + g, build, _ = yield self.setup_generator( + mode=("change",), + add_patch=True, + db_args={'insert_patch': False} + ) report = yield self.build_message(g, build) self.assertEqual(report['patches'], []) @defer.inlineCallbacks def test_generate_finished(self): - g, build = yield self.setup_generator() + g, build, buildset = yield self.setup_generator() report = yield self.generate(g, ('builds', 123, 'finished'), build) self.assertEqual(report, { @@ -197,6 +211,7 @@ def test_generate_finished(self): 'type': 'text', 'results': SUCCESS, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -204,21 +219,25 @@ def test_generate_finished(self): @defer.inlineCallbacks def test_generate_finished_non_matching_builder(self): - g, build = yield self.setup_generator(builders=['non-matched']) + g, build, _ = yield self.setup_generator(builders=['non-matched']) report = yield self.generate(g, ('builds', 123, 'finished'), build) self.assertIsNone(report) @defer.inlineCallbacks def test_generate_finished_non_matching_result(self): - g, build = yield self.setup_generator(mode=('failing',)) + g, build, _ = yield self.setup_generator(mode=('failing',)) report = yield self.generate(g, ('builds', 123, 'finished'), build) self.assertIsNone(report) @defer.inlineCallbacks def test_generate_new(self): - g, build = yield self.setup_generator(results=None, mode=('failing',), report_new=True) + g, build, buildset = yield self.setup_generator( + results=None, + mode=("failing",), + report_new=True + ) report = yield self.generate(g, ('builds', 123, 'new'), build) self.assertEqual(report, { @@ -227,6 +246,7 @@ def test_generate_new(self): 'type': 'text', 'results': None, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -310,6 +330,7 @@ def generate(self, g, key, build): def test_build_message_start(self): g = yield self.setup_generator() build = yield self.insert_build_finished_get_props(SUCCESS) + buildset = yield self.get_inserted_buildset() report = yield self.build_message(g, build) g.start_formatter.format_message_for_build.assert_called_with(self.master, build, @@ -323,6 +344,7 @@ def test_build_message_start(self): 'type': 'plain', 'results': SUCCESS, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -332,6 +354,8 @@ def test_build_message_start(self): def test_build_message_start_no_result(self): g = yield self.setup_generator(results=None) build = yield self.insert_build_new() + buildset = yield self.get_inserted_buildset() + build["buildset"] = buildset report = yield self.build_message(g, build, results=None) g.start_formatter.format_message_for_build.assert_called_with(self.master, build, @@ -345,6 +369,7 @@ def test_build_message_start_no_result(self): 'type': 'plain', 'results': None, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -404,6 +429,7 @@ def test_build_message_add_patch_no_patch(self): def test_generate_new(self): g = yield self.setup_generator() build = yield self.insert_build_new() + buildset = yield self.get_inserted_buildset() report = yield self.generate(g, ('builds', 123, 'new'), build) self.assertEqual(report, { @@ -412,6 +438,7 @@ def test_generate_new(self): 'type': 'plain', 'results': None, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -421,6 +448,7 @@ def test_generate_new(self): def test_generate_finished(self): g = yield self.setup_generator() build = yield self.insert_build_finished_get_props(SUCCESS) + buildset = yield self.get_inserted_buildset() report = yield self.generate(g, ('builds', 123, 'finished'), build) self.assertEqual(report, { @@ -429,6 +457,7 @@ def test_generate_finished(self): 'type': 'plain', 'results': SUCCESS, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -438,6 +467,7 @@ def test_generate_finished(self): def test_generate_none(self): g = yield self.setup_generator(builders=['other builder']) build = yield self.insert_build_new() + build["buildset"] = yield self.get_inserted_buildset() report = yield self.generate(g, ('builds', 123, 'new'), build) self.assertIsNone(report, None) diff --git a/master/buildbot/test/unit/reporters/test_generators_buildrequest.py b/master/buildbot/test/unit/reporters/test_generators_buildrequest.py index b8b8ea0a73b0..6b1fd6862acd 100644 --- a/master/buildbot/test/unit/reporters/test_generators_buildrequest.py +++ b/master/buildbot/test/unit/reporters/test_generators_buildrequest.py @@ -80,6 +80,7 @@ def setup_generator(self, message=None, **kwargs): def test_build_message_start_no_result(self): g = yield self.setup_generator() buildrequest = yield self.insert_buildrequest_new() + buildset = yield self.get_inserted_buildset() build = yield g.partial_build_dict(self.master, buildrequest) report = yield g.buildrequest_message(self.master, build) @@ -94,6 +95,7 @@ def test_build_message_start_no_result(self): 'type': 'plain', 'results': None, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -128,6 +130,7 @@ def test_build_message_add_patch_no_patch(self): def test_generate_new(self): g = yield self.setup_generator(add_patch=True) buildrequest = yield self.insert_buildrequest_new(insert_patch=False) + buildset = yield self.get_inserted_buildset() build = yield g.partial_build_dict(self.master, buildrequest) report = yield g.generate(self.master, None, ('buildrequests', 11, 'new'), buildrequest) @@ -137,6 +140,7 @@ def test_generate_new(self): 'type': 'plain', 'results': None, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -147,6 +151,7 @@ def test_generate_cancel(self): self.maxDiff = None g = yield self.setup_generator(add_patch=True) buildrequest = yield self.insert_buildrequest_new(insert_patch=False) + buildset = yield self.get_inserted_buildset() build = yield g.partial_build_dict(self.master, buildrequest) report = yield g.generate(self.master, None, ('buildrequests', 11, 'cancel'), buildrequest) @@ -159,6 +164,7 @@ def test_generate_cancel(self): 'type': 'plain', 'results': CANCELLED, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] diff --git a/master/buildbot/test/unit/reporters/test_generators_buildset.py b/master/buildbot/test/unit/reporters/test_generators_buildset.py index 177ef35fe733..220eb14ffd65 100644 --- a/master/buildbot/test/unit/reporters/test_generators_buildset.py +++ b/master/buildbot/test/unit/reporters/test_generators_buildset.py @@ -58,7 +58,7 @@ def setup_generator(self, results=SUCCESS, message=None, db_args=None, **kwargs) db_args = {} build = yield self.insert_build_finished_get_props(results, **db_args) - buildset = yield self.master.data.get(("buildsets", 98)) + buildset = yield self.get_inserted_buildset() g = BuildSetStatusGenerator(**kwargs) @@ -71,11 +71,11 @@ def setup_generator(self, results=SUCCESS, message=None, db_args=None, **kwargs) return (g, build, buildset) @defer.inlineCallbacks - def buildset_message(self, g, builds, results=SUCCESS): + def buildset_message(self, g, builds, buildset): reporter = Mock() reporter.getResponsibleUsersForBuild.return_value = [] - report = yield g.buildset_message(g.formatter, self.master, reporter, builds, results) + report = yield g.buildset_message(g.formatter, self.master, reporter, builds, buildset) return report @defer.inlineCallbacks @@ -88,8 +88,8 @@ def generate(self, g, key, build): @defer.inlineCallbacks def test_buildset_message_nominal(self): - g, build, _ = yield self.setup_generator(mode=("change",)) - report = yield self.buildset_message(g, [build]) + g, build, buildset = yield self.setup_generator(mode=("change",)) + report = yield self.buildset_message(g, [build], buildset) g.formatter.format_message_for_build.assert_called_with(self.master, build, is_buildset=True, mode=('change',), @@ -101,6 +101,7 @@ def test_buildset_message_nominal(self): 'type': 'text', 'results': SUCCESS, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -108,8 +109,9 @@ def test_buildset_message_nominal(self): @defer.inlineCallbacks def test_buildset_message_no_result(self): - g, build, _ = yield self.setup_generator(results=None, mode=("change",)) - report = yield self.buildset_message(g, [build], results=None) + g, build, buildset = yield self.setup_generator(results=None, mode=("change",)) + buildset["results"] = None + report = yield self.buildset_message(g, [build], buildset) g.formatter.format_message_for_build.assert_called_with(self.master, build, is_buildset=True, @@ -121,6 +123,7 @@ def test_buildset_message_no_result(self): 'type': 'text', 'results': None, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -139,8 +142,13 @@ def test_buildset_message_no_result_formatter_no_subject(self): "subject": None, # deprecated unspecified subject } - g, build, _ = yield self.setup_generator(results=None, message=message, mode=("change",)) - report = yield self.buildset_message(g, [build], results=None) + g, build, buildset = yield self.setup_generator( + results=None, + message=message, + mode=("change",) + ) + buildset["results"] = None + report = yield self.buildset_message(g, [build], buildset) g.formatter.format_message_for_build.assert_called_with(self.master, build, is_buildset=True, @@ -152,6 +160,7 @@ def test_buildset_message_no_result_formatter_no_subject(self): 'type': 'text', 'results': None, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] @@ -173,6 +182,7 @@ def test_generate_complete(self): 'type': 'text', 'results': SUCCESS, 'builds': [build], + "buildset": buildset, 'users': [], 'patches': [], 'logs': [] diff --git a/master/buildbot/test/util/reporter.py b/master/buildbot/test/util/reporter.py index 0af39ff2e974..68e76c183b08 100644 --- a/master/buildbot/test/util/reporter.py +++ b/master/buildbot/test/util/reporter.py @@ -162,6 +162,9 @@ def insert_test_data(self, buildResults, finalResult, insertSS=True, self.setup_fake_get_changes_for_build() + def get_inserted_buildset(self): + return self.master.data.get(("buildsets", 98)) + def setup_fake_get_changes_for_build(self, has_change=True): @defer.inlineCallbacks def getChangesForBuild(buildid): diff --git a/master/docs/manual/configuration/reporters/reporter_base.rst b/master/docs/manual/configuration/reporters/reporter_base.rst index 60f2bf136374..8a596b7dbb2c 100644 --- a/master/docs/manual/configuration/reporters/reporter_base.rst +++ b/master/docs/manual/configuration/reporters/reporter_base.rst @@ -52,6 +52,9 @@ This documents frequently used keys within the dictionaries that are passed to t - ``builds`` (a list of build dictionaries as reported by the data API) A list of builds that the report describes. + - ``buildset`` (a buildset dictionary as reported by the data API) + The buildset that is being described. + - ``users`` (a list of strings) A list of users to send the report to. diff --git a/newsfragments/report-generator-reports-buildset.feature b/newsfragments/report-generator-reports-buildset.feature new file mode 100644 index 000000000000..6c5a9d3a6e21 --- /dev/null +++ b/newsfragments/report-generator-reports-buildset.feature @@ -0,0 +1 @@ +Added buildset information to dictionaries returned by report generators. From fbdad163bd53327c94a79158795b73e7a008e211 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:29:58 +0200 Subject: [PATCH 39/68] reporters: Don't build properties when renderable is None --- master/buildbot/reporters/message.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py index 7186792b02e3..a240192de354 100644 --- a/master/buildbot/reporters/message.py +++ b/master/buildbot/reporters/message.py @@ -290,6 +290,9 @@ def render_message_body(self, context): @defer.inlineCallbacks def render_message_subject(self, context): + if self.subject is None: + return None + props = Properties.fromDict(context['build']['properties']) props.master = context['master'] From 8cf999d6d4562b43f13c80a50fa56186f4e9216b Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:29:59 +0200 Subject: [PATCH 40/68] reporters: Add BuildSetCombinedStatusGenerator --- .../buildbot/reporters/generators/buildset.py | 59 ++++ master/buildbot/reporters/generators/utils.py | 1 - master/buildbot/reporters/message.py | 45 +++ .../reporters/test_generators_buildset.py | 271 ++++++++++++++++-- master/buildbot/test/util/reporter.py | 14 + .../report_generators/buildset.rst | 5 +- .../report_generators/buildset_combined.rst | 27 ++ .../report_generators/formatter.rst | 55 +++- .../report_generators/formatter_function.rst | 12 +- .../formatter_renderable.rst | 3 + .../configuration/report_generators/index.rst | 5 +- master/setup.py | 7 +- ...ombined-buildsets-status-generator.feature | 2 + 13 files changed, 482 insertions(+), 24 deletions(-) create mode 100644 master/docs/manual/configuration/report_generators/buildset_combined.rst create mode 100644 newsfragments/combined-buildsets-status-generator.feature diff --git a/master/buildbot/reporters/generators/buildset.py b/master/buildbot/reporters/generators/buildset.py index 0b378480b64e..f65ba9143736 100644 --- a/master/buildbot/reporters/generators/buildset.py +++ b/master/buildbot/reporters/generators/buildset.py @@ -123,3 +123,62 @@ def buildset_message(self, formatter, master, reporter, builds, buildset): def _want_previous_build(self): return "change" in self.mode or "problem" in self.mode + + +@implementer(interfaces.IReportGenerator) +class BuildSetCombinedStatusGenerator: + + wanted_event_keys = [ + ("buildsets", None, "complete"), + ] + + compare_attrs = ["formatter"] + + def __init__(self, message_formatter): + self.formatter = message_formatter + + @defer.inlineCallbacks + def generate(self, master, reporter, key, message): + bsid = message["bsid"] + + res = yield utils.getDetailsForBuildset( + master, + bsid, + want_properties=self.formatter.want_properties, + want_steps=self.formatter.want_steps, + want_logs=self.formatter.want_logs, + want_logs_content=self.formatter.want_logs_content + ) + + builds = res['builds'] + buildset = res['buildset'] + + report = yield self.buildset_message(self.formatter, master, reporter, buildset, builds) + + return report + + def check(self): + pass + + @defer.inlineCallbacks + def buildset_message(self, formatter, master, reporter, buildset, builds): + buildmsg = yield formatter.format_message_for_buildset( + master, + buildset, + builds, + is_buildset=True, + mode=("passing",), + users=[] + ) + + return { + "body": buildmsg["body"], + "subject": buildmsg["subject"], + "type": buildmsg["type"], + "results": buildset["results"], + "builds": builds, + "buildset": buildset, + "users": [], + "patches": [], + "logs": [] + } diff --git a/master/buildbot/reporters/generators/utils.py b/master/buildbot/reporters/generators/utils.py index 901d39bba9c2..9c3793db3d49 100644 --- a/master/buildbot/reporters/generators/utils.py +++ b/master/buildbot/reporters/generators/utils.py @@ -91,7 +91,6 @@ def _should_attach_log(self, log): return False def is_message_needed_by_props(self, build): - # here is where we actually do something. builder = build['builder'] scheduler = build['properties'].get('scheduler', [None])[0] branch = build['properties'].get('branch', [None])[0] diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py index a240192de354..a7d5a989ec38 100644 --- a/master/buildbot/reporters/message.py +++ b/master/buildbot/reporters/message.py @@ -145,6 +145,26 @@ def create_context_for_build(mode, build, is_buildset, master, blamelist): } +def create_context_for_buildset(mode, buildset, builds, master, blamelist): + ss_list = buildset['sourcestamps'] + results = buildset["results"] + + return { + "results": results, + "result_names": Results, + "mode": mode, + "buildset": buildset, + "builds": builds, + "is_buildset": True, + "projects": get_projects_text(ss_list, master), + "status_detected": get_detected_status_text(mode, results, None), + "buildbot_title": master.config.title, + "buildbot_url": master.config.buildbotURL, + "blamelist": blamelist, + "sourcestamps": get_message_source_stamp_text(ss_list) + } + + def create_context_for_worker(master, worker): return { 'buildbot_title': master.config.title, @@ -237,6 +257,10 @@ def format_message_for_build(self, master, build, **kwargs): # Known kwargs keys: mode, users, is_buildset raise NotImplementedError + def format_message_for_buildset(self, master, buildset, builds, **kwargs): + # Known kwargs keys: mode, users, is_buildset + raise NotImplementedError + class MessageFormatterEmpty(MessageFormatterBase): def format_message_for_build(self, master, build, **kwargs): @@ -246,6 +270,13 @@ def format_message_for_build(self, master, build, **kwargs): 'subject': None } + def format_message_for_buildset(self, master, buildset, builds, **kwargs): + return { + "body": None, + "type": "plain", + "subject": None + } + class MessageFormatterFunction(MessageFormatterBase): @@ -259,6 +290,11 @@ def format_message_for_build(self, master, build, **kwargs): msgdict = yield self.render_message_dict(master, {'build': build}) return msgdict + @defer.inlineCallbacks + def format_message_for_buildset(self, master, buildset, builds, **kwargs): + msgdict = yield self.render_message_dict(master, {"buildset": buildset, "builds": builds}) + return msgdict + def render_message_body(self, context): return self._function(context) @@ -280,6 +316,9 @@ def format_message_for_build(self, master, build, **kwargs): msgdict = yield self.render_message_dict(master, {'build': build, 'master': master}) return msgdict + def format_message_for_buildset(self, master, buildset, builds, **kwargs): + raise NotImplementedError + @defer.inlineCallbacks def render_message_body(self, context): props = Properties.fromDict(context['build']['properties']) @@ -416,6 +455,12 @@ def format_message_for_build(self, master, build, is_buildset=False, users=None, msgdict = yield self.render_message_dict(master, ctx) return msgdict + @defer.inlineCallbacks + def format_message_for_buildset(self, master, buildset, builds, users=None, mode=None): + ctx = create_context_for_buildset(mode, buildset, builds, master, users) + msgdict = yield self.render_message_dict(master, ctx) + return msgdict + default_missing_template_plain = '''\ The Buildbot worker named {{worker.name}} went away. diff --git a/master/buildbot/test/unit/reporters/test_generators_buildset.py b/master/buildbot/test/unit/reporters/test_generators_buildset.py index 220eb14ffd65..8acad193689a 100644 --- a/master/buildbot/test/unit/reporters/test_generators_buildset.py +++ b/master/buildbot/test/unit/reporters/test_generators_buildset.py @@ -20,7 +20,9 @@ from buildbot.process.results import SUCCESS from buildbot.reporters import utils +from buildbot.reporters.generators.buildset import BuildSetCombinedStatusGenerator from buildbot.reporters.generators.buildset import BuildSetStatusGenerator +from buildbot.reporters.message import MessageFormatter from buildbot.test.fake import fakemaster from buildbot.test.reactor import TestReactorMixin from buildbot.test.util.config import ConfigErrorsMixin @@ -29,16 +31,16 @@ from buildbot.warnings import DeprecatedApiWarning -class TestBuildSetGenerator(ConfigErrorsMixin, TestReactorMixin, ReporterTestMixin, - unittest.TestCase): - # Note: most of the functionality of BuildSetStatusGenerator is shared with - # BuildStatusGenerator and is tested there. - +class TestBuildSetGeneratorBase( + ConfigErrorsMixin, + TestReactorMixin, + ReporterTestMixin, + unittest.TestCase +): def setUp(self): self.setup_test_reactor() self.setup_reporter_test() - self.master = fakemaster.make_master(self, wantData=True, wantDb=True, - wantMq=True) + self.master = fakemaster.make_master(self, wantData=True, wantDb=True, wantMq=True) @defer.inlineCallbacks def insert_build_finished_get_props(self, results, **kwargs): @@ -47,7 +49,14 @@ def insert_build_finished_get_props(self, results, **kwargs): return build @defer.inlineCallbacks - def setup_generator(self, results=SUCCESS, message=None, db_args=None, **kwargs): + def setup_generator( + self, + results=SUCCESS, + message=None, + db_args=None, + insert_build=True, + **kwargs + ): if message is None: message = { "body": "body", @@ -57,19 +66,31 @@ def setup_generator(self, results=SUCCESS, message=None, db_args=None, **kwargs) if db_args is None: db_args = {} - build = yield self.insert_build_finished_get_props(results, **db_args) - buildset = yield self.get_inserted_buildset() + if insert_build: + build = yield self.insert_build_finished_get_props(results, **db_args) + buildset = yield self.get_inserted_buildset() + else: + build = None + buildset = yield self.insert_buildset_no_builds(results, **db_args) - g = BuildSetStatusGenerator(**kwargs) + formatter = Mock(spec=MessageFormatter()) + formatter.format_message_for_build.return_value = message + formatter.format_message_for_buildset.return_value = message + formatter.want_logs = False + formatter.want_logs_content = False + formatter.want_steps = False - g.formatter = Mock(spec=g.formatter) - g.formatter.format_message_for_build.return_value = message - g.formatter.want_logs = False - g.formatter.want_logs_content = False - g.formatter.want_steps = False + g = self.GENERATOR_CLASS(message_formatter=formatter, **kwargs) return (g, build, buildset) + +class TestBuildSetGenerator(TestBuildSetGeneratorBase): + # Note: most of the functionality of BuildSetStatusGenerator is shared with + # BuildStatusGenerator and is tested there. + + GENERATOR_CLASS = BuildSetStatusGenerator + @defer.inlineCallbacks def buildset_message(self, g, builds, buildset): reporter = Mock() @@ -188,11 +209,20 @@ def test_generate_complete(self): 'logs': [] }) + @defer.inlineCallbacks + def test_generate_complete_no_builds(self): + g, _, buildset = yield self.setup_generator(insert_build=False) + report = yield self.generate(g, ('buildsets', 98, 'complete'), buildset) + + g.formatter.format_message_for_build.assert_not_called() + self.assertIsNone(report) + @defer.inlineCallbacks def test_generate_complete_non_matching_builder(self): g, _, buildset = yield self.setup_generator(builders=['non-matched']) report = yield self.generate(g, ('buildsets', 98, 'complete'), buildset) + g.formatter.format_message_for_build.assert_not_called() self.assertIsNone(report) @defer.inlineCallbacks @@ -200,4 +230,213 @@ def test_generate_complete_non_matching_result(self): g, _, buildset = yield self.setup_generator(mode=('failing',)) report = yield self.generate(g, ('buildsets', 98, 'complete'), buildset) + g.formatter.format_message_for_build.assert_not_called() self.assertIsNone(report) + + +class TestBuildSetCombinedGenerator(TestBuildSetGeneratorBase): + + GENERATOR_CLASS = BuildSetCombinedStatusGenerator + + @defer.inlineCallbacks + def buildset_message(self, g, buildset, builds): + reporter = Mock() + report = yield g.buildset_message(g.formatter, self.master, reporter, buildset, builds) + return report + + @defer.inlineCallbacks + def generate(self, g, key, buildset): + report = yield g.generate(self.master, Mock(), key, buildset) + return report + + @defer.inlineCallbacks + def test_buildset_message_normal(self): + g, build, buildset = yield self.setup_generator() + report = yield self.buildset_message(g, buildset, [build]) + + g.formatter.format_message_for_buildset.assert_called_with( + self.master, + buildset, + [build], + is_buildset=True, + mode=("passing",), + users=[] + ) + + # we retrieve build data differently when processing the buildset, so adjust it to match + del build['buildrequest'] + del build['parentbuild'] + del build['parentbuilder'] + + self.assertEqual(report, { + "body": "body", + "subject": "subject", + "type": "text", + "results": SUCCESS, + "builds": [build], + "buildset": buildset, + "users": [], + "patches": [], + "logs": [] + }) + + @defer.inlineCallbacks + def test_buildset_message_no_builds(self): + g, _, buildset = yield self.setup_generator(insert_build=False) + report = yield self.buildset_message(g, buildset, []) + + g.formatter.format_message_for_buildset.assert_called_with( + self.master, + buildset, + [], + is_buildset=True, + mode=("passing",), + users=[] + ) + + self.assertEqual(report, { + "body": "body", + "subject": "subject", + "type": "text", + "results": SUCCESS, + "builds": [], + "buildset": buildset, + "users": [], + "patches": [], + "logs": [] + }) + + @defer.inlineCallbacks + def test_buildset_message_no_result(self): + g, build, buildset = yield self.setup_generator(results=None) + buildset["results"] = None + report = yield self.buildset_message(g, buildset, [build]) + + g.formatter.format_message_for_buildset.assert_called_with( + self.master, + buildset, + [build], + is_buildset=True, + mode=("passing",), + users=[] + ) + + # we retrieve build data differently when processing the buildset, so adjust it to match + del build['buildrequest'] + del build['parentbuild'] + del build['parentbuilder'] + + self.assertEqual(report, { + "body": "body", + "subject": "subject", + "type": "text", + "results": None, + "builds": [build], + "buildset": buildset, + "users": [], + "patches": [], + "logs": [] + }) + + @defer.inlineCallbacks + def test_buildset_message_no_builds_no_result(self): + g, _, buildset = yield self.setup_generator(results=None, insert_build=False) + buildset["results"] = None + report = yield self.buildset_message(g, buildset, []) + + g.formatter.format_message_for_buildset.assert_called_with( + self.master, + buildset, + [], + is_buildset=True, + mode=("passing",), + users=[] + ) + + self.assertEqual(report, { + "body": "body", + "subject": "subject", + "type": "text", + "results": None, + "builds": [], + "buildset": buildset, + "users": [], + "patches": [], + "logs": [] + }) + + @defer.inlineCallbacks + def test_buildset_message_no_result_formatter_no_subject(self): + message = { + "body": "body", + "type": "text", + "subject": None, # deprecated unspecified subject + } + + g, build, buildset = yield self.setup_generator(message=message) + report = yield self.buildset_message(g, buildset, [build]) + + g.formatter.format_message_for_buildset.assert_called_with( + self.master, + buildset, + [build], + is_buildset=True, + mode=("passing",), + users=[] + ) + + # we retrieve build data differently when processing the buildset, so adjust it to match + del build['buildrequest'] + del build['parentbuild'] + del build['parentbuilder'] + + self.assertEqual(report, { + "body": "body", + "subject": None, + "type": "text", + "results": SUCCESS, + "builds": [build], + "buildset": buildset, + "users": [], + "patches": [], + "logs": [] + }) + + @defer.inlineCallbacks + def test_generate_complete(self): + g, _, buildset = yield self.setup_generator(insert_build=False) + report = yield self.generate(g, ("buildsets", 98, "complete"), buildset) + + self.assertEqual(report, { + "body": "body", + "subject": "subject", + "type": "text", + "results": SUCCESS, + "builds": [], + "buildset": buildset, + "users": [], + "patches": [], + "logs": [] + }) + + @defer.inlineCallbacks + def test_generate_complete_with_builds(self): + g, build, buildset = yield self.setup_generator(insert_build=True) + report = yield self.generate(g, ("buildsets", 98, "complete"), buildset) + + # we retrieve build data differently when processing the buildset, so adjust it to match + del build['buildrequest'] + del build['parentbuild'] + del build['parentbuilder'] + + self.assertEqual(report, { + "body": "body", + "subject": "subject", + "type": "text", + "results": SUCCESS, + "builds": [build], + "buildset": buildset, + "users": [], + "patches": [], + "logs": [] + }) diff --git a/master/buildbot/test/util/reporter.py b/master/buildbot/test/util/reporter.py index 68e76c183b08..c876c089f0a9 100644 --- a/master/buildbot/test/util/reporter.py +++ b/master/buildbot/test/util/reporter.py @@ -47,6 +47,20 @@ def insert_build(self, results, insert_ss=True, parent_plan=False, insert_patch= build = yield self.master.data.get(("builds", 20)) return build + @defer.inlineCallbacks + def insert_buildset_no_builds( + self, + results, + insert_ss=True, + parent_plan=False, + insert_patch=False + ): + self.insert_test_data( + [], results, insertSS=insert_ss, parentPlan=parent_plan, insert_patch=insert_patch + ) + buildset = yield self.master.data.get(("buildsets", 98)) + return buildset + @defer.inlineCallbacks def insert_build_finished(self, results=SUCCESS, **kwargs): return (yield self.insert_build(results=results, **kwargs)) diff --git a/master/docs/manual/configuration/report_generators/buildset.rst b/master/docs/manual/configuration/report_generators/buildset.rst index e9999a54787d..1a41d6aaa99b 100644 --- a/master/docs/manual/configuration/report_generators/buildset.rst +++ b/master/docs/manual/configuration/report_generators/buildset.rst @@ -8,7 +8,10 @@ BuildSetStatusGenerator .. py:class:: buildbot.reporters.BuildSetStatusGenerator This report generator sends a message about builds in a buildset. -It is very similar to :bb:reportgen:`BuildStatusGenerator` but sends single message about all builds in a buildset, not individual builds. + +Message formatters are invoked for each matching build in the buildset. The collected messages are +then joined and sent as a single message. :bb:reportgen:`BuildStatusGenerator` report generator +uses the same message generation logic, but a single, not multiple builds. The following parameters are supported: diff --git a/master/docs/manual/configuration/report_generators/buildset_combined.rst b/master/docs/manual/configuration/report_generators/buildset_combined.rst new file mode 100644 index 000000000000..c7eca95ddb66 --- /dev/null +++ b/master/docs/manual/configuration/report_generators/buildset_combined.rst @@ -0,0 +1,27 @@ +.. bb:reportgen:: BuildSetCombinedStatusGenerator + +.. _Reportgen-BuildSetCombinedStatusGenerator: + +BuildSetCombinedStatusGenerator ++++++++++++++++++++++++++++++++ + +.. py:class:: buildbot.reporters.BuildSetCombinedStatusGenerator + +This report generator sends a message about a buildset. + +Message formatter is invoked only once for all builds in the buildset. + +It is very similar to :bb:reportgen:`BuildSetCombinedStatusGenerator` but invokes message formatters for each +matching build in the buildset. The collected messages are then joined and sent as a single message. + + +A buildset without any builds is useful as a means to report to code review system that a +particular code version does not need to be tested. For example in cases when a pull request is +updated with the only difference being commit message being changed. + +The following parameters are supported: + +``message_formatter`` + (instance of ``reporters.MessageFormatter``) + This is an instance of the ``reporters.MessageFormatter`` class that will be used to generate + message for the buildset. diff --git a/master/docs/manual/configuration/report_generators/formatter.rst b/master/docs/manual/configuration/report_generators/formatter.rst index af0a41287fb2..1659f90c4141 100644 --- a/master/docs/manual/configuration/report_generators/formatter.rst +++ b/master/docs/manual/configuration/report_generators/formatter.rst @@ -66,10 +66,11 @@ The constructor of the class takes the following arguments: This implies ``want_logs`` and ``wantSteps`` to be `True`. Use it only when mandatory, as this greatly increases the overhead in terms of CPU and memory on the master. -Context -~~~~~~~ +Context (build) +~~~~~~~~~~~~~~~ -The context that is given to the template consists of the following data: +In the case the message formatter is used to create message for a build the context that is given +to the template consists of the following data: ``results`` The results of the build as an integer. @@ -163,6 +164,54 @@ The context that is given to the template consists of the following data: ``sourcestamps`` A string identifying the source stamps for which the build was made. +Context (buildset) +~~~~~~~~~~~~~~~~~~ + +In the case the message formatter is used to create message for an buildset itself (see +``BuildSetCombinedStatusGenerator``), the context that is given to the template consists of the +following data: + +``results`` + The results of the buildset as an integer. + Equivalent to ``build['results']``. + +``result_names`` + A collection that allows accessing a textual identifier of build result. + The intended usage is ``result_names[results]``. + + The following are possible values: ``success``, ``warnings``, ``failure``, ``skipped``, ``exception``, ``retry``, ``cancelled``. + +``mode`` + The mode argument that has been passed to the report generator. + +``buildset`` + The :bb:rtype:`buildset` dictionary from data API. + +``builds`` + A list of :bb:rtype:`build` dictionaries from data API. The builds are part of the buildset + that is being formatted. + +``is_buildset`` + Always ``True``. + +``projects`` + A string identifying the projects that the buildset was built for. + +``status_detected`` + String that describes the build in terms of current buildset results, previous build results and ``mode``. + +``buildbot_title`` + The title of the Buildbot instance as per ``c['title']`` from the ``master.cfg`` + +``buildbot_url`` + The URL of the Buildbot instance as per ``c['buildbotURL']`` from the ``master.cfg`` + +``blamelist`` + The list of users responsible for the buildset. + +``sourcestamps`` + A string identifying the source stamps for which the buildset was made. + Examples ~~~~~~~~ diff --git a/master/docs/manual/configuration/report_generators/formatter_function.rst b/master/docs/manual/configuration/report_generators/formatter_function.rst index 84a1142d76d2..851f70cbc6de 100644 --- a/master/docs/manual/configuration/report_generators/formatter_function.rst +++ b/master/docs/manual/configuration/report_generators/formatter_function.rst @@ -10,7 +10,17 @@ As opposed to :ref:`MessageFormatterRenderable`, more information is made availa .. py:class:: MessageFormatterFunction(function, template_type, want_properties=True, wantProperties=None, want_steps=False, wantSteps=None, wantLogs=None, want_logs=False, want_logs_content=False) - :param callable function: A callable that will be called with a dictionary that contains ``build`` key with the value that contains the build dictionary as received from the data API. + :param callable function: A callable that will be called with a dictionary. + + If the message formatter is used to format a build, the dictionary contains ``build`` key + with the build dictionary as received from the data API. + + If the message formatter is used to format a buildset (e.g. when used from + :bb:reportgen:`BuildSetCombinedStatusGenerator`), the dictionary contains the following: + + - ``buildset`` key with the buildset dictionary as received from the data API. + - ``builds`` key with the builds dictionaries as received from the data API. + :param string template_type: either ``plain``, ``html`` or ``json`` depending on the output of the formatter. JSON output must not be encoded. :param boolean want_properties: include 'properties' in the build dictionary diff --git a/master/docs/manual/configuration/report_generators/formatter_renderable.rst b/master/docs/manual/configuration/report_generators/formatter_renderable.rst index 2d4514aa8a23..fc0978201a99 100644 --- a/master/docs/manual/configuration/report_generators/formatter_renderable.rst +++ b/master/docs/manual/configuration/report_generators/formatter_renderable.rst @@ -9,6 +9,9 @@ This formatter is used to format messages in :ref:`Reportgen-BuildStatusGenerato It renders any renderable using the properties of the build that was passed by the status generator. +This message formatter does not support formatting complete buildsets ( +:bb:reportgen:`BuildSetCombinedStatusGenerator`). + The constructor of the class takes the following arguments: ``template`` diff --git a/master/docs/manual/configuration/report_generators/index.rst b/master/docs/manual/configuration/report_generators/index.rst index c5587e8b73ca..6e6cc2d49c23 100644 --- a/master/docs/manual/configuration/report_generators/index.rst +++ b/master/docs/manual/configuration/report_generators/index.rst @@ -10,6 +10,7 @@ Report Generators build build_start_end buildset + buildset_combined worker formatter formatter_function @@ -43,12 +44,14 @@ The following report generators are available: * :ref:`Reportgen-BuildStatusGenerator` * :ref:`Reportgen-BuildStartEndStatusGenerator` * :ref:`Reportgen-BuildSetStatusGenerator` + * :ref:`Reportgen-BuildSetCombinedStatusGenerator` * :ref:`Reportgen-WorkerMissingGenerator` The report generators may customize the reports using message formatters. The following message formatter classes are provided: - * :ref:`MessageFormatter` (used in ``BuildStatusGenerator``, ``BuildStartEndStatusGenerator`` and ``BuildSetStatusGenerator``) + * :ref:`MessageFormatter` (used in ``BuildStatusGenerator``, ``BuildStartEndStatusGenerator``, + ``BuildSetCombinedStatusGenerator`` and ``BuildSetStatusGenerator``) * :ref:`MessageFormatterRenderable` (used in ``BuildStatusGenerator`` and ``BuildStartEndStatusGenerator``) * :ref:`MessageFormatterFunction` (used in ``BuildStatusGenerator`` and ``BuildStartEndStatusGenerator``) * :ref:`MessageFormatterMissingWorkers` (used in ``WorkerMissingGenerator``) diff --git a/master/setup.py b/master/setup.py index 8bec42a0cdd1..8f1944850ad6 100755 --- a/master/setup.py +++ b/master/setup.py @@ -334,7 +334,12 @@ def define_plugin_entries(groups): ('buildbot.reporters.generators.buildrequest', [ 'BuildRequestGenerator' ]), - ('buildbot.reporters.generators.buildset', ['BuildSetStatusGenerator']), + ("buildbot.reporters.generators.buildset", + [ + "BuildSetCombinedStatusGenerator", + "BuildSetStatusGenerator", + ] + ), ('buildbot.reporters.generators.worker', ['WorkerMissingGenerator']), ('buildbot.reporters.mail', ['MailNotifier']), ('buildbot.reporters.pushjet', ['PushjetNotifier']), diff --git a/newsfragments/combined-buildsets-status-generator.feature b/newsfragments/combined-buildsets-status-generator.feature new file mode 100644 index 000000000000..9484643a2fdb --- /dev/null +++ b/newsfragments/combined-buildsets-status-generator.feature @@ -0,0 +1,2 @@ +Implemented a report generator (``BuildSetCombinedStatusGenerator``) that can access complete +information about a buildset. From 6c3998284dcd79d37e801fee79f38c0321a7a291 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:29:54 +0200 Subject: [PATCH 41/68] db: Add API to retrieve source stamps for buildset --- common/code_spelling_ignore_words.txt | 1 + master/buildbot/db/sourcestamps.py | 22 +++ master/buildbot/test/fakedb/sourcestamps.py | 9 ++ .../test/unit/db/test_sourcestamps.py | 138 +++++++++++++++++- .../docs/developer/database/sourcestamps.rst | 7 + master/docs/spelling_wordlist.txt | 1 + .../db-get-source-stamps-for-buildset.feature | 2 + 7 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 newsfragments/db-get-source-stamps-for-buildset.feature diff --git a/common/code_spelling_ignore_words.txt b/common/code_spelling_ignore_words.txt index 20c4a15210a2..b498b1b83a2a 100644 --- a/common/code_spelling_ignore_words.txt +++ b/common/code_spelling_ignore_words.txt @@ -209,6 +209,7 @@ buildroot build's buildset buildsetcomplete +buildsetid buildset's buildsets buildsetsubmitted diff --git a/master/buildbot/db/sourcestamps.py b/master/buildbot/db/sourcestamps.py index 920cca1b42cc..07236611b28d 100644 --- a/master/buildbot/db/sourcestamps.py +++ b/master/buildbot/db/sourcestamps.py @@ -112,6 +112,28 @@ def thd(conn): return ssdict return self.db.pool.do(thd) + # returns a Deferred that returns a value + def get_sourcestamps_for_buildset(self, buildsetid): + def thd(conn): + bsets_tbl = self.db.model.buildsets + bsss_tbl = self.db.model.buildset_sourcestamps + sstamps_tbl = self.db.model.sourcestamps + + from_clause = bsets_tbl.join( + bsss_tbl, bsets_tbl.c.id == bsss_tbl.c.buildsetid + ).join(sstamps_tbl, bsss_tbl.c.sourcestampid == sstamps_tbl.c.id) + + q = ( + sa.select([sstamps_tbl]) + .select_from(from_clause) + .where(bsets_tbl.c.id == buildsetid) + ) + + res = conn.execute(q) + return [self._rowToSsdict_thd(conn, row) for row in res.fetchall()] + + return self.db.pool.do(thd) + # returns a Deferred that returns a value def getSourceStampsForBuild(self, buildid): assert buildid > 0 diff --git a/master/buildbot/test/fakedb/sourcestamps.py b/master/buildbot/test/fakedb/sourcestamps.py index cea01b7a27d9..62ba21366da8 100644 --- a/master/buildbot/test/fakedb/sourcestamps.py +++ b/master/buildbot/test/fakedb/sourcestamps.py @@ -157,6 +157,15 @@ def _getSourceStamp_sync(self, ssid): else: return None + @defer.inlineCallbacks + def get_sourcestamps_for_buildset(self, buildsetid): + bset = yield self.db.buildsets.getBuildset(buildsetid) + + results = [] + for ssid in bset["sourcestamps"]: + results.append((yield self.getSourceStamp(ssid))) + return results + @defer.inlineCallbacks def getSourceStampsForBuild(self, buildid): build = yield self.db.builds.getBuild(buildid) diff --git a/master/buildbot/test/unit/db/test_sourcestamps.py b/master/buildbot/test/unit/db/test_sourcestamps.py index 95dd370955cd..d7722db7058c 100644 --- a/master/buildbot/test/unit/db/test_sourcestamps.py +++ b/master/buildbot/test/unit/db/test_sourcestamps.py @@ -51,6 +51,16 @@ def test_signature_getSourceStamps(self): def getSourceStamps(self): pass + def test_signature_getSourceStampsForBuild(self): + @self.assertArgSpecMatches(self.db.sourcestamps.getSourceStampsForBuild) + def getSourceStampsForBuild(self, buildid): + pass + + def test_signature_get_sourcestamps_for_buildset(self): + @self.assertArgSpecMatches(self.db.sourcestamps.get_sourcestamps_for_buildset) + def get_sourcestamps_for_buildset(self, buildsetid): + pass + @defer.inlineCallbacks def test_findSourceStampId_simple(self): self.reactor.advance(CREATED_AT) @@ -249,10 +259,130 @@ def test_getSourceStamps_empty(self): self.assertEqual(sourcestamps, []) - def test_signature_getSourceStampsForBuild(self): - @self.assertArgSpecMatches(self.db.sourcestamps.getSourceStampsForBuild) - def getSourceStampsForBuild(self, buildid): - pass + @defer.inlineCallbacks + def test_get_sourcestamps_for_buildset_one_codebase(self): + yield self.insert_test_data( + [ + fakedb.Master(id=88, name="bar"), + fakedb.Worker(id=13, name="one"), + fakedb.Builder(id=77, name="A"), + fakedb.SourceStamp(id=234, codebase="A", created_at=CREATED_AT, revision="aaa"), + fakedb.Buildset( + id=30, + reason="foo", + submitted_at=1300305712, + results=-1 + ), + fakedb.BuildsetSourceStamp(sourcestampid=234, buildsetid=30), + ] + ) + + sourcestamps = yield self.db.sourcestamps.get_sourcestamps_for_buildset(30) + + expected = [ + { + "branch": "master", + "codebase": "A", + "created_at": epoch2datetime(CREATED_AT), + "patch_author": None, + "patch_body": None, + "patch_comment": None, + "patch_level": None, + "patch_subdir": None, + "patchid": None, + "project": "proj", + "repository": "repo", + "revision": "aaa", + "ssid": 234 + } + ] + + self.assertEqual( + sorted(sourcestamps, key=sourceStampKey), + sorted(expected, key=sourceStampKey) + ) + + @defer.inlineCallbacks + def test_get_sourcestamps_for_buildset_three_codebases(self): + yield self.insert_test_data( + [ + fakedb.Master(id=88, name="bar"), + fakedb.Worker(id=13, name="one"), + fakedb.Builder(id=77, name="A"), + fakedb.SourceStamp(id=234, codebase="A", created_at=CREATED_AT, revision="aaa"), + fakedb.SourceStamp( + id=235, + codebase="B", + created_at=CREATED_AT + 10, + revision="bbb" + ), + fakedb.SourceStamp( + id=236, + codebase="C", + created_at=CREATED_AT + 20, + revision="ccc" + ), + fakedb.Buildset(id=30, reason="foo", submitted_at=1300305712, results=-1), + fakedb.BuildsetSourceStamp(sourcestampid=234, buildsetid=30), + fakedb.BuildsetSourceStamp(sourcestampid=235, buildsetid=30), + fakedb.BuildsetSourceStamp(sourcestampid=236, buildsetid=30) + ] + ) + + sourcestamps = yield self.db.sourcestamps.get_sourcestamps_for_buildset(30) + + expected = [ + { + "branch": "master", + "codebase": "A", + "created_at": epoch2datetime(CREATED_AT), + "patch_author": None, + "patch_body": None, + "patch_comment": None, + "patch_level": None, + "patch_subdir": None, + "patchid": None, + "project": "proj", + "repository": "repo", + "revision": "aaa", + "ssid": 234 + }, + { + "branch": "master", + "codebase": "B", + "created_at": epoch2datetime(CREATED_AT + 10), + "patch_author": None, + "patch_body": None, + "patch_comment": None, + "patch_level": None, + "patch_subdir": None, + "patchid": None, + "project": "proj", + "repository": "repo", + "revision": "bbb", + "ssid": 235 + }, + { + "branch": "master", + "codebase": "C", + "created_at": epoch2datetime(CREATED_AT + 20), + "patch_author": None, + "patch_body": None, + "patch_comment": None, + "patch_level": None, + "patch_subdir": None, + "patchid": None, + "project": "proj", + "repository": "repo", + "revision": "ccc", + "ssid": 236 + } + ] + + self.assertEqual( + sorted(sourcestamps, key=sourceStampKey), + sorted(expected, key=sourceStampKey) + ) @defer.inlineCallbacks def do_test_getSourceStampsForBuild(self, rows, buildid, expected): diff --git a/master/docs/developer/database/sourcestamps.rst b/master/docs/developer/database/sourcestamps.rst index 1d39f3e2611b..2776bdca2003 100644 --- a/master/docs/developer/database/sourcestamps.rst +++ b/master/docs/developer/database/sourcestamps.rst @@ -88,6 +88,13 @@ Source stamps connector You probably don't want to do this! This method will be extended to allow appropriate filtering. + .. py:method:: get_sourcestamps_for_buildset(buildsetid) + + :param buildsetid: buildset ID + :returns: list of ssdict, via Deferred + + Get sourcestamps related to a buildset. + .. py:method:: getSourceStampsForBuild(buildid) :param buildid: build ID diff --git a/master/docs/spelling_wordlist.txt b/master/docs/spelling_wordlist.txt index 5eb784e8b35c..f9f30aef77f3 100644 --- a/master/docs/spelling_wordlist.txt +++ b/master/docs/spelling_wordlist.txt @@ -110,6 +110,7 @@ buildrequestid buildrequests buildset Buildset +buildsetid buildsetids buildsets Buildsets diff --git a/newsfragments/db-get-source-stamps-for-buildset.feature b/newsfragments/db-get-source-stamps-for-buildset.feature new file mode 100644 index 000000000000..374cb3aadf6c --- /dev/null +++ b/newsfragments/db-get-source-stamps-for-buildset.feature @@ -0,0 +1,2 @@ +Low level database API now has ``get_sourcestamps_for_buildset`` to get source stamps for a +buildset. From 66691a1baf44b04d9df1dc3541822f3fd7c446d8 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:29:55 +0200 Subject: [PATCH 42/68] data: Add endpoint to retrieve source stamps for buildset --- master/buildbot/data/sourcestamps.py | 11 +++++-- master/buildbot/test/fakedb/sourcestamps.py | 2 ++ .../test/unit/data/test_sourcestamps.py | 31 +++++++++++++++---- .../db-get-source-stamps-for-buildset.feature | 3 +- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/master/buildbot/data/sourcestamps.py b/master/buildbot/data/sourcestamps.py index 6c3ebfdd731f..706365f2dcd9 100644 --- a/master/buildbot/data/sourcestamps.py +++ b/master/buildbot/data/sourcestamps.py @@ -63,13 +63,20 @@ class SourceStampsEndpoint(base.Endpoint): kind = base.EndpointKind.COLLECTION pathPatterns = """ /sourcestamps + /buildsets/:buildsetid/sourcestamps """ rootLinkName = 'sourcestamps' @defer.inlineCallbacks def get(self, resultSpec, kwargs): - return [_db2data(ssdict) for ssdict in - (yield self.master.db.sourcestamps.getSourceStamps())] + buildsetid = kwargs.get("buildsetid") + if buildsetid is not None: + sourcestamps = \ + yield self.master.db.sourcestamps.get_sourcestamps_for_buildset(buildsetid) + else: + sourcestamps = yield self.master.db.sourcestamps.getSourceStamps() + + return [_db2data(ssdict) for ssdict in sourcestamps] class SourceStamp(base.ResourceType): diff --git a/master/buildbot/test/fakedb/sourcestamps.py b/master/buildbot/test/fakedb/sourcestamps.py index 62ba21366da8..bf32e676858b 100644 --- a/master/buildbot/test/fakedb/sourcestamps.py +++ b/master/buildbot/test/fakedb/sourcestamps.py @@ -160,6 +160,8 @@ def _getSourceStamp_sync(self, ssid): @defer.inlineCallbacks def get_sourcestamps_for_buildset(self, buildsetid): bset = yield self.db.buildsets.getBuildset(buildsetid) + if bset is None: + return [] results = [] for ssid in bset["sourcestamps"]: diff --git a/master/buildbot/test/unit/data/test_sourcestamps.py b/master/buildbot/test/unit/data/test_sourcestamps.py index eeda9cf4ecf6..39fff1902ce1 100644 --- a/master/buildbot/test/unit/data/test_sourcestamps.py +++ b/master/buildbot/test/unit/data/test_sourcestamps.py @@ -76,10 +76,16 @@ class SourceStampsEndpoint(endpoint.EndpointMixin, unittest.TestCase): def setUp(self): self.setUpEndpoint() - self.db.insert_test_data([ - fakedb.SourceStamp(id=13), - fakedb.SourceStamp(id=14), - ]) + self.db.insert_test_data( + [ + fakedb.Buildset(id=30, reason="foo", submitted_at=1300305712, results=-1), + fakedb.SourceStamp(id=13), + fakedb.SourceStamp(id=14), + fakedb.SourceStamp(id=15), + fakedb.BuildsetSourceStamp(sourcestampid=13, buildsetid=30), + fakedb.BuildsetSourceStamp(sourcestampid=14, buildsetid=30), + ] + ) def tearDown(self): self.tearDownEndpoint() @@ -91,8 +97,21 @@ def test_get(self): for m in sourcestamps: self.validateData(m) - self.assertEqual(sorted([m['ssid'] for m in sourcestamps]), - [13, 14]) + self.assertEqual(sorted([m['ssid'] for m in sourcestamps]), [13, 14, 15]) + + @defer.inlineCallbacks + def test_get_by_buildsetid_no_buildset(self): + sourcestamps = yield self.callGet(("buildsets", 101, "sourcestamps")) + self.assertEqual(sourcestamps, []) + + @defer.inlineCallbacks + def test_get_by_buildsetid(self): + sourcestamps = yield self.callGet(("buildsets", 30, "sourcestamps")) + + for m in sourcestamps: + self.validateData(m) + + self.assertEqual(sorted([m['ssid'] for m in sourcestamps]), [13, 14]) class SourceStamp(unittest.TestCase): diff --git a/newsfragments/db-get-source-stamps-for-buildset.feature b/newsfragments/db-get-source-stamps-for-buildset.feature index 374cb3aadf6c..0f7e4143f8aa 100644 --- a/newsfragments/db-get-source-stamps-for-buildset.feature +++ b/newsfragments/db-get-source-stamps-for-buildset.feature @@ -1,2 +1,3 @@ Low level database API now has ``get_sourcestamps_for_buildset`` to get source stamps for a -buildset. +buildset. "/buildsets/:buildsetid/sourcestamps" endpoint has been added to access this from the +Data API. From 9a239ab10cc4f5ec495be4088cc0cfa03d389560 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 06:42:12 +0200 Subject: [PATCH 43/68] data: Use explicit Buildset conversion --- master/buildbot/data/buildsets.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/master/buildbot/data/buildsets.py b/master/buildbot/data/buildsets.py index 4eeeb3b7e22e..28aa007d33ff 100644 --- a/master/buildbot/data/buildsets.py +++ b/master/buildbot/data/buildsets.py @@ -36,7 +36,17 @@ def db2data(self, bsdict): if not bsdict: return None - buildset = bsdict.copy() + buildset = { + "bsid": bsdict["bsid"], + "external_idstring": bsdict["external_idstring"], + "reason": bsdict["reason"], + "submitted_at": datetime2epoch(bsdict["submitted_at"]), + "complete": bsdict["complete"], + "complete_at": datetime2epoch(bsdict["complete_at"]), + "results": bsdict["results"], + "parent_buildid": bsdict["parent_buildid"], + "parent_relationship": bsdict["parent_relationship"], + } # gather the actual sourcestamps, in parallel sourcestamps = [] @@ -46,14 +56,10 @@ def getSs(ssid): ss = yield self.master.data.get(('sourcestamps', str(ssid))) sourcestamps.append(ss) yield defer.DeferredList([getSs(id) - for id in buildset['sourcestamps']], + for id in bsdict['sourcestamps']], fireOnOneErrback=True, consumeErrors=True) - buildset['sourcestamps'] = sourcestamps - - # minor modifications - buildset['submitted_at'] = datetime2epoch(buildset['submitted_at']) - buildset['complete_at'] = datetime2epoch(buildset['complete_at']) + buildset['sourcestamps'] = sourcestamps return buildset fieldMapping = { From 560f8216173230c1e3b703f31815c4a7180c802b Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 06:42:13 +0200 Subject: [PATCH 44/68] db: Use normal dict for buildsets --- master/buildbot/db/buildsets.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/master/buildbot/db/buildsets.py b/master/buildbot/db/buildsets.py index fdbf1542dc0b..cfd389658fe7 100644 --- a/master/buildbot/db/buildsets.py +++ b/master/buildbot/db/buildsets.py @@ -28,10 +28,6 @@ from buildbot.util import epoch2datetime -class BsDict(dict): - pass - - class BsProps(dict): pass @@ -245,12 +241,15 @@ def _thd_row2dict(self, conn, row): conn.execute(sa.select([tbl.c.sourcestampid], (tbl.c.buildsetid == row.id))).fetchall()] - return BsDict(external_idstring=row.external_idstring, - reason=row.reason, - submitted_at=epoch2datetime(row.submitted_at), - complete=bool(row.complete), - complete_at=epoch2datetime(row.complete_at), - results=row.results, - bsid=row.id, sourcestamps=sourcestamps, - parent_buildid=row.parent_buildid, - parent_relationship=row.parent_relationship) + return { + "external_idstring": row.external_idstring, + "reason": row.reason, + "submitted_at": epoch2datetime(row.submitted_at), + "complete": bool(row.complete), + "complete_at": epoch2datetime(row.complete_at), + "results": row.results, + "bsid": row.id, + "sourcestamps": sourcestamps, + "parent_buildid": row.parent_buildid, + "parent_relationship": row.parent_relationship + } From 04924f45ab4f30b4167b6f9f26ab4d5fd3d9371d Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 06:42:14 +0200 Subject: [PATCH 45/68] data: Use defer.inlineCallbacks in BuildsetsEndpoint --- master/buildbot/data/buildsets.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/master/buildbot/data/buildsets.py b/master/buildbot/data/buildsets.py index 28aa007d33ff..84a9a2d28513 100644 --- a/master/buildbot/data/buildsets.py +++ b/master/buildbot/data/buildsets.py @@ -97,22 +97,21 @@ class BuildsetsEndpoint(Db2DataMixin, base.Endpoint): """ rootLinkName = 'buildsets' + @defer.inlineCallbacks def get(self, resultSpec, kwargs): complete = resultSpec.popBooleanFilter('complete') resultSpec.fieldMapping = self.fieldMapping - d = self.master.db.buildsets.getBuildsets( - complete=complete, resultSpec=resultSpec) - - @d.addCallback - def db2data(buildsets): - d = defer.DeferredList([self.db2data(bs) for bs in buildsets], - fireOnOneErrback=True, consumeErrors=True) - - @d.addCallback - def getResults(res): - return [r[1] for r in res] - return d - return d + buildsets = yield self.master.db.buildsets.getBuildsets( + complete=complete, + resultSpec=resultSpec + ) + + buildsets = yield defer.gatherResults( + [self.db2data(bs) for bs in buildsets], + consumeErrors=True + ) + + return buildsets class Buildset(base.ResourceType): From 01862a594cef77e7bc536848a6f4e07cebe9e5d0 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:30:00 +0200 Subject: [PATCH 46/68] reporters: Add support for extra information in message formatter dicts --- .../reporters/generators/buildrequest.py | 3 +- .../buildbot/reporters/generators/buildset.py | 9 +++++- master/buildbot/reporters/generators/utils.py | 21 +++++++++++- master/buildbot/reporters/message.py | 21 ++++++++++-- .../buildbot/test/unit/reporters/test_base.py | 4 ++- .../unit/reporters/test_bitbucketserver.py | 3 +- .../unit/reporters/test_generators_build.py | 19 +++++++++-- .../reporters/test_generators_buildrequest.py | 6 +++- .../reporters/test_generators_buildset.py | 16 +++++++++- .../unit/reporters/test_generators_utils.py | 21 ++++++++++++ .../buildbot/test/unit/reporters/test_mail.py | 9 ++++-- .../test/unit/reporters/test_message.py | 32 ++++++++++++------- .../report_generators/buildset.rst | 14 ++++++++ .../configuration/report_generators/index.rst | 14 ++++++++ .../configuration/reporters/reporter_base.rst | 3 ++ newsfragments/reporter-extra-info.feature | 1 + 16 files changed, 169 insertions(+), 27 deletions(-) create mode 100644 newsfragments/reporter-extra-info.feature diff --git a/master/buildbot/reporters/generators/buildrequest.py b/master/buildbot/reporters/generators/buildrequest.py index ab1b46eabe3d..86231357d9d0 100644 --- a/master/buildbot/reporters/generators/buildrequest.py +++ b/master/buildbot/reporters/generators/buildrequest.py @@ -91,5 +91,6 @@ def buildrequest_message(self, master, build): "buildset": build["buildset"], 'users': list(users), 'patches': patches, - 'logs': [] + 'logs': [], + "extra_info": buildmsg["extra_info"], } diff --git a/master/buildbot/reporters/generators/buildset.py b/master/buildbot/reporters/generators/buildset.py index f65ba9143736..afe920a1d457 100644 --- a/master/buildbot/reporters/generators/buildset.py +++ b/master/buildbot/reporters/generators/buildset.py @@ -79,6 +79,7 @@ def buildset_message(self, formatter, master, reporter, builds, buildset): body = None subject = None msgtype = None + extra_info = None users = set() results = buildset["results"] for build in builds: @@ -103,6 +104,10 @@ def buildset_message(self, formatter, master, reporter, builds, buildset): if not ok: continue + extra_info, ok = self._merge_extra_info(extra_info, buildmsg["extra_info"]) + if not ok: + continue + if subject is None and self.subject is not None: subject = self.subject % {'result': statusToString(results), 'projectName': master.config.title, @@ -118,7 +123,8 @@ def buildset_message(self, formatter, master, reporter, builds, buildset): "buildset": buildset, 'users': list(users), 'patches': patches, - 'logs': logs + 'logs': logs, + "extra_info": extra_info, } def _want_previous_build(self): @@ -175,6 +181,7 @@ def buildset_message(self, formatter, master, reporter, buildset, builds): "body": buildmsg["body"], "subject": buildmsg["subject"], "type": buildmsg["type"], + "extra_info": buildmsg["extra_info"], "results": buildset["results"], "builds": builds, "buildset": buildset, diff --git a/master/buildbot/reporters/generators/utils.py b/master/buildbot/reporters/generators/utils.py index 9c3793db3d49..635a54010720 100644 --- a/master/buildbot/reporters/generators/utils.py +++ b/master/buildbot/reporters/generators/utils.py @@ -161,6 +161,24 @@ def _merge_body(self, body, new_body): f'({type(body)} and {type(new_body)}). Ignoring') return body, False + def _merge_extra_info(self, info, new_info): + if info is None: + return new_info, True + if new_info is None: + return info, True + + for key, new_value in new_info.items(): + if key not in info: + info[key] = new_value + continue + + value = info[key] + for vkey, vvalue in new_value.items(): + if vkey not in value: + value[vkey] = vvalue + + return info, True + def _get_patches_for_build(self, build): if not self.add_patch: return [] @@ -199,7 +217,8 @@ def build_message(self, formatter, master, reporter, build): "buildset": build["buildset"], 'users': list(users), 'patches': patches, - 'logs': logs + 'logs': logs, + "extra_info": buildmsg["extra_info"], } @defer.inlineCallbacks diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py index a7d5a989ec38..238c1719f46b 100644 --- a/master/buildbot/reporters/message.py +++ b/master/buildbot/reporters/message.py @@ -234,6 +234,8 @@ def render_message_dict(self, master, context): of dictionary, list or string. This must not change during all invocations of a particular instance of the formatter. + - "extra_info" is an optional dictionary of dictionaries of extra information. + In case of a report being created for multiple builds (e.g. in the case of a buildset), the values returned by message formatter are concatenated. If this is not possible (e.g. if the body is a dictionary), any subsequent messages are ignored. @@ -241,10 +243,19 @@ def render_message_dict(self, master, context): yield self.buildAdditionalContext(master, context) context.update(self.context) + body, subject, extra_info = yield defer.gatherResults( + [ + defer.maybeDeferred(self.render_message_body, context), + defer.maybeDeferred(self.render_message_subject, context), + defer.maybeDeferred(self.render_message_extra_info, context), + ] + ) + return { - 'body': (yield self.render_message_body(context)), + "body": body, 'type': self.template_type, - 'subject': (yield self.render_message_subject(context)) + "subject": subject, + "extra_info": extra_info, } def render_message_body(self, context): @@ -253,6 +264,9 @@ def render_message_body(self, context): def render_message_subject(self, context): return None + def render_message_extra_info(self, context): + return None + def format_message_for_build(self, master, build, **kwargs): # Known kwargs keys: mode, users, is_buildset raise NotImplementedError @@ -267,7 +281,8 @@ def format_message_for_build(self, master, build, **kwargs): return { 'body': None, 'type': 'plain', - 'subject': None + 'subject': None, + "extra_info": None } def format_message_for_buildset(self, master, buildset, builds, **kwargs): diff --git a/master/buildbot/test/unit/reporters/test_base.py b/master/buildbot/test/unit/reporters/test_base.py index d8fcda40b87e..57f2d5b63c4f 100644 --- a/master/buildbot/test/unit/reporters/test_base.py +++ b/master/buildbot/test/unit/reporters/test_base.py @@ -64,7 +64,8 @@ def setup_build_message(self, **kwargs): formatter.format_message_for_build.return_value = { "body": "body", "type": "text", - "subject": "subject" + "subject": "subject", + "extra_info": None, } formatter.want_properties = False formatter.want_steps = False @@ -98,6 +99,7 @@ def test_buildMessage_nominal(self): 'body': 'body', 'subject': 'subject', 'type': 'text', + "extra_info": None, 'results': FAILURE, 'builds': [build], "buildset": buildset, diff --git a/master/buildbot/test/unit/reporters/test_bitbucketserver.py b/master/buildbot/test/unit/reporters/test_bitbucketserver.py index e17499778bf1..67f85e518c1b 100644 --- a/master/buildbot/test/unit/reporters/test_bitbucketserver.py +++ b/master/buildbot/test/unit/reporters/test_bitbucketserver.py @@ -511,7 +511,8 @@ def setupReporter(self, verbose=True, generator_class=BuildStatusGenerator, **kw formatter.format_message_for_build.return_value = { "body": UNICODE_BODY, "type": "text", - "subject": "subject" + "subject": "subject", + "extra_info": None, } formatter.want_properties = True formatter.want_steps = False diff --git a/master/buildbot/test/unit/reporters/test_generators_build.py b/master/buildbot/test/unit/reporters/test_generators_build.py index 1a7bb1ca6ae9..5eb0f88fd8ae 100644 --- a/master/buildbot/test/unit/reporters/test_generators_build.py +++ b/master/buildbot/test/unit/reporters/test_generators_build.py @@ -54,7 +54,8 @@ def setup_generator(self, results=SUCCESS, message=None, db_args=None, **kwargs) message = { "body": "body", "type": "text", - "subject": "subject" + "subject": "subject", + "extra_info": None, } if db_args is None: db_args = {} @@ -98,6 +99,7 @@ def test_build_message_nominal(self): 'body': 'body', 'subject': 'subject', 'type': 'text', + "extra_info": None, 'results': SUCCESS, 'builds': [build], "buildset": buildset, @@ -119,6 +121,7 @@ def test_build_message_no_result(self): 'body': 'body', 'subject': 'subject', 'type': 'text', + "extra_info": None, 'results': None, 'builds': [build], "buildset": buildset, @@ -138,6 +141,7 @@ def test_build_message_no_result_formatter_no_subject(self): "body": "body", "type": "text", "subject": None, # deprecated unspecified subject + "extra_info": None, } g, build, buildset = yield self.setup_generator( @@ -155,6 +159,7 @@ def test_build_message_no_result_formatter_no_subject(self): 'body': 'body', 'subject': 'Buildbot not finished in Buildbot on Builder0', 'type': 'text', + "extra_info": None, 'results': None, 'builds': [build], "buildset": buildset, @@ -209,6 +214,7 @@ def test_generate_finished(self): 'body': 'body', 'subject': 'subject', 'type': 'text', + "extra_info": None, 'results': SUCCESS, 'builds': [build], "buildset": buildset, @@ -244,6 +250,7 @@ def test_generate_new(self): 'body': 'body', 'subject': 'subject', 'type': 'text', + "extra_info": None, 'results': None, 'builds': [build], "buildset": buildset, @@ -291,14 +298,16 @@ def setup_generator(self, results=SUCCESS, start_message=None, end_message=None, start_message = { "body": "start body", "type": "plain", - "subject": "start subject" + "subject": "start subject", + "extra_info": None, } if end_message is None: end_message = { "body": "end body", "type": "plain", - "subject": "end subject" + "subject": "end subject", + "extra_info": None, } g = BuildStartEndStatusGenerator(**kwargs) @@ -342,6 +351,7 @@ def test_build_message_start(self): 'body': 'start body', 'subject': 'start subject', 'type': 'plain', + "extra_info": None, 'results': SUCCESS, 'builds': [build], "buildset": buildset, @@ -367,6 +377,7 @@ def test_build_message_start_no_result(self): 'body': 'start body', 'subject': 'start subject', 'type': 'plain', + "extra_info": None, 'results': None, 'builds': [build], "buildset": buildset, @@ -436,6 +447,7 @@ def test_generate_new(self): 'body': 'start body', 'subject': 'start subject', 'type': 'plain', + "extra_info": None, 'results': None, 'builds': [build], "buildset": buildset, @@ -455,6 +467,7 @@ def test_generate_finished(self): 'body': 'end body', 'subject': 'end subject', 'type': 'plain', + "extra_info": None, 'results': SUCCESS, 'builds': [build], "buildset": buildset, diff --git a/master/buildbot/test/unit/reporters/test_generators_buildrequest.py b/master/buildbot/test/unit/reporters/test_generators_buildrequest.py index 6b1fd6862acd..183997d8c117 100644 --- a/master/buildbot/test/unit/reporters/test_generators_buildrequest.py +++ b/master/buildbot/test/unit/reporters/test_generators_buildrequest.py @@ -66,7 +66,8 @@ def setup_generator(self, message=None, **kwargs): message = { "body": "start body", "type": "plain", - "subject": "start subject" + "subject": "start subject", + "extra_info": None, } g = BuildRequestGenerator(**kwargs) @@ -93,6 +94,7 @@ def test_build_message_start_no_result(self): 'body': 'start body', 'subject': 'start subject', 'type': 'plain', + "extra_info": None, 'results': None, 'builds': [build], "buildset": buildset, @@ -138,6 +140,7 @@ def test_generate_new(self): 'body': 'start body', 'subject': 'start subject', 'type': 'plain', + "extra_info": None, 'results': None, 'builds': [build], "buildset": buildset, @@ -162,6 +165,7 @@ def test_generate_cancel(self): 'body': 'start body', 'subject': 'start subject', 'type': 'plain', + "extra_info": None, 'results': CANCELLED, 'builds': [build], "buildset": buildset, diff --git a/master/buildbot/test/unit/reporters/test_generators_buildset.py b/master/buildbot/test/unit/reporters/test_generators_buildset.py index 8acad193689a..8af5b9f4522f 100644 --- a/master/buildbot/test/unit/reporters/test_generators_buildset.py +++ b/master/buildbot/test/unit/reporters/test_generators_buildset.py @@ -61,7 +61,8 @@ def setup_generator( message = { "body": "body", "type": "text", - "subject": "subject" + "subject": "subject", + "extra_info": None, } if db_args is None: db_args = {} @@ -120,6 +121,7 @@ def test_buildset_message_nominal(self): 'body': 'body', 'subject': 'subject', 'type': 'text', + "extra_info": None, 'results': SUCCESS, 'builds': [build], "buildset": buildset, @@ -142,6 +144,7 @@ def test_buildset_message_no_result(self): 'body': 'body', 'subject': 'subject', 'type': 'text', + "extra_info": None, 'results': None, 'builds': [build], "buildset": buildset, @@ -161,6 +164,7 @@ def test_buildset_message_no_result_formatter_no_subject(self): "body": "body", "type": "text", "subject": None, # deprecated unspecified subject + "extra_info": None, } g, build, buildset = yield self.setup_generator( @@ -179,6 +183,7 @@ def test_buildset_message_no_result_formatter_no_subject(self): 'body': 'body', 'subject': 'Buildbot not finished in Buildbot on whole buildset', 'type': 'text', + "extra_info": None, 'results': None, 'builds': [build], "buildset": buildset, @@ -201,6 +206,7 @@ def test_generate_complete(self): 'body': 'body', 'subject': 'subject', 'type': 'text', + "extra_info": None, 'results': SUCCESS, 'builds': [build], "buildset": buildset, @@ -272,6 +278,7 @@ def test_buildset_message_normal(self): "body": "body", "subject": "subject", "type": "text", + "extra_info": None, "results": SUCCESS, "builds": [build], "buildset": buildset, @@ -298,6 +305,7 @@ def test_buildset_message_no_builds(self): "body": "body", "subject": "subject", "type": "text", + "extra_info": None, "results": SUCCESS, "builds": [], "buildset": buildset, @@ -330,6 +338,7 @@ def test_buildset_message_no_result(self): "body": "body", "subject": "subject", "type": "text", + "extra_info": None, "results": None, "builds": [build], "buildset": buildset, @@ -357,6 +366,7 @@ def test_buildset_message_no_builds_no_result(self): "body": "body", "subject": "subject", "type": "text", + "extra_info": None, "results": None, "builds": [], "buildset": buildset, @@ -371,6 +381,7 @@ def test_buildset_message_no_result_formatter_no_subject(self): "body": "body", "type": "text", "subject": None, # deprecated unspecified subject + "extra_info": None, } g, build, buildset = yield self.setup_generator(message=message) @@ -394,6 +405,7 @@ def test_buildset_message_no_result_formatter_no_subject(self): "body": "body", "subject": None, "type": "text", + "extra_info": None, "results": SUCCESS, "builds": [build], "buildset": buildset, @@ -411,6 +423,7 @@ def test_generate_complete(self): "body": "body", "subject": "subject", "type": "text", + "extra_info": None, "results": SUCCESS, "builds": [], "buildset": buildset, @@ -433,6 +446,7 @@ def test_generate_complete_with_builds(self): "body": "body", "subject": "subject", "type": "text", + "extra_info": None, "results": SUCCESS, "builds": [build], "buildset": buildset, diff --git a/master/buildbot/test/unit/reporters/test_generators_utils.py b/master/buildbot/test/unit/reporters/test_generators_utils.py index 0a16c56d99e1..6a3e7a29d799 100644 --- a/master/buildbot/test/unit/reporters/test_generators_utils.py +++ b/master/buildbot/test/unit/reporters/test_generators_utils.py @@ -297,3 +297,24 @@ def test_merge_subject(self, name, old, new, expected_result): def test_merge_body(self, name, old, new, expected_result): g = self.create_generator() self.assertEqual(g._merge_body(old, new), expected_result) + + @parameterized.expand([ + ("both_none", None, None, (None, True)), + ("old_none", None, {"k": "v"}, ({"k": "v"}, True)), + ("new_none", {"k": "v"}, None, ({"k": "v"}, True)), + ( + "both_same_key", + {"k": {"kk1": "vv1"}}, + {"k": {"kk2": "vv2"}}, + ({"k": {"kk1": "vv1", "kk2": "vv2"}}, True) + ), + ( + "both_same_key_conflict", + {"k": {"kk1": "vv1"}}, + {"k": {"kk1": "vv2"}}, + ({"k": {"kk1": "vv1"}}, True) + ), + ]) + def test_merge_info(self, name, old, new, expected_result): + g = self.create_generator() + self.assertEqual(g._merge_extra_info(old, new), expected_result) diff --git a/master/buildbot/test/unit/reporters/test_mail.py b/master/buildbot/test/unit/reporters/test_mail.py index 3383dd1bc951..9c695cec3656 100644 --- a/master/buildbot/test/unit/reporters/test_mail.py +++ b/master/buildbot/test/unit/reporters/test_mail.py @@ -189,7 +189,8 @@ def setupBuildMessage(self, **generator_kwargs): formatter.format_message_for_build.return_value = { "body": "body", "type": "text", - "subject": "subject" + "subject": "subject", + "extra_info": None, } formatter.want_properties = False formatter.want_steps = False @@ -329,7 +330,8 @@ def do_test_sendMessage(self, **mn_kwargs): formatter.format_message_for_build.return_value = { "body": "body", "type": "text", - "subject": "subject" + "subject": "subject", + "extra_info": None, } formatter.want_properties = False formatter.want_steps = False @@ -477,6 +479,7 @@ def create_msgdict(funny_chars='\u00E5\u00E4\u00F6'): msg_dict = { "body": unibody, "subject": "testsubject", - "type": 'plain' + "type": 'plain', + "extra_info": None, } return msg_dict diff --git a/master/buildbot/test/unit/reporters/test_message.py b/master/buildbot/test/unit/reporters/test_message.py index c1041e76a037..01fe934fb218 100644 --- a/master/buildbot/test/unit/reporters/test_message.py +++ b/master/buildbot/test/unit/reporters/test_message.py @@ -226,6 +226,7 @@ def test_message_success_plain_no_steps(self): self.assertEqual(res, { 'type': 'plain', 'subject': '☺ Buildbot (Buildbot): Builder1 - test ((unknown revision))', + "extra_info": None, 'body': textwrap.dedent('''\ A passing build has been detected on builder Builder1 while building Buildbot. @@ -252,6 +253,7 @@ def test_message_success_plain_with_steps(self): self.assertEqual(res, { 'type': 'plain', 'subject': '☺ Buildbot (Buildbot): Builder1 - test (abcd1234)', + "extra_info": None, 'body': textwrap.dedent('''\ A passing build has been detected on builder Builder1 while building Buildbot. @@ -287,6 +289,7 @@ def test_message_success_html(self): self.assertEqual(res, { 'type': 'html', 'subject': '☺ Buildbot (Buildbot): Builder1 - test ((unknown revision))', + "extra_info": None, 'body': textwrap.dedent('''\

A passing build has been detected on builder Builder1 @@ -315,6 +318,7 @@ def test_message_success_html_with_steps(self): self.assertEqual(res, { 'type': 'html', 'subject': '☺ Buildbot (Buildbot): Builder1 - test (abcd1234)', + "extra_info": None, 'body': textwrap.dedent('''\

A passing build has been detected on builder Builder1 @@ -353,18 +357,21 @@ def test_message_success_html_with_steps(self): }) @defer.inlineCallbacks - def test_inline_template(self): - formatter = message.MessageFormatter(template="URL: {{ build_url }} -- {{ summary }}") + def test_inline_templates(self): + formatter = message.MessageFormatter( + template="URL: {{ build_url }} -- {{ summary }}", + subject="subject" + ) res = yield self.do_one_test(formatter, SUCCESS, SUCCESS) - self.assertEqual(res['type'], "plain") - self.assertEqual(res['body'], - "URL: http://localhost:8080/#/builders/80/builds/1 -- Build succeeded!") - - @defer.inlineCallbacks - def test_inline_subject(self): - formatter = message.MessageFormatter(subject="subject") - res = yield self.do_one_test(formatter, SUCCESS, SUCCESS) - self.assertEqual(res['subject'], "subject") + self.assertEqual( + res, + { + "type": "plain", + "subject": "subject", + "extra_info": None, + "body": "URL: http://localhost:8080/#/builders/80/builds/1 -- Build succeeded!" + } + ) @defer.inlineCallbacks def test_message_failure(self): @@ -413,6 +420,7 @@ def test_basic(self): 'body': 'templ_wrkr/because', 'type': 'plain', 'subject': 'subj_wrkr/because', + "extra_info": None, }) @@ -432,6 +440,7 @@ def test_basic(self): 'body': {'key': 'value'}, 'type': 'json', 'subject': None, + "extra_info": None, }) @defer.inlineCallbacks @@ -451,6 +460,7 @@ def test_renderable(self): 'body': {'key': 'value'}, 'type': 'json', 'subject': None, + "extra_info": None, }) diff --git a/master/docs/manual/configuration/report_generators/buildset.rst b/master/docs/manual/configuration/report_generators/buildset.rst index 1a41d6aaa99b..73f50f3fc98c 100644 --- a/master/docs/manual/configuration/report_generators/buildset.rst +++ b/master/docs/manual/configuration/report_generators/buildset.rst @@ -13,6 +13,20 @@ Message formatters are invoked for each matching build in the buildset. The coll then joined and sent as a single message. :bb:reportgen:`BuildStatusGenerator` report generator uses the same message generation logic, but a single, not multiple builds. +In case of multiple builds, the following algorithm is used to build the final message: + + - message body is merged from bodies provided by message formatters for the builds. If message + bodies are lists or strings, then the result is simple concatenation. If the type is different + or there is type mismatch, then mismatching messages are ignored. + + - message subject is taken from the first build for which message formatter a subject. + + - extra information is merged from the information dictionaries provided by message formatters. + Note that extra information is specified as dictionary of dictionaries. Two root dictionaries + are merged by merging child dictionaries. Values in merged child dictionaries that conflict + (i.e. correspond to the same keys) are resolved by taking the value of the first build for + which it is provided. + The following parameters are supported: ``subject`` diff --git a/master/docs/manual/configuration/report_generators/index.rst b/master/docs/manual/configuration/report_generators/index.rst index 6e6cc2d49c23..9295eeb461c6 100644 --- a/master/docs/manual/configuration/report_generators/index.rst +++ b/master/docs/manual/configuration/report_generators/index.rst @@ -55,3 +55,17 @@ The following message formatter classes are provided: * :ref:`MessageFormatterRenderable` (used in ``BuildStatusGenerator`` and ``BuildStartEndStatusGenerator``) * :ref:`MessageFormatterFunction` (used in ``BuildStatusGenerator`` and ``BuildStartEndStatusGenerator``) * :ref:`MessageFormatterMissingWorkers` (used in ``WorkerMissingGenerator``) + +Message formatters produce the following information that is later used by the report generators: + + - Message type: ``plain`` (text), ``html`` or ``json``. + + - Message body: a string that describes the information about build or buildset. Other data types + are supported too, but then the interpretation of data depends on actual reporter that is being + used. + + - Message subject: an optional title of the message about build or buildset. + + - Extra information: optional dictionary of dictionaries with any extra information to give to + the reporter. Interpretation of the data depends on the reporter that is being used. + diff --git a/master/docs/manual/configuration/reporters/reporter_base.rst b/master/docs/manual/configuration/reporters/reporter_base.rst index 8a596b7dbb2c..eaee69683061 100644 --- a/master/docs/manual/configuration/reporters/reporter_base.rst +++ b/master/docs/manual/configuration/reporters/reporter_base.rst @@ -68,3 +68,6 @@ This documents frequently used keys within the dictionaries that are passed to t - ``stepname`` (string) The name of the step that produced the log. - ``content`` (string) The content of the log. + + - ``extra_info`` (a dictionary of dictionaries with string keys in both) + A list of additional reporter-specific data to apply. diff --git a/newsfragments/reporter-extra-info.feature b/newsfragments/reporter-extra-info.feature new file mode 100644 index 000000000000..612270bcb161 --- /dev/null +++ b/newsfragments/reporter-extra-info.feature @@ -0,0 +1 @@ +Added a way to pass additional reporter-specific data to Reporters. From 768bb095666ebb0c7332c7b96c361b98ac024aaf Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:30:01 +0200 Subject: [PATCH 47/68] reporters: Add a way to set extra info to MessageFormatter --- master/buildbot/reporters/message.py | 15 ++++++++++++++- .../test/unit/reporters/test_message.py | 18 ++++++++++++++++++ .../report_generators/formatter.rst | 6 ++++++ newsfragments/reporter-extra-info.feature | 3 ++- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py index 238c1719f46b..b2eb0b745a45 100644 --- a/master/buildbot/reporters/message.py +++ b/master/buildbot/reporters/message.py @@ -419,7 +419,14 @@ class MessageFormatterBaseJinja(MessageFormatterBase): template_type = 'plain' uses_default_body_template = False - def __init__(self, template=None, subject=None, template_type=None, **kwargs): + def __init__( + self, + template=None, + subject=None, + template_type=None, + extra_info_cb=None, + **kwargs + ): if template_type is not None: self.template_type = template_type @@ -441,6 +448,7 @@ def __init__(self, template=None, subject=None, template_type=None, **kwargs): self.body_template = jinja2.Template(template) self.subject_template = jinja2.Template(subject) + self.extra_info_cb = extra_info_cb super().__init__(**kwargs) @@ -462,6 +470,11 @@ def render_message_body(self, context): def render_message_subject(self, context): return self.subject_template.render(context) + def render_message_extra_info(self, context): + if self.extra_info_cb is None: + return None + return self.extra_info_cb(context) + class MessageFormatter(MessageFormatterBaseJinja): @defer.inlineCallbacks diff --git a/master/buildbot/test/unit/reporters/test_message.py b/master/buildbot/test/unit/reporters/test_message.py index 01fe934fb218..f8e4185cd89a 100644 --- a/master/buildbot/test/unit/reporters/test_message.py +++ b/master/buildbot/test/unit/reporters/test_message.py @@ -373,6 +373,24 @@ def test_inline_templates(self): } ) + @defer.inlineCallbacks + def test_inline_templates_extra_info(self): + formatter = message.MessageFormatter( + template="URL: {{ build_url }} -- {{ summary }}", + subject="subject", + extra_info_cb=lambda ctx: {"key1", ctx["build"]["state_string"]} + ) + res = yield self.do_one_test(formatter, SUCCESS, SUCCESS) + self.assertEqual( + res, + { + "type": "plain", + "subject": "subject", + "extra_info": {"key1", "test"}, + "body": "URL: http://localhost:8080/#/builders/80/builds/1 -- Build succeeded!" + } + ) + @defer.inlineCallbacks def test_message_failure(self): formatter = message.MessageFormatter() diff --git a/master/docs/manual/configuration/report_generators/formatter.rst b/master/docs/manual/configuration/report_generators/formatter.rst index 1659f90c4141..1669e489a0f0 100644 --- a/master/docs/manual/configuration/report_generators/formatter.rst +++ b/master/docs/manual/configuration/report_generators/formatter.rst @@ -66,6 +66,12 @@ The constructor of the class takes the following arguments: This implies ``want_logs`` and ``wantSteps`` to be `True`. Use it only when mandatory, as this greatly increases the overhead in terms of CPU and memory on the master. +``extra_info_cb`` + This parameter (defaults to ``None``) can be used to customize extra information that is passed + to reporters. If set, this argument must be a function that returns a dictionary of + dictionaries either directly or via a ``Deferred``. The interpretation of the return value + depends on the exact reporter being used. + Context (build) ~~~~~~~~~~~~~~~ diff --git a/newsfragments/reporter-extra-info.feature b/newsfragments/reporter-extra-info.feature index 612270bcb161..5fad73feb789 100644 --- a/newsfragments/reporter-extra-info.feature +++ b/newsfragments/reporter-extra-info.feature @@ -1 +1,2 @@ -Added a way to pass additional reporter-specific data to Reporters. +Added a way to pass additional reporter-specific data to Reporters. Added ``extra_info_cb`` +argument to ``MessageFormatter`` for this use case. From 12fcc554bdab26251e1c5874c53ed12989a22d47 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:30:04 +0200 Subject: [PATCH 48/68] reporters: Use sendMessage() interface within GerritStatusPush --- master/buildbot/reporters/gerrit.py | 121 +++++++++------- .../test/unit/reporters/test_gerrit.py | 133 ++++++++++-------- 2 files changed, 146 insertions(+), 108 deletions(-) diff --git a/master/buildbot/reporters/gerrit.py b/master/buildbot/reporters/gerrit.py index 49e1e57366c0..9d7416c16803 100644 --- a/master/buildbot/reporters/gerrit.py +++ b/master/buildbot/reporters/gerrit.py @@ -128,6 +128,52 @@ class DEFAULT_SUMMARY: pass +def extract_project_revision(report): + build = report["builds"][0] + + def getProperty(build, name): + return build['properties'].get(name, [None])[0] + # Gerrit + Repo + downloads = getProperty(build, "repo_downloads") + downloaded = getProperty(build, "repo_downloaded") + if downloads is not None and downloaded is not None: + downloaded = downloaded.split(" ") + if downloads and 2 * len(downloads) == len(downloaded): + for i, download in enumerate(downloads): + try: + project, change1 = download.split(" ") + except ValueError: + return None, None # something is wrong, abort + change2 = downloaded[2 * i] + revision = downloaded[2 * i + 1] + if change1 == change2: + return project, revision + else: + return None, None + return None, None + + # Gerrit + Git + # used only to verify Gerrit source + if getProperty(build, "event.change.id") is not None: + project = getProperty(build, "event.change.project") + codebase = getProperty(build, "codebase") + revision = (getProperty(build, "event.patchSet.revision") or + getProperty(build, "got_revision") or + getProperty(build, "revision")) + + if isinstance(revision, dict): + # in case of the revision is a codebase revision, we just take + # the revisionfor current codebase + if codebase is not None: + revision = revision[codebase] + else: + revision = None + + return project, revision + + return None, None + + class GerritStatusPush(service.BuildbotService): """Event streamer to a gerrit ssh server.""" @@ -303,7 +349,7 @@ def buildStarted(self, key, build): yield self.getBuildDetails(build) if self.isBuildReported(build): result = yield self.startCB(build['builder']['name'], build, self.startArg) - self.sendCodeReviews(build, result) + self.send_message_old(build["buildset"], build, result) @defer.inlineCallbacks def buildComplete(self, key, build): @@ -314,7 +360,7 @@ def buildComplete(self, key, build): result = yield self.reviewCB(build['builder']['name'], build, build['results'], self.master, self.reviewArg) result = _handleLegacyResult(result) - self.sendCodeReviews(build, result) + self.send_message_old(build["buildset"], build, result) @defer.inlineCallbacks def getBuildDetails(self, build): @@ -371,60 +417,35 @@ def getBuildInfo(build): self.summaryArg) result = _handleLegacyResult(result) - self.sendCodeReviews(builds[0], result) + self.send_message_old(buildset, builds[0], result) - def sendCodeReviews(self, build, result): - message = result.get('message', None) - if message is None: - return + def send_message_old(self, buildset, build, result): + self.sendMessage( + [ + { + "body": result.get("message", None), + "labels": result.get("labels"), + "builds": [build], + "buildset": buildset, + } + ] + ) - def getProperty(build, name): - return build['properties'].get(name, [None])[0] - # Gerrit + Repo - downloads = getProperty(build, "repo_downloads") - downloaded = getProperty(build, "repo_downloaded") - if downloads is not None and downloaded is not None: - downloaded = downloaded.split(" ") - if downloads and 2 * len(downloads) == len(downloaded): - for i, download in enumerate(downloads): - try: - project, change1 = download.split(" ") - except ValueError: - return # something is wrong, abort - change2 = downloaded[2 * i] - revision = downloaded[2 * i + 1] - if change1 == change2: - self.sendCodeReview(project, revision, result) - else: - return # something is wrong, abort - return + def sendMessage(self, reports): + report = reports[0] - # Gerrit + Git - # used only to verify Gerrit source - if getProperty(build, "event.change.id") is not None: - project = getProperty(build, "event.change.project") - codebase = getProperty(build, "codebase") - revision = (getProperty(build, "event.patchSet.revision") or - getProperty(build, "got_revision") or - getProperty(build, "revision")) - - if isinstance(revision, dict): - # in case of the revision is a codebase revision, we just take - # the revisionfor current codebase - if codebase is not None: - revision = revision[codebase] - else: - revision = None + project, revision = extract_project_revision(report) - if project is not None and revision is not None: - self.sendCodeReview(project, revision, result) - return + if report["body"] is None or project is None or revision is None: + return defer.succeed(None) + + self.send_code_review(project, revision, report["body"], report.get("labels", None)) + return defer.succeed(None) - def sendCodeReview(self, project, revision, result): + def send_code_review(self, project, revision, message, labels): gerrit_version = self.getCachedVersion() if gerrit_version is None: - self.callWithVersion( - lambda: self.sendCodeReview(project, revision, result)) + self.callWithVersion(lambda: self.send_code_review(project, revision, message, labels)) return assert gerrit_version @@ -436,12 +457,10 @@ def sendCodeReview(self, project, revision, result): if self._gerrit_notify is not None: command.append(f'--notify {str(self._gerrit_notify)}') - message = result.get('message', None) if message: message = message.replace("'", "\"") command.append(f"--message '{message}'") - labels = result.get('labels', None) if labels: if gerrit_version < parse_version("2.6"): add_label = _old_add_label diff --git a/master/buildbot/test/unit/reporters/test_gerrit.py b/master/buildbot/test/unit/reporters/test_gerrit.py index e2221b227246..774055aac894 100644 --- a/master/buildbot/test/unit/reporters/test_gerrit.py +++ b/master/buildbot/test/unit/reporters/test_gerrit.py @@ -158,7 +158,7 @@ def setupGerritStatusPushSimple(self, *args, **kwargs): @defer.inlineCallbacks def setupGerritStatusPush(self, *args, **kwargs): gsp = yield self.setupGerritStatusPushSimple(*args, **kwargs) - gsp.sendCodeReview = Mock() + gsp.send_code_review = Mock() return gsp @defer.inlineCallbacks @@ -213,11 +213,12 @@ def check_summary_build_deferred(self, buildResults, finalResult, resultText, msg = yield self.run_fake_summary_build(gsp, buildResults, finalResult, resultText) - result = makeReviewResult(msg, - (GERRIT_LABEL_VERIFIED, verifiedScore)) - gsp.sendCodeReview.assert_called_once_with(self.reporter_test_project, - self.reporter_test_revision, - result) + gsp.send_code_review.assert_called_once_with( + self.reporter_test_project, + self.reporter_test_revision, + msg, + {GERRIT_LABEL_VERIFIED: verifiedScore} + ) @defer.inlineCallbacks def check_summary_build(self, buildResults, finalResult, resultText, @@ -227,11 +228,12 @@ def check_summary_build(self, buildResults, finalResult, resultText, msg = yield self.run_fake_summary_build(gsp, buildResults, finalResult, resultText) - result = makeReviewResult(msg, - (GERRIT_LABEL_VERIFIED, verifiedScore)) - gsp.sendCodeReview.assert_called_once_with(self.reporter_test_project, - self.reporter_test_revision, - result) + gsp.send_code_review.assert_called_once_with( + self.reporter_test_project, + self.reporter_test_revision, + msg, + {GERRIT_LABEL_VERIFIED: verifiedScore} + ) @defer.inlineCallbacks def check_summary_build_legacy(self, buildResults, finalResult, resultText, @@ -241,12 +243,12 @@ def check_summary_build_legacy(self, buildResults, finalResult, resultText, msg = yield self.run_fake_summary_build(gsp, buildResults, finalResult, resultText, expWarning=True) - result = makeReviewResult(msg, - (GERRIT_LABEL_VERIFIED, verifiedScore), - (GERRIT_LABEL_REVIEWED, 0)) - gsp.sendCodeReview.assert_called_once_with(self.reporter_test_project, - self.reporter_test_revision, - result) + gsp.send_code_review.assert_called_once_with( + self.reporter_test_project, + self.reporter_test_revision, + msg, + {GERRIT_LABEL_VERIFIED: verifiedScore, GERRIT_LABEL_REVIEWED: 0} + ) @defer.inlineCallbacks def test_gerrit_ssh_cmd(self): @@ -327,7 +329,8 @@ def test_buildsetComplete_filtered_builder(self): ["failed", "failed"]) self.assertFalse( - gsp.sendCodeReview.called, "sendCodeReview should not be called") + gsp.send_code_review.called, "send_code_review should not be called" + ) @defer.inlineCallbacks def test_buildsetComplete_filtered_matching_builder(self): @@ -337,7 +340,7 @@ def test_buildsetComplete_filtered_matching_builder(self): ["failed", "failed"]) self.assertTrue( - gsp.sendCodeReview.called, "sendCodeReview should be called") + gsp.send_code_review.called, "send_code_review should be called") @defer.inlineCallbacks def run_fake_single_build(self, gsp, buildResult, expWarning=False): @@ -362,13 +365,21 @@ def check_single_build(self, buildResult, verifiedScore): startCB=sampleStartCB) msg = yield self.run_fake_single_build(gsp, buildResult) - start = makeReviewResult(str({'name': self.reporter_test_builder_name}), - (GERRIT_LABEL_REVIEWED, 0)) - result = makeReviewResult(msg, - (GERRIT_LABEL_VERIFIED, verifiedScore)) - calls = [call(self.reporter_test_project, self.reporter_test_revision, start), - call(self.reporter_test_project, self.reporter_test_revision, result)] - gsp.sendCodeReview.assert_has_calls(calls) + calls = [ + call( + self.reporter_test_project, + self.reporter_test_revision, + str({'name': self.reporter_test_builder_name}), + {GERRIT_LABEL_REVIEWED: 0} + ), + call( + self.reporter_test_project, + self.reporter_test_revision, + msg, + {GERRIT_LABEL_VERIFIED: verifiedScore} + ) + ] + gsp.send_code_review.assert_has_calls(calls) # same goes for check_single_build and check_single_build_legacy @defer.inlineCallbacks @@ -378,13 +389,21 @@ def check_single_build_deferred(self, buildResult, verifiedScore): startCB=sampleStartCBDeferred) msg = yield self.run_fake_single_build(gsp, buildResult) - start = makeReviewResult(str({'name': self.reporter_test_builder_name}), - (GERRIT_LABEL_REVIEWED, 0)) - result = makeReviewResult(msg, - (GERRIT_LABEL_VERIFIED, verifiedScore)) - calls = [call(self.reporter_test_project, self.reporter_test_revision, start), - call(self.reporter_test_project, self.reporter_test_revision, result)] - gsp.sendCodeReview.assert_has_calls(calls) + calls = [ + call( + self.reporter_test_project, + self.reporter_test_revision, + str({'name': self.reporter_test_builder_name}), + {GERRIT_LABEL_REVIEWED: 0} + ), + call( + self.reporter_test_project, + self.reporter_test_revision, + msg, + {GERRIT_LABEL_VERIFIED: verifiedScore} + ) + ] + gsp.send_code_review.assert_has_calls(calls) @defer.inlineCallbacks def check_single_build_legacy(self, buildResult, verifiedScore): @@ -392,15 +411,21 @@ def check_single_build_legacy(self, buildResult, verifiedScore): startCB=sampleStartCB) msg = yield self.run_fake_single_build(gsp, buildResult, expWarning=True) - - start = makeReviewResult(str({'name': self.reporter_test_builder_name}), - (GERRIT_LABEL_REVIEWED, 0)) - result = makeReviewResult(msg, - (GERRIT_LABEL_VERIFIED, verifiedScore), - (GERRIT_LABEL_REVIEWED, 0)) - calls = [call(self.reporter_test_project, self.reporter_test_revision, start), - call(self.reporter_test_project, self.reporter_test_revision, result)] - gsp.sendCodeReview.assert_has_calls(calls) + calls = [ + call( + self.reporter_test_project, + self.reporter_test_revision, + str({'name': self.reporter_test_builder_name}), + {GERRIT_LABEL_REVIEWED: 0} + ), + call( + self.reporter_test_project, + self.reporter_test_revision, + msg, + {GERRIT_LABEL_VERIFIED: verifiedScore, GERRIT_LABEL_REVIEWED: 0} + ) + ] + gsp.send_code_review.assert_has_calls(calls) def test_buildComplete_success_sends_review(self): return self.check_single_build(SUCCESS, 1) @@ -424,12 +449,12 @@ def test_single_build_filtered(self): gsp.builders = ["Builder0"] yield self.run_fake_single_build(gsp, SUCCESS) self.assertTrue( - gsp.sendCodeReview.called, "sendCodeReview should be called") - gsp.sendCodeReview = Mock() + gsp.send_code_review.called, "send_code_review should be called") + gsp.send_code_review = Mock() gsp.builders = ["foo"] yield self.run_fake_single_build(gsp, SUCCESS) self.assertFalse( - gsp.sendCodeReview.called, "sendCodeReview should not be called") + gsp.send_code_review.called, "send_code_review should not be called") def test_defaultReviewCBSuccess(self): res = defaultReviewCB("builderName", {}, SUCCESS, None, None) @@ -452,16 +477,14 @@ def testBuildGerritCommand(self): gsp = yield self.setupGerritStatusPushSimple() spawnSkipFirstArg = Mock() gsp.spawnProcess = lambda _, *a, **k: spawnSkipFirstArg(*a, **k) - yield gsp.sendCodeReview("project", "revision", - {"message": "bla", "labels": {'Verified': 1}}) + yield gsp.send_code_review("project", "revision", "bla", {'Verified': 1}) spawnSkipFirstArg.assert_called_once_with( 'ssh', ['ssh', '-o', 'BatchMode=yes', 'user@serv', '-p', '29418', 'gerrit', 'version'], env=None) gsp.processVersion(parse_version("2.6"), lambda: None) spawnSkipFirstArg = Mock() - yield gsp.sendCodeReview("project", "revision", - {"message": "bla", "labels": {'Verified': 1}}) + yield gsp.send_code_review("project", "revision", "bla", {'Verified': 1}) spawnSkipFirstArg.assert_called_once_with( 'ssh', ['ssh', '-o', 'BatchMode=yes', 'user@serv', '-p', '29418', 'gerrit', 'review', @@ -471,8 +494,7 @@ def testBuildGerritCommand(self): # <=2.5 uses other syntax gsp.processVersion(parse_version("2.4"), lambda: None) spawnSkipFirstArg = Mock() - yield gsp.sendCodeReview("project", "revision", - {"message": "bla", "labels": {'Verified': 1}}) + yield gsp.send_code_review("project", "revision", "bla", {'Verified': 1}) spawnSkipFirstArg.assert_called_once_with( 'ssh', ['ssh', '-o', 'BatchMode=yes', 'user@serv', '-p', '29418', 'gerrit', 'review', @@ -484,8 +506,7 @@ def testBuildGerritCommand(self): gsp._gerrit_notify = 'OWNER' gsp.processVersion(parse_version('2.6'), lambda: None) spawnSkipFirstArg = Mock() - yield gsp.sendCodeReview('project', 'revision', - {'message': 'bla', 'labels': {'Verified': 1}}) + yield gsp.send_code_review('project', 'revision', "bla", {'Verified': 1}) spawnSkipFirstArg.assert_called_once_with( 'ssh', ['ssh', '-o', 'BatchMode=yes', 'user@serv', '-p', '29418', 'gerrit', 'review', @@ -496,8 +517,7 @@ def testBuildGerritCommand(self): # gerrit versions <= 2.5 uses other syntax gsp.processVersion(parse_version('2.4'), lambda: None) spawnSkipFirstArg = Mock() - yield gsp.sendCodeReview('project', 'revision', - {'message': 'bla', 'labels': {'Verified': 1}}) + yield gsp.send_code_review('project', 'revision', "bla", {'Verified': 1}) spawnSkipFirstArg.assert_called_once_with( 'ssh', ['ssh', '-o', 'BatchMode=yes', 'user@serv', '-p', '29418', 'gerrit', 'review', @@ -506,8 +526,7 @@ def testBuildGerritCommand(self): gsp.processVersion(parse_version("2.13"), lambda: None) spawnSkipFirstArg = Mock() - yield gsp.sendCodeReview("project", "revision", - {"message": "bla", "labels": {'Verified': 1}}) + yield gsp.send_code_review("project", "revision", "bla", {'Verified': 1}) spawnSkipFirstArg.assert_called_once_with( 'ssh', ['ssh', '-o', 'BatchMode=yes', 'user@serv', '-p', '29418', 'gerrit', 'review', From a2f361824e1aa376a0ccf24f4c896485540f8f8d Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:30:05 +0200 Subject: [PATCH 49/68] reporters: Refactor GerritStatusPush to reuse ReporterBase --- master/buildbot/reporters/gerrit.py | 456 ++++++++++++------ .../integration/test_setup_entrypoints.py | 4 + .../test/unit/reporters/test_gerrit.py | 49 +- 3 files changed, 322 insertions(+), 187 deletions(-) diff --git a/master/buildbot/reporters/gerrit.py b/master/buildbot/reporters/gerrit.py index 9d7416c16803..9e606d840e43 100644 --- a/master/buildbot/reporters/gerrit.py +++ b/master/buildbot/reporters/gerrit.py @@ -25,7 +25,9 @@ from twisted.internet import reactor from twisted.internet.protocol import ProcessProtocol from twisted.python import log +from zope.interface import implementer +from buildbot import interfaces from buildbot.process.results import EXCEPTION from buildbot.process.results import FAILURE from buildbot.process.results import RETRY @@ -33,8 +35,8 @@ from buildbot.process.results import WARNINGS from buildbot.process.results import Results from buildbot.reporters import utils +from buildbot.reporters.base import ReporterBase from buildbot.util import bytes2unicode -from buildbot.util import service # Cache the version that the gerrit server is running for this many seconds GERRIT_VERSION_CACHE_TIMEOUT = 600 @@ -174,7 +176,171 @@ def getProperty(build, name): return None, None -class GerritStatusPush(service.BuildbotService): +class GerritStatusGeneratorBase: + + def __init__(self, callback, callback_arg, builders, want_steps, want_logs): + self.callback = callback + self.callback_arg = callback_arg + self.builders = builders + self.want_steps = want_steps + self.want_logs = want_logs + + def is_build_reported(self, build): + return self.builders is None or build["builder"]["name"] in self.builders + + @defer.inlineCallbacks + def get_build_details(self, master, build): + br = yield master.data.get(("buildrequests", build["buildrequestid"])) + buildset = yield master.data.get(("buildsets", br["buildsetid"])) + yield utils.getDetailsForBuilds( + master, + buildset, + [build], + want_properties=True, + want_steps=self.want_steps + ) + + +@implementer(interfaces.IReportGenerator) +class GerritBuildSetStatusGenerator(GerritStatusGeneratorBase): + wanted_event_keys = [ + ("buildsets", None, "complete"), + ] + + def check(self): + pass + + @defer.inlineCallbacks + def generate(self, master, reporter, key, message): + bsid = message["bsid"] + res = yield utils.getDetailsForBuildset( + master, + bsid, + want_properties=True, + want_steps=self.want_steps, + want_logs=self.want_logs, + want_logs_content=self.want_logs + ) + + builds = res["builds"] + buildset = res["buildset"] + + builds = [build for build in builds if self.is_build_reported(build)] + if not builds: + return None + + def get_build_info(build): + result = build["results"] + resultText = { + SUCCESS: "succeeded", + FAILURE: "failed", + WARNINGS: "completed with warnings", + EXCEPTION: "encountered an exception", + }.get(result, f"completed with unknown result {result}") + + return { + "name": build["builder"]["name"], + "result": result, + "resultText": resultText, + "text": build["state_string"], + "url": utils.getURLForBuild( + master, + build["builder"]["builderid"], + build["number"] + ), + "build": build + } + + build_info_list = sorted( + [get_build_info(build) for build in builds], key=lambda bi: bi["name"] + ) + + result = yield self.callback( + build_info_list, + Results[buildset["results"]], + master, + self.callback_arg + ) + + result = _handleLegacyResult(result) + + return { + "body": result.get("message", None), + "extra_info": { + "labels": result.get("labels"), + }, + "builds": [builds[0]], + "buildset": buildset, + } + + +@implementer(interfaces.IReportGenerator) +class GerritBuildStartStatusGenerator(GerritStatusGeneratorBase): + wanted_event_keys = [ + ("builds", None, "new"), + ] + + def check(self): + pass + + @defer.inlineCallbacks + def generate(self, master, reporter, key, message): + build = message + yield self.get_build_details(master, build) + if not self.is_build_reported(build): + return None + + result = yield self.callback(build["builder"]["name"], build, self.callback_arg) + + result = _handleLegacyResult(result) + + return { + "body": result.get("message", None), + "extra_info": { + "labels": result.get("labels"), + }, + "builds": [build], + "buildset": build["buildset"], + } + + +@implementer(interfaces.IReportGenerator) +class GerritBuildEndStatusGenerator(GerritStatusGeneratorBase): + wanted_event_keys = [ + ('builds', None, 'finished'), + ] + + def check(self): + pass + + @defer.inlineCallbacks + def generate(self, master, reporter, key, message): + build = message + yield self.get_build_details(master, build) + if not self.is_build_reported(build): + return None + + result = yield self.callback( + build['builder']['name'], + build, + build['results'], + master, + self.callback_arg + ) + + result = _handleLegacyResult(result) + + return { + "body": result.get("message", None), + "extra_info": { + "labels": result.get("labels"), + }, + "builds": [build], + "buildset": build["buildset"], + } + + +class GerritStatusPush(ReporterBase): """Event streamer to a gerrit ssh server.""" name = "GerritStatusPush" @@ -184,22 +350,96 @@ class GerritStatusPush(service.BuildbotService): gerrit_version_time = None gerrit_version = None gerrit_identity_file = None - reviewCB = None - reviewArg = None - startCB = None - startArg = None - summaryCB = None - summaryArg = None - wantSteps = False - wantLogs = False _gerrit_notify = None - def reconfigService(self, server, username, reviewCB=DEFAULT_REVIEW, - startCB=None, port=29418, reviewArg=None, - startArg=None, summaryCB=DEFAULT_SUMMARY, summaryArg=None, - identity_file=None, builders=None, notify=None, - wantSteps=False, wantLogs=False): + def checkConfig( + self, + server, + username, + reviewCB=DEFAULT_REVIEW, + startCB=None, + port=29418, + reviewArg=None, + startArg=None, + summaryCB=DEFAULT_SUMMARY, + summaryArg=None, + identity_file=None, + builders=None, + notify=None, + wantSteps=False, + wantLogs=False, + generators=None, + **kwargs + ): + if generators is None: + generators = self._create_generators_from_old_args( + reviewCB, + startCB, + reviewArg, + startArg, + summaryCB, + summaryArg, + builders, + wantSteps, + wantLogs + ) + + super().checkConfig(generators=generators, **kwargs) + + def reconfigService( + self, + server, + username, + reviewCB=DEFAULT_REVIEW, + startCB=None, + port=29418, + reviewArg=None, + startArg=None, + summaryCB=DEFAULT_SUMMARY, + summaryArg=None, + identity_file=None, + builders=None, + notify=None, + wantSteps=False, + wantLogs=False, + generators=None, + **kwargs + ): + self.gerrit_server = server + self.gerrit_username = username + self.gerrit_port = port + self.gerrit_version = None + self.gerrit_version_time = 0 + self.gerrit_identity_file = identity_file + self._gerrit_notify = notify + if generators is None: + generators = self._create_generators_from_old_args( + reviewCB, + startCB, + reviewArg, + startArg, + summaryCB, + summaryArg, + builders, + wantSteps, + wantLogs + ) + + super().reconfigService(generators=generators, **kwargs) + + def _create_generators_from_old_args( + self, + reviewCB, + startCB, + reviewArg, + startArg, + summaryCB, + summaryArg, + builders, + wantSteps, + wantLogs + ): # If neither reviewCB nor summaryCB were specified, default to sending # out "summary" reviews. But if we were given a reviewCB and only a # reviewCB, disable the "summary" reviews, so we don't send out both @@ -211,23 +451,43 @@ def reconfigService(self, server, username, reviewCB=DEFAULT_REVIEW, reviewCB = None if summaryCB is DEFAULT_SUMMARY: summaryCB = None - # Parameters. - self.gerrit_server = server - self.gerrit_username = username - self.gerrit_port = port - self.gerrit_version = None - self.gerrit_version_time = 0 - self.gerrit_identity_file = identity_file - self.reviewCB = reviewCB - self.reviewArg = reviewArg - self.startCB = startCB - self.startArg = startArg - self.summaryCB = summaryCB - self.summaryArg = summaryArg - self.builders = builders - self._gerrit_notify = notify - self.wantSteps = wantSteps - self.wantLogs = wantLogs + + generators = [] + + if startCB is not None: + generators.append( + GerritBuildStartStatusGenerator( + callback=startCB, + callback_arg=startArg, + builders=builders, + want_steps=wantSteps, + want_logs=wantLogs + ) + ) + + if reviewCB is not None: + generators.append( + GerritBuildEndStatusGenerator( + callback=reviewCB, + callback_arg=reviewArg, + builders=builders, + want_steps=wantSteps, + want_logs=wantLogs + ) + ) + + if summaryCB is not None: + generators.append( + GerritBuildSetStatusGenerator( + callback=summaryCB, + callback_arg=summaryArg, + builders=builders, + want_steps=wantSteps, + want_logs=wantLogs + ) + ) + + return generators def _gerritCmd(self, *args): '''Construct a command as a list of strings suitable for @@ -306,131 +566,6 @@ def processEnded(self, reason): else: log.msg("gerrit status: OK") - @defer.inlineCallbacks - def startService(self): - yield super().startService() - startConsuming = self.master.mq.startConsuming - self._buildsetCompleteConsumer = yield startConsuming( - self.buildsetComplete, - ('buildsets', None, 'complete')) - - self._buildCompleteConsumer = yield startConsuming( - self.buildComplete, - ('builds', None, 'finished')) - - self._buildStartedConsumer = yield startConsuming( - self.buildStarted, - ('builds', None, 'new')) - - def stopService(self): - self._buildsetCompleteConsumer.stopConsuming() - self._buildCompleteConsumer.stopConsuming() - self._buildStartedConsumer.stopConsuming() - - @defer.inlineCallbacks - def _got_event(self, key, msg): - # This function is used only from tests - if key[0] == 'builds': - if key[2] == 'new': - yield self.buildStarted(key, msg) - return - elif key[2] == 'finished': - yield self.buildComplete(key, msg) - return - if key[0] == 'buildsets' and key[2] == 'complete': # pragma: no cover - yield self.buildsetComplete(key, msg) - return - raise RuntimeError(f'Invalid key for _got_event: {key}') # pragma: no cover - - @defer.inlineCallbacks - def buildStarted(self, key, build): - if self.startCB is None: - return - yield self.getBuildDetails(build) - if self.isBuildReported(build): - result = yield self.startCB(build['builder']['name'], build, self.startArg) - self.send_message_old(build["buildset"], build, result) - - @defer.inlineCallbacks - def buildComplete(self, key, build): - if self.reviewCB is None: - return - yield self.getBuildDetails(build) - if self.isBuildReported(build): - result = yield self.reviewCB(build['builder']['name'], build, build['results'], - self.master, self.reviewArg) - result = _handleLegacyResult(result) - self.send_message_old(build["buildset"], build, result) - - @defer.inlineCallbacks - def getBuildDetails(self, build): - br = yield self.master.data.get(("buildrequests", build['buildrequestid'])) - buildset = yield self.master.data.get(("buildsets", br['buildsetid'])) - yield utils.getDetailsForBuilds(self.master, - buildset, - [build], - want_properties=True, - want_steps=self.wantSteps) - - def isBuildReported(self, build): - return self.builders is None or build['builder']['name'] in self.builders - - @defer.inlineCallbacks - def buildsetComplete(self, key, msg): - if not self.summaryCB: - return - bsid = msg['bsid'] - res = yield utils.getDetailsForBuildset(self.master, bsid, want_properties=True, - want_steps=self.wantSteps, want_logs=self.wantLogs, - want_logs_content=self.wantLogs) - builds = res['builds'] - buildset = res['buildset'] - self.sendBuildSetSummary(buildset, builds) - - @defer.inlineCallbacks - def sendBuildSetSummary(self, buildset, builds): - builds = [build for build in builds if self.isBuildReported(build)] - if builds and self.summaryCB: - def getBuildInfo(build): - result = build['results'] - resultText = { - SUCCESS: "succeeded", - FAILURE: "failed", - WARNINGS: "completed with warnings", - EXCEPTION: "encountered an exception", - }.get(result, f"completed with unknown result {result}") - - return {'name': build['builder']['name'], - 'result': result, - 'resultText': resultText, - 'text': build['state_string'], - 'url': utils.getURLForBuild(self.master, build['builder']['builderid'], - build['number']), - 'build': build - } - buildInfoList = sorted( - [getBuildInfo(build) for build in builds], key=lambda bi: bi['name']) - - result = yield self.summaryCB(buildInfoList, - Results[buildset['results']], - self.master, - self.summaryArg) - - result = _handleLegacyResult(result) - self.send_message_old(buildset, builds[0], result) - - def send_message_old(self, buildset, build, result): - self.sendMessage( - [ - { - "body": result.get("message", None), - "labels": result.get("labels"), - "builds": [build], - "buildset": buildset, - } - ] - ) - def sendMessage(self, reports): report = reports[0] @@ -439,7 +574,12 @@ def sendMessage(self, reports): if report["body"] is None or project is None or revision is None: return defer.succeed(None) - self.send_code_review(project, revision, report["body"], report.get("labels", None)) + labels = None + extra_info = report.get("extra_info", None) + if extra_info is not None: + labels = extra_info.get("labels", None) + + self.send_code_review(project, revision, report["body"], labels) return defer.succeed(None) def send_code_review(self, project, revision, message, labels): diff --git a/master/buildbot/test/integration/test_setup_entrypoints.py b/master/buildbot/test/integration/test_setup_entrypoints.py index d56a9bdb3a11..b31afd130098 100644 --- a/master/buildbot/test/integration/test_setup_entrypoints.py +++ b/master/buildbot/test/integration/test_setup_entrypoints.py @@ -165,6 +165,10 @@ def test_reporters(self): 'buildbot.reporters.generators.utils.BuildStatusGeneratorMixin', 'buildbot.reporters.gerrit.DEFAULT_REVIEW', 'buildbot.reporters.gerrit.DEFAULT_SUMMARY', + 'buildbot.reporters.gerrit.GerritBuildEndStatusGenerator', + 'buildbot.reporters.gerrit.GerritBuildSetStatusGenerator', + 'buildbot.reporters.gerrit.GerritBuildStartStatusGenerator', + 'buildbot.reporters.gerrit.GerritStatusGeneratorBase', 'buildbot.reporters.irc.IRCChannel', 'buildbot.reporters.irc.IRCContact', 'buildbot.reporters.irc.IrcStatusBot', diff --git a/master/buildbot/test/unit/reporters/test_gerrit.py b/master/buildbot/test/unit/reporters/test_gerrit.py index 774055aac894..1c52a8b2f7f3 100644 --- a/master/buildbot/test/unit/reporters/test_gerrit.py +++ b/master/buildbot/test/unit/reporters/test_gerrit.py @@ -18,6 +18,7 @@ from unittest.mock import call from packaging.version import parse as parse_version +from parameterized import parameterized from twisted.internet import defer from twisted.internet import error @@ -190,8 +191,7 @@ def makeBuildInfo(self, buildResults, resultText, builds): def run_fake_summary_build(self, gsp, buildResults, finalResult, resultText, expWarning=False): buildset, builds = yield self.setupBuildResults(buildResults, finalResult) - yield gsp.buildsetComplete('buildset.98.complete'.split("."), - buildset) + yield gsp._got_event(("buildsets", 98, "complete"), buildset) info = self.makeBuildInfo(buildResults, resultText, builds) if expWarning: @@ -321,26 +321,17 @@ def test_buildsetComplete_mixed_sends_summary_review_legacy(self): verifiedScore=-1) return d + @parameterized.expand([ + ("matched", ["Builder1"], True), + ("not_matched", ["foo"], False), + ]) @defer.inlineCallbacks - def test_buildsetComplete_filtered_builder(self): - gsp = yield self.setupGerritStatusPush(summaryCB=sampleSummaryCB) - gsp.builders = ["foo"] + def test_buildset_complete_filtered_builder(self, name, builders, should_call): + gsp = yield self.setupGerritStatusPush(summaryCB=sampleSummaryCB, builders=builders) yield self.run_fake_summary_build(gsp, [FAILURE, FAILURE], FAILURE, ["failed", "failed"]) - self.assertFalse( - gsp.send_code_review.called, "send_code_review should not be called" - ) - - @defer.inlineCallbacks - def test_buildsetComplete_filtered_matching_builder(self): - gsp = yield self.setupGerritStatusPush(summaryCB=sampleSummaryCB) - gsp.builders = ["Builder1"] - yield self.run_fake_summary_build(gsp, [FAILURE, FAILURE], FAILURE, - ["failed", "failed"]) - - self.assertTrue( - gsp.send_code_review.called, "send_code_review should be called") + self.assertEqual(gsp.send_code_review.called, should_call) @defer.inlineCallbacks def run_fake_single_build(self, gsp, buildResult, expWarning=False): @@ -440,21 +431,21 @@ def test_buildComplete_failure_sends_review_legacy(self): return self.check_single_build_legacy(FAILURE, -1) # same goes for check_single_build and check_single_build_legacy + @parameterized.expand([ + ("matched", ["Builder0"], True), + ("not_matched", ["foo"], False), + ]) @defer.inlineCallbacks - def test_single_build_filtered(self): + def test_single_build_filtered(self, name, builders, should_call): - gsp = yield self.setupGerritStatusPush(reviewCB=sampleReviewCB, - startCB=sampleStartCB) + gsp = yield self.setupGerritStatusPush( + reviewCB=sampleReviewCB, + startCB=sampleStartCB, + builders=builders + ) - gsp.builders = ["Builder0"] - yield self.run_fake_single_build(gsp, SUCCESS) - self.assertTrue( - gsp.send_code_review.called, "send_code_review should be called") - gsp.send_code_review = Mock() - gsp.builders = ["foo"] yield self.run_fake_single_build(gsp, SUCCESS) - self.assertFalse( - gsp.send_code_review.called, "send_code_review should not be called") + self.assertEqual(gsp.send_code_review.called, should_call) def test_defaultReviewCBSuccess(self): res = defaultReviewCB("builderName", {}, SUCCESS, None, None) From 21052470b3fc5f600754359cda2daab38965a3a9 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:30:02 +0200 Subject: [PATCH 50/68] reporters: Implement MessageFormatterFunctionRaw --- master/buildbot/reporters/message.py | 37 ++++++++++ .../test/unit/reporters/test_message.py | 70 +++++++++++++++++++ .../report_generators/formatter_function.rst | 4 +- .../formatter_function_raw.rst | 42 +++++++++++ .../configuration/report_generators/index.rst | 21 ++++-- master/setup.py | 1 + .../message-formatter-function-raw.rst | 1 + 7 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 master/docs/manual/configuration/report_generators/formatter_function_raw.rst create mode 100644 newsfragments/message-formatter-function-raw.rst diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py index b2eb0b745a45..eca54014df65 100644 --- a/master/buildbot/reporters/message.py +++ b/master/buildbot/reporters/message.py @@ -293,6 +293,43 @@ def format_message_for_buildset(self, master, buildset, builds, **kwargs): } +class MessageFormatterFunctionRaw(MessageFormatterBase): + + def __init__(self, function, **kwargs): + super().__init__(**kwargs) + self._function = function + + @defer.inlineCallbacks + def format_message_for_build(self, master, build, is_buildset=False, users=None, mode=None): + ctx = create_context_for_build(mode, build, is_buildset, master, users) + msgdict = yield self._function(master, ctx) + return { + "body": msgdict.get("body", None), + "type": msgdict.get("type", "plain"), + "subject": msgdict.get("subject", None), + "extra_info": msgdict.get("extra_info", None), + } + + @defer.inlineCallbacks + def format_message_for_buildset( + self, + master, + buildset, + builds, + users=None, + mode=None, + **kwargs + ): + ctx = create_context_for_buildset(mode, buildset, builds, master, users) + msgdict = yield self._function(master, ctx) + return { + "body": msgdict.get("body", None), + "type": msgdict.get("type", "plain"), + "subject": msgdict.get("subject", None), + "extra_info": msgdict.get("extra_info", None), + } + + class MessageFormatterFunction(MessageFormatterBase): def __init__(self, function, template_type, **kwargs): diff --git a/master/buildbot/test/unit/reporters/test_message.py b/master/buildbot/test/unit/reporters/test_message.py index f8e4185cd89a..c7303450ad36 100644 --- a/master/buildbot/test/unit/reporters/test_message.py +++ b/master/buildbot/test/unit/reporters/test_message.py @@ -196,6 +196,38 @@ def do_one_test(self, formatter, lastresults, results, mode="all", users=["him@bar", "me@foo"]) return res + @defer.inlineCallbacks + def do_one_test_buildset( + self, + formatter, + lastresults, + results, + mode="all", + with_steps=False, + extra_build_properties=None + ): + self.setup_db( + lastresults, + results, + with_steps=with_steps, + extra_build_properties=extra_build_properties + ) + + res = yield utils.getDetailsForBuildset( + self.master, + 99, + want_properties=formatter.want_properties, + want_steps=formatter.want_steps, + want_previous_build=True, + want_logs=formatter.want_logs, + want_logs_content=formatter.want_logs_content + ) + + res = yield formatter.format_message_for_buildset( + self.master, res["buildset"], res["builds"], mode=mode, users=["him@bar", "me@foo"] + ) + return res + class TestMessageFormatter(MessageFormatterTestBase): @@ -482,6 +514,44 @@ def test_renderable(self): }) +class TestMessageFormatterFunctionRaw(MessageFormatterTestBase): + @defer.inlineCallbacks + def test_basic(self): + function = mock.Mock(side_effect=lambda master, ctx: { + "body": {"key": "value"}, + "type": "json", + "subject": "sub1", + "extra_info": {"key": {"kk": "vv"}}, + }) + formatter = message.MessageFormatterFunctionRaw(function) + res = yield self.do_one_test(formatter, SUCCESS, SUCCESS) + + self.assertEqual(res, { + "body": {"key": "value"}, + "type": "json", + "subject": "sub1", + "extra_info": {"key": {"kk": "vv"}}, + }) + + @defer.inlineCallbacks + def test_basic_buildset(self): + function = mock.Mock(side_effect=lambda master, ctx: { + "body": {"key": "value"}, + "type": "json", + "subject": "sub1", + "extra_info": {"key": {"kk": "vv"}}, + }) + formatter = message.MessageFormatterFunctionRaw(function) + res = yield self.do_one_test_buildset(formatter, SUCCESS, SUCCESS) + + self.assertEqual(res, { + "body": {"key": "value"}, + "type": "json", + "subject": "sub1", + "extra_info": {"key": {"kk": "vv"}}, + }) + + class TestMessageFormatterMissingWorker(MessageFormatterTestBase): @defer.inlineCallbacks def test_basic(self): diff --git a/master/docs/manual/configuration/report_generators/formatter_function.rst b/master/docs/manual/configuration/report_generators/formatter_function.rst index 851f70cbc6de..2dcf37eabe38 100644 --- a/master/docs/manual/configuration/report_generators/formatter_function.rst +++ b/master/docs/manual/configuration/report_generators/formatter_function.rst @@ -5,8 +5,10 @@ MessageFormatterFunction .. py:currentmodule:: buildbot.reporters.message -This formatter can be used to generate arbitrary messages according to arbitrary calculations. +This formatter can be used to generate arbitrary messages bodies according to arbitrary calculations. + As opposed to :ref:`MessageFormatterRenderable`, more information is made available to this reporter. +As opposed to :ref:`MessageFormatterFunctionRaw`, only the message body can be customized. .. py:class:: MessageFormatterFunction(function, template_type, want_properties=True, wantProperties=None, want_steps=False, wantSteps=None, wantLogs=None, want_logs=False, want_logs_content=False) diff --git a/master/docs/manual/configuration/report_generators/formatter_function_raw.rst b/master/docs/manual/configuration/report_generators/formatter_function_raw.rst new file mode 100644 index 000000000000..e121ac44bda1 --- /dev/null +++ b/master/docs/manual/configuration/report_generators/formatter_function_raw.rst @@ -0,0 +1,42 @@ +.. _MessageFormatterFunctionRaw: + +MessageFormatterFunctionRaw ++++++++++++++++++++++++++++ + +.. py:currentmodule:: buildbot.reporters.message + +This formatter can be used to generate arbitrary messages according to arbitrary calculations. + +As opposed to :ref:`MessageFormatterFunction`, full message information can be customized. + +The return value of the provided function must be a dictionary and is interpreted as follows: + + - ``body``. Body of the message. Most reporters require this to be a string. If not provided, + ``None`` is used. + + - ``type``. Type of the message. Must be either ``plain``, ``html`` or ``json``. If not provided, + ``"plain"`` is used. + + - ``subject``. Subject of the message. Must be a string. If not provided, ``None`` is used. + + - ``extra_info``. Extra information of the message. Must be either ``None`` or a dictionary + of dictionaries with string keys in both root and child dictionaries. If not provided, ``None`` + is used. + +.. py:class:: MessageFormatterFunctionRaw(function, want_properties=True, want_steps=False, want_logs=False, want_logs_content=False) + + :param callable function: A callable that will be called with a two arguments. + + - ``master``: An instance of ``BuildMaster`` + + - ``ctx``: dictionary that contains the same context dictionary as :ref:`MessageFormatter`. + + :param boolean want_properties: include 'properties' in the build dictionary + :param boolean want_steps: include 'steps' in the build dictionary + :param boolean want_logs: include 'logs' in the steps dictionaries. + This implies `want_steps=True`. + This includes only log metadata, for content use ``want_logs_content``. + :param boolean want_logs_content: include logs content in the logs dictionaries. + This implies `want_logs=True` and `want_steps=True`. + This dumps the *full* content of logs and may consume lots of memory and CPU depending on + the log size. diff --git a/master/docs/manual/configuration/report_generators/index.rst b/master/docs/manual/configuration/report_generators/index.rst index 9295eeb461c6..96bf0b3da0b5 100644 --- a/master/docs/manual/configuration/report_generators/index.rst +++ b/master/docs/manual/configuration/report_generators/index.rst @@ -14,6 +14,7 @@ Report Generators worker formatter formatter_function + formatter_function_raw formatter_renderable formatter_missing_worker @@ -50,11 +51,21 @@ The following report generators are available: The report generators may customize the reports using message formatters. The following message formatter classes are provided: - * :ref:`MessageFormatter` (used in ``BuildStatusGenerator``, ``BuildStartEndStatusGenerator``, - ``BuildSetCombinedStatusGenerator`` and ``BuildSetStatusGenerator``) - * :ref:`MessageFormatterRenderable` (used in ``BuildStatusGenerator`` and ``BuildStartEndStatusGenerator``) - * :ref:`MessageFormatterFunction` (used in ``BuildStatusGenerator`` and ``BuildStartEndStatusGenerator``) - * :ref:`MessageFormatterMissingWorkers` (used in ``WorkerMissingGenerator``) + * :ref:`MessageFormatter` (commonly used in + ``BuildStatusGenerator``, + ``BuildStartEndStatusGenerator``, + ``BuildSetCombinedStatusGenerator`` and + ``BuildSetStatusGenerator``) + * :ref:`MessageFormatterRenderable` (commonly used in ``BuildStatusGenerator`` and + ``BuildStartEndStatusGenerator``) + * :ref:`MessageFormatterFunction` (commonly used in ``BuildStatusGenerator`` and + ``BuildStartEndStatusGenerator``) + * :ref:`MessageFormatterFunctionRaw` (commonly used in + ``BuildStatusGenerator``, + ``BuildStartEndStatusGenerator``, + ``BuildSetCombinedStatusGenerator`` and + ``BuildSetStatusGenerator``) + * :ref:`MessageFormatterMissingWorkers` (commonly used in ``WorkerMissingGenerator``) Message formatters produce the following information that is later used by the report generators: diff --git a/master/setup.py b/master/setup.py index 8f1944850ad6..df8ba36aac0a 100755 --- a/master/setup.py +++ b/master/setup.py @@ -348,6 +348,7 @@ def define_plugin_entries(groups): 'MessageFormatter', 'MessageFormatterEmpty', 'MessageFormatterFunction', + "MessageFormatterFunctionRaw", 'MessageFormatterMissingWorker', 'MessageFormatterRenderable', ]), diff --git a/newsfragments/message-formatter-function-raw.rst b/newsfragments/message-formatter-function-raw.rst new file mode 100644 index 000000000000..d34f10dc792a --- /dev/null +++ b/newsfragments/message-formatter-function-raw.rst @@ -0,0 +1 @@ +Added ``MessageFormatterFunctionRaw`` which allows complete customization of messages to be emitted. From 6ae6a245d98389b89aa848e4556793101adf6e84 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:30:03 +0200 Subject: [PATCH 51/68] reporters: Remove duplicate MessageFormatterFunction test --- .../test/unit/reporters/test_message.py | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/master/buildbot/test/unit/reporters/test_message.py b/master/buildbot/test/unit/reporters/test_message.py index c7303450ad36..48cd1d0853e8 100644 --- a/master/buildbot/test/unit/reporters/test_message.py +++ b/master/buildbot/test/unit/reporters/test_message.py @@ -493,26 +493,6 @@ def test_basic(self): "extra_info": None, }) - @defer.inlineCallbacks - def test_renderable(self): - function = mock.Mock(side_effect=lambda x: {'key': 'value'}) - - formatter = message.MessageFormatterFunction(function, 'json') - - res = yield self.do_one_test(formatter, SUCCESS, SUCCESS) - - function.assert_called_with({ - 'build': BuildDictLookAlike(extra_keys=['prev_build'], - expected_missing_keys=['parentbuilder', 'buildrequest', - 'parentbuild']) - }) - self.assertEqual(res, { - 'body': {'key': 'value'}, - 'type': 'json', - 'subject': None, - "extra_info": None, - }) - class TestMessageFormatterFunctionRaw(MessageFormatterTestBase): @defer.inlineCallbacks From dbd6c26b21960717dc2c5d991865c0731f331eb4 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:30:07 +0200 Subject: [PATCH 52/68] reporters: Improve error logging in ReporterBase._got_event() Current logs are insufficient to understand which generator causes errors. --- master/buildbot/reporters/base.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/master/buildbot/reporters/base.py b/master/buildbot/reporters/base.py index fdff70539dcf..13763e024091 100644 --- a/master/buildbot/reporters/base.py +++ b/master/buildbot/reporters/base.py @@ -109,9 +109,13 @@ def _got_event(self, key, msg): reports = [] for g in self.generators: if self._does_generator_want_key(g, key): - report = yield g.generate(self.master, self, key, msg) - if report is not None: - reports.append(report) + try: + report = yield g.generate(self.master, self, key, msg) + if report is not None: + reports.append(report) + except Exception as e: + log.err(e, "Got exception when handling reporter events: " + f"key: {key} generator: {g}") if reports: yield self.sendMessage(reports) From 0b5014e8333f15d2c91b1ea1a1179c377af33628 Mon Sep 17 00:00:00 2001 From: Le Philousophe Date: Mon, 22 Jan 2024 20:25:46 +0100 Subject: [PATCH 53/68] www: Pass config object to plugins setup function --- www/plugin_support/src/index.ts | 2 +- www/react-base/src/index.tsx | 4 +++- www/react-base/src/plugins/GlobalSetup.ts | 18 ++++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/www/plugin_support/src/index.ts b/www/plugin_support/src/index.ts index d48f579a2105..1a441f57ed3d 100644 --- a/www/plugin_support/src/index.ts +++ b/www/plugin_support/src/index.ts @@ -61,7 +61,7 @@ export interface ISettings { save(): void; }; -export type PluginRegistrationCallback = (registrationCallbacks: RegistrationCallbacks) => void; +export type PluginRegistrationCallback = (registrationCallbacks: RegistrationCallbacks, config: any) => void; const pluginRegistrationCallbacks: PluginRegistrationCallback[] = []; const pluginRegistrationConsumers: ((callback: PluginRegistrationCallback) => void)[] = []; diff --git a/www/react-base/src/index.tsx b/www/react-base/src/index.tsx index 9fed7ac5a422..fc1fab8f24c4 100644 --- a/www/react-base/src/index.tsx +++ b/www/react-base/src/index.tsx @@ -3,7 +3,7 @@ import './globals2'; import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; -import "./plugins/GlobalSetup"; +import {initializeGlobalSetup} from "./plugins/GlobalSetup"; import "buildbot-plugin-support"; import {App} from './App'; import { @@ -49,6 +49,8 @@ const doRender = (buildbotFrontendConfig: Config) => { globalSettings.applyBuildbotConfig(buildbotFrontendConfig); globalSettings.load(); + initializeGlobalSetup(buildbotFrontendConfig); + for (const pluginKey in buildbotFrontendConfig.plugins) { // TODO: in production this could be added to the document by buildbot backend const pluginScript = document.createElement('script'); diff --git a/www/react-base/src/plugins/GlobalSetup.ts b/www/react-base/src/plugins/GlobalSetup.ts index 873fd90e2250..69eeeee8756b 100644 --- a/www/react-base/src/plugins/GlobalSetup.ts +++ b/www/react-base/src/plugins/GlobalSetup.ts @@ -22,16 +22,18 @@ import { RouteConfig, SettingGroupConfig } from "buildbot-plugin-support"; +import {Config} from "buildbot-ui"; import {globalMenuSettings} from "./GlobalMenuSettings"; import {globalRoutes} from "./GlobalRoutes"; import {globalSettings} from "./GlobalSettings"; -const onBuildbotSetupPlugin = (callback: (registrationCallbacks: RegistrationCallbacks) => void) => { - callback({ - registerMenuGroup: (group: GroupSettings) => { globalMenuSettings.addGroup(group); }, - registerRoute: (route: RouteConfig) => { globalRoutes.addRoute(route); }, - registerSettingGroup: (group: SettingGroupConfig) => { globalSettings.addGroup(group); }, - }); +export function initializeGlobalSetup(config: Config) { + const onBuildbotSetupPlugin = (callback: (registrationCallbacks: RegistrationCallbacks, config: Config) => void) => { + callback({ + registerMenuGroup: (group: GroupSettings) => { globalMenuSettings.addGroup(group); }, + registerRoute: (route: RouteConfig) => { globalRoutes.addRoute(route); }, + registerSettingGroup: (group: SettingGroupConfig) => { globalSettings.addGroup(group); }, + }, config); + } + registerPluginRegistrationConsumer(onBuildbotSetupPlugin); } - -registerPluginRegistrationConsumer(onBuildbotSetupPlugin); From 4f144e2563dab08329b251c82fc1d3a11b01115a Mon Sep 17 00:00:00 2001 From: Le Philousophe Date: Mon, 22 Jan 2024 20:51:56 +0100 Subject: [PATCH 54/68] www: Port wsgi_dashboards to React --- Makefile | 4 +- .../add-react-wsgi-dashboards.feature | 2 + www/react-base/vite.config.ts | 1 + .../__init__.py | 42 + .../config/jest/babelTransform.cjs | 29 + .../config/jest/cssTransform.cjs | 14 + .../config/jest/fileTransform.cjs | 40 + www/react-wsgi_dashboards/package.json | 106 + www/react-wsgi_dashboards/setup.cfg | 0 www/react-wsgi_dashboards/setup.py | 51 + www/react-wsgi_dashboards/src/index.ts | 19 + .../WSGIDashboardsView/WSGIDashboardsView.tsx | 115 + www/react-wsgi_dashboards/tsconfig.json | 27 + www/react-wsgi_dashboards/vite.config.ts | 62 + www/react-wsgi_dashboards/yarn.lock | 5329 +++++++++++++++++ 15 files changed, 5839 insertions(+), 2 deletions(-) create mode 100644 newsfragments/add-react-wsgi-dashboards.feature create mode 100644 www/react-wsgi_dashboards/buildbot_react_wsgi_dashboards/__init__.py create mode 100644 www/react-wsgi_dashboards/config/jest/babelTransform.cjs create mode 100644 www/react-wsgi_dashboards/config/jest/cssTransform.cjs create mode 100644 www/react-wsgi_dashboards/config/jest/fileTransform.cjs create mode 100644 www/react-wsgi_dashboards/package.json create mode 100644 www/react-wsgi_dashboards/setup.cfg create mode 100644 www/react-wsgi_dashboards/setup.py create mode 100644 www/react-wsgi_dashboards/src/index.ts create mode 100644 www/react-wsgi_dashboards/src/views/WSGIDashboardsView/WSGIDashboardsView.tsx create mode 100644 www/react-wsgi_dashboards/tsconfig.json create mode 100644 www/react-wsgi_dashboards/vite.config.ts create mode 100644 www/react-wsgi_dashboards/yarn.lock diff --git a/Makefile b/Makefile index 46c23521d70d..c2a1c1a83b57 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,12 @@ PYTHON ?= $(ROOT_DIR)/$(VENV_NAME)/bin/python VENV_PY_VERSION ?= python3 YARN := $(shell which yarnpkg || which yarn) -WWW_PKGS := www/base www/react-base www/console_view www/react-console_view www/grid_view www/react-grid_view www/waterfall_view www/react-waterfall_view www/wsgi_dashboards www/badges +WWW_PKGS := www/base www/react-base www/console_view www/react-console_view www/grid_view www/react-grid_view www/waterfall_view www/react-waterfall_view www/wsgi_dashboards www/react-wsgi_dashboards www/badges WWW_EX_PKGS := www/nestedexample www/codeparameter WWW_DEP_PKGS := www/guanlecoja-ui www/data_module www/plugin_support www/react-data-module www/react-ui ALL_PKGS := master worker pkg $(WWW_PKGS) -WWW_PKGS_FOR_UNIT_TESTS := $(filter-out www/badges www/plugin_support www/react-ui www/react-grid_view, $(WWW_DEP_PKGS) $(WWW_PKGS)) +WWW_PKGS_FOR_UNIT_TESTS := $(filter-out www/badges www/plugin_support www/react-ui www/react-grid_view www/react-wsgi_dashboards, $(WWW_DEP_PKGS) $(WWW_PKGS)) ALL_PKGS_TARGETS := $(addsuffix _pkg,$(ALL_PKGS)) .PHONY: $(ALL_PKGS_TARGETS) diff --git a/newsfragments/add-react-wsgi-dashboards.feature b/newsfragments/add-react-wsgi-dashboards.feature new file mode 100644 index 000000000000..492637ba5293 --- /dev/null +++ b/newsfragments/add-react-wsgi-dashboards.feature @@ -0,0 +1,2 @@ +Added a new WSGI dashboards plugin for React frontend. +It is backwards compatible with AngularJS one but may require changes in CSS styling of displayed web pages. diff --git a/www/react-base/vite.config.ts b/www/react-base/vite.config.ts index 2d51360bfc3b..7ab93928c2ba 100644 --- a/www/react-base/vite.config.ts +++ b/www/react-base/vite.config.ts @@ -35,6 +35,7 @@ const buildPluginsPathsMap = () => { addPlugin('react_console_view', path.join(root, `react-console_view/buildbot_react_console_view/static/`)) addPlugin('react_waterfall_view', path.join(root, `react-waterfall_view/buildbot_react_waterfall_view/static/`)) + addPlugin('react_wsgi_dashboards', path.join(root, `react-wsgi_dashboards/buildbot_react_wsgi_dashboards/static/`)) return aliases; } diff --git a/www/react-wsgi_dashboards/buildbot_react_wsgi_dashboards/__init__.py b/www/react-wsgi_dashboards/buildbot_react_wsgi_dashboards/__init__.py new file mode 100644 index 000000000000..13035c410447 --- /dev/null +++ b/www/react-wsgi_dashboards/buildbot_react_wsgi_dashboards/__init__.py @@ -0,0 +1,42 @@ +# This file is part of Buildbot. Buildbot is free software: you can +# redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright Buildbot Team Members + +from twisted.internet import reactor +from twisted.internet.threads import blockingCallFromThread +from twisted.web.wsgi import WSGIResource + +from buildbot.util import unicode2bytes +from buildbot.www.plugin import Application + + +class WSGIDashboardsApplication(Application): + + def setConfiguration(self, config): + super().setConfiguration(config) + for dashboard in config: + dashboard['app'].buildbot_api = self + resource = WSGIResource(reactor, + reactor.getThreadPool(), dashboard['app']) + self.resource.putChild(unicode2bytes(dashboard['name']), resource) + + def dataGet(self, path, **kwargs): + if not isinstance(path, tuple): + path = tuple(path.strip("/").split("/")) + return blockingCallFromThread(reactor, + self.master.data.get, path, **kwargs) + + +# create the interface for the setuptools entry point +ep = WSGIDashboardsApplication(__name__, "Buildbot WSGI Dashboard Glue") diff --git a/www/react-wsgi_dashboards/config/jest/babelTransform.cjs b/www/react-wsgi_dashboards/config/jest/babelTransform.cjs new file mode 100644 index 000000000000..dabf5a8cb7e2 --- /dev/null +++ b/www/react-wsgi_dashboards/config/jest/babelTransform.cjs @@ -0,0 +1,29 @@ +'use strict'; + +const babelJest = require('babel-jest'); + +const hasJsxRuntime = (() => { + if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { + return false; + } + + try { + require.resolve('react/jsx-runtime'); + return true; + } catch (e) { + return false; + } +})(); + +module.exports = babelJest.createTransformer({ + presets: [ + [ + require.resolve('babel-preset-react-app'), + { + runtime: hasJsxRuntime ? 'automatic' : 'classic', + }, + ], + ], + babelrc: false, + configFile: false, +}); diff --git a/www/react-wsgi_dashboards/config/jest/cssTransform.cjs b/www/react-wsgi_dashboards/config/jest/cssTransform.cjs new file mode 100644 index 000000000000..8f65114812a4 --- /dev/null +++ b/www/react-wsgi_dashboards/config/jest/cssTransform.cjs @@ -0,0 +1,14 @@ +'use strict'; + +// This is a custom Jest transformer turning style imports into empty objects. +// http://facebook.github.io/jest/docs/en/webpack.html + +module.exports = { + process() { + return 'module.exports = {};'; + }, + getCacheKey() { + // The output is always the same. + return 'cssTransform'; + }, +}; diff --git a/www/react-wsgi_dashboards/config/jest/fileTransform.cjs b/www/react-wsgi_dashboards/config/jest/fileTransform.cjs new file mode 100644 index 000000000000..aab67618c38b --- /dev/null +++ b/www/react-wsgi_dashboards/config/jest/fileTransform.cjs @@ -0,0 +1,40 @@ +'use strict'; + +const path = require('path'); +const camelcase = require('camelcase'); + +// This is a custom Jest transformer turning file imports into filenames. +// http://facebook.github.io/jest/docs/en/webpack.html + +module.exports = { + process(src, filename) { + const assetFilename = JSON.stringify(path.basename(filename)); + + if (filename.match(/\.svg$/)) { + // Based on how SVGR generates a component name: + // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 + const pascalCaseFilename = camelcase(path.parse(filename).name, { + pascalCase: true, + }); + const componentName = `Svg${pascalCaseFilename}`; + return `const React = require('react'); + module.exports = { + __esModule: true, + default: ${assetFilename}, + ReactComponent: React.forwardRef(function ${componentName}(props, ref) { + return { + $$typeof: Symbol.for('react.element'), + type: 'svg', + ref: ref, + key: null, + props: Object.assign({}, props, { + children: ${assetFilename} + }) + }; + }), + };`; + } + + return `module.exports = ${assetFilename};`; + }, +}; diff --git a/www/react-wsgi_dashboards/package.json b/www/react-wsgi_dashboards/package.json new file mode 100644 index 000000000000..01cfed3dea1a --- /dev/null +++ b/www/react-wsgi_dashboards/package.json @@ -0,0 +1,106 @@ +{ + "name": "buildbot-wsgi-dashboards", + "private": true, + "type": "module", + "module": "buildbot_react_wsgi_dashboards/static/scripts.js", + "style": "buildbot_react_wsgi_dashboards/static/styles.css", + "scripts": { + "start": "vite", + "build": "vite build", + "build-dev": "vite build -m development", + "test": "jest", + "test-watch": "jest --watchAll" + }, + "eslintConfig": { + "extends": [ + "react-app" + ] + }, + "jest": { + "roots": [ + "/src" + ], + "collectCoverageFrom": [ + "src/**/*.{js,jsx,ts,tsx}", + "!src/**/*.d.ts" + ], + "testMatch": [ + "/src/**/__tests__/**/*.{js,jsx,ts,tsx}", + "/src/**/*.{spec,test}.{js,jsx,ts,tsx}" + ], + "testEnvironment": "jsdom", + "testRunner": "./node_modules/jest-circus/runner.js", + "transform": { + "^.+\\.(js|jsx|mjs|cjs|ts|tsx)$": "/config/jest/babelTransform.cjs", + "^.+\\.css$": "/config/jest/cssTransform.cjs", + "^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)": "/config/jest/fileTransform.cjs" + }, + "transformIgnorePatterns": [ + "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$", + "^.+\\.module\\.(css|sass|scss)$" + ], + "modulePaths": [], + "moduleDirectories": [ + "/node_modules", + "node_modules" + ], + "moduleNameMapper": { + "^react-native$": "react-native-web", + "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy" + }, + "moduleFileExtensions": [ + "js", + "ts", + "tsx", + "json", + "jsx", + "node" + ], + "watchPlugins": [ + "jest-watch-typeahead/filename", + "jest-watch-typeahead/testname" + ], + "resetMocks": true + }, + "babel": { + "presets": [ + "react-app" + ] + }, + "peerDependencies": { + "axios": "^0.27.2", + "mobx": "^6.6.1", + "mobx-react": "^7.5.2", + "moment": "^2.29.4", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.3.0" + }, + "devDependencies": { + "@types/jest": "^29.5.0", + "@vitejs/plugin-react": "^3.1.0", + "axios": "^0.27.2", + "axios-mock-adapter": "^1.21.1", + "babel-preset-react-app": "^10.0.0", + "buildbot-data-js": "link:../react-data-module", + "buildbot-plugin-support": "link:../plugin_support", + "jest": "26.6.0", + "jest-circus": "26.6.0", + "jest-watch-typeahead": "0.6.1", + "mobx": "^6.6.1", + "mobx-react": "^7.5.2", + "moment": "^2.29.4", + "react": "^18.2.0", + "react-app-polyfill": "^2.0.0", + "react-bootstrap": "^1.6.5", + "react-dom": "^18.2.0", + "react-router-dom": "^6.3.0", + "sass": "^1.56.0", + "vite": "^4.5.2" + }, + "license": "GPL-2.0", + "dependencies": { + "buildbot-ui": "link:../react-ui", + "react-icons": "^4.8.0" + } +} diff --git a/www/react-wsgi_dashboards/setup.cfg b/www/react-wsgi_dashboards/setup.cfg new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/www/react-wsgi_dashboards/setup.py b/www/react-wsgi_dashboards/setup.py new file mode 100644 index 000000000000..b0f665886fed --- /dev/null +++ b/www/react-wsgi_dashboards/setup.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# +# This file is part of Buildbot. Buildbot is free software: you can +# redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright Buildbot Team Members + +try: + from buildbot_pkg import setup_www_plugin +except ImportError: + import sys + + print( + 'Please install buildbot_pkg module in order to install that ' + 'package, or use the pre-build .whl modules available on pypi', + file=sys.stderr, + ) + sys.exit(1) + +setup_www_plugin( + name='buildbot-react-wsgi-dashboards', + description='Buildbot plugin to integrate flask or bottle' + 'dashboards to buildbot UI (React)', + author=u'Buildbot maintainers', + author_email=u'devel@buildbot.net', + url='http://buildbot.net/', + packages=['buildbot_react_wsgi_dashboards'], + package_data={ + '': [ + 'VERSION', + 'static/*' + ] + }, + entry_points=""" + [buildbot.www] + react_wsgi_dashboards = buildbot_react_wsgi_dashboards:ep + """, + classifiers=[ + 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)' + ], +) diff --git a/www/react-wsgi_dashboards/src/index.ts b/www/react-wsgi_dashboards/src/index.ts new file mode 100644 index 000000000000..b040bbf39588 --- /dev/null +++ b/www/react-wsgi_dashboards/src/index.ts @@ -0,0 +1,19 @@ +/* + This file is part of Buildbot. Buildbot is free software: you can + redistribute it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 51 + Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Copyright Buildbot Team Members +*/ + +import "buildbot-plugin-support"; +import './views/WSGIDashboardsView/WSGIDashboardsView'; diff --git a/www/react-wsgi_dashboards/src/views/WSGIDashboardsView/WSGIDashboardsView.tsx b/www/react-wsgi_dashboards/src/views/WSGIDashboardsView/WSGIDashboardsView.tsx new file mode 100644 index 000000000000..bb90108c5ffd --- /dev/null +++ b/www/react-wsgi_dashboards/src/views/WSGIDashboardsView/WSGIDashboardsView.tsx @@ -0,0 +1,115 @@ +/* + This file is part of Buildbot. Buildbot is free software: you can + redistribute it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 51 + Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Copyright Buildbot Team Members +*/ + +import axios, {AxiosRequestConfig} from 'axios'; +import {createElement, useEffect, useRef, useState} from "react"; +import * as fa from "react-icons/fa"; +import {CancellablePromise, capitalize} from "buildbot-data-js"; +import {LoadingIndicator} from "buildbot-ui"; +import {buildbotSetupPlugin} from "buildbot-plugin-support"; + +function getWsgiUrl(location: Location, name) { + let pathname = location.pathname; + if (!pathname.endsWith("/")) { + pathname += "/"; + } + return `${location.protocol}//${location.hostname}:${location.port}${pathname}plugins/react_wsgi_dashboards/${name}/index.html`; +} + +function getData(url) { + return new CancellablePromise((resolve, reject, onCancel) => { + const controller = new AbortController(); + onCancel(() => { + controller.abort(); + }); + let config = { + method: 'get', + url, + params: {}, + signal: controller.signal, + }; + const request = axios.request(config); + request.then(response => { + resolve(response.data); + }).catch(reason => { + reject(reason); + }) + }); +} + +export default function WSGIDashboardsView({ name }) { + const location = getWsgiUrl(window.location, name); + const pendingRequest = useRef | null>(null); + + const [wsgiContent, setWsgiContent] = useState(undefined); + + useEffect(() => { + if (pendingRequest.current !== null) { + pendingRequest.current.cancel(); + } + + pendingRequest.current = getData(location); + pendingRequest.current.then(content => { + setWsgiContent(content); + }); + return () => { + if (pendingRequest.current !== null) { + pendingRequest.current.cancel(); + } + }; + }, [location]); + + if (wsgiContent === undefined) { + return ( +

+ +
+ ) + } + return ( +
+ ) +} + +buildbotSetupPlugin((reg, config) => { + const wsgi_dashboards = config.plugins['react_wsgi_dashboards']; + + for (let dashboard of wsgi_dashboards) { + const { name } = dashboard; + let { caption } = dashboard; + if (caption == null) { caption = capitalize(name); } + if (dashboard.order == null) { dashboard.order = 5; } + + const icon = fa['Fa' + capitalize(dashboard.icon)]; + + reg.registerMenuGroup({ + name: name, + caption: caption, + icon: createElement(icon, {}), + order: dashboard.order, + route: `/wsgi/${name}`, + parentName: null, + }); + + reg.registerRoute({ + route: `/wsgi/${name}`, + group: name, + element: () => , + }); + } +}); + diff --git a/www/react-wsgi_dashboards/tsconfig.json b/www/react-wsgi_dashboards/tsconfig.json new file mode 100644 index 000000000000..b8a0e0e4b235 --- /dev/null +++ b/www/react-wsgi_dashboards/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "target": "es6", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src" + ] +} diff --git a/www/react-wsgi_dashboards/vite.config.ts b/www/react-wsgi_dashboards/vite.config.ts new file mode 100644 index 000000000000..dd880fd1318f --- /dev/null +++ b/www/react-wsgi_dashboards/vite.config.ts @@ -0,0 +1,62 @@ +import {resolve} from "path"; +import {defineConfig} from "vite"; +import react from "@vitejs/plugin-react"; +import { ModuleFormat } from "rollup"; + +const outDir = 'buildbot_react_wsgi_dashboards/static'; + +export default defineConfig({ + plugins: [ + react({ + babel: { + parserOpts: { + plugins: ['decorators-legacy', 'classProperties'] + } + } + }), + ], + define: { + 'process.env.NODE_ENV': '"production"', + }, + build: { + lib: { + entry: resolve(__dirname, 'src/index.ts'), + name: "buildbotWSGIDashboardsPlugin", + formats: ["umd"], + fileName: "scripts", + }, + rollupOptions: { + external: [ + 'axios', + 'buildbot-data-js', + 'buildbot-ui', + 'mobx', + 'mobx-react', + 'moment', + 'react', + 'react-dom', + 'react-router-dom', + 'buildbot-plugin-support', + ], + output: { + assetFileNames: 'styles.css', + entryFileNames: 'scripts.js', + globals: { + axios: "axios", + "buildbot-data-js": "BuildbotDataJs", + "buildbot-plugin-support": "BuildbotPluginSupport", + "buildbot-ui": "BuildbotUi", + mobx: "mobx", + "mobx-react": "mobxReact", + react: "React", + moment: "moment", + "react-dom": "ReactDOM", + "react-router-dom": "ReactRouterDOM", + }, + }, + }, + target: ['es2015'], + outDir: outDir, + emptyOutDir: true, + }, +}); diff --git a/www/react-wsgi_dashboards/yarn.lock b/www/react-wsgi_dashboards/yarn.lock new file mode 100644 index 000000000000..252a50290ae5 --- /dev/null +++ b/www/react-wsgi_dashboards/yarn.lock @@ -0,0 +1,5329 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== + +"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.20.12", "@babel/core@^7.7.5": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/generator@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== + dependencies: + "@babel/types" "^7.21.4" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" + +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== + dependencies: + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" + integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz#40411a8ab134258ad2cf3a3d987ec6aa0723cee5" + integrity sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.3.1" + +"@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" + integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== + dependencies: + "@babel/types" "^7.21.0" + +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + +"@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== + dependencies: + "@babel/types" "^7.20.0" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== + +"@babel/helper-wrap-function@^7.18.9": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== + +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" + integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" + +"@babel/plugin-proposal-async-generator-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.16.0", "@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-class-static-block@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" + integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-decorators@^7.16.4": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz#70e0c89fdcd7465c97593edb8f628ba6e4199d63" + integrity sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/plugin-syntax-decorators" "^7.21.0" + +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.16.0", "@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== + dependencies: + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.20.7" + +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.16.0", "@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.16.0", "@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" + integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-decorators@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.21.0.tgz#d2b3f31c3e86fa86e16bb540b7660c55bd7d0e78" + integrity sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-flow@^7.18.6": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz#3e37fca4f06d93567c1cd9b75156422e90a67107" + integrity sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-syntax-import-assertions@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" + integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" + integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.20.0": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-arrow-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" + integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-async-to-generator@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-block-scoping@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-classes@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" + integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" + +"@babel/plugin-transform-destructuring@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-flow-strip-types@^7.16.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" + integrity sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-flow" "^7.18.6" + +"@babel/plugin-transform-for-of@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" + integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + dependencies: + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-modules-amd@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" + integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== + dependencies: + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-modules-commonjs@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" + integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== + dependencies: + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-simple-access" "^7.20.2" + +"@babel/plugin-transform-modules-systemjs@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" + integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== + dependencies: + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-identifier" "^7.19.1" + +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + +"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-jsx-development@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" + integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.18.6" + +"@babel/plugin-transform-react-jsx-self@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz#ec98d4a9baafc5a1eb398da4cf94afbb40254a54" + integrity sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-react-jsx-source@^7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86" + integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-transform-react-jsx@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz#656b42c2fdea0a6d8762075d58ef9d4e3c4ab8a2" + integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.21.0" + +"@babel/plugin-transform-react-pure-annotations@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" + integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-regenerator@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" + integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + regenerator-transform "^0.15.1" + +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-runtime@^7.16.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz#2e1da21ca597a7d01fc96b699b21d8d2023191aa" + integrity sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA== + dependencies: + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-plugin-utils" "^7.20.2" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + semver "^6.3.0" + +"@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typescript@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b" + integrity sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-typescript" "^7.20.0" + +"@babel/plugin-transform-unicode-escapes@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/preset-env@^7.16.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.4.tgz#a952482e634a8dd8271a3fe5459a16eb10739c58" + integrity sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw== + dependencies: + "@babel/compat-data" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" + "@babel/plugin-proposal-async-generator-functions" "^7.20.7" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.21.0" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.20.7" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.21.0" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.21.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.20.7" + "@babel/plugin-transform-async-to-generator" "^7.20.7" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-classes" "^7.21.0" + "@babel/plugin-transform-computed-properties" "^7.20.7" + "@babel/plugin-transform-destructuring" "^7.21.3" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.21.0" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.20.11" + "@babel/plugin-transform-modules-commonjs" "^7.21.2" + "@babel/plugin-transform-modules-systemjs" "^7.20.11" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.21.3" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.20.5" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.20.7" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.21.4" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + core-js-compat "^3.25.1" + semver "^6.3.0" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.16.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" + integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-react-display-name" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-pure-annotations" "^7.18.6" + +"@babel/preset-typescript@^7.16.0": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.4.tgz#b913ac8e6aa8932e47c21b01b4368d8aa239a529" + integrity sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-syntax-jsx" "^7.21.4" + "@babel/plugin-transform-modules-commonjs" "^7.21.2" + "@babel/plugin-transform-typescript" "^7.21.3" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.14.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@esbuild/android-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" + integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== + +"@esbuild/android-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" + integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== + +"@esbuild/android-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" + integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== + +"@esbuild/darwin-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" + integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== + +"@esbuild/darwin-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" + integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== + +"@esbuild/freebsd-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" + integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== + +"@esbuild/freebsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" + integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== + +"@esbuild/linux-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" + integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== + +"@esbuild/linux-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" + integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== + +"@esbuild/linux-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" + integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== + +"@esbuild/linux-loong64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" + integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== + +"@esbuild/linux-mips64el@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" + integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== + +"@esbuild/linux-ppc64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" + integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== + +"@esbuild/linux-riscv64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" + integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== + +"@esbuild/linux-s390x@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" + integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== + +"@esbuild/linux-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" + integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== + +"@esbuild/netbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" + integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== + +"@esbuild/openbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" + integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== + +"@esbuild/sunos-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" + integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== + +"@esbuild/win32-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" + integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== + +"@esbuild/win32-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" + integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== + +"@esbuild/win32-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" + integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" + integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.6.2" + jest-util "^26.6.2" + slash "^3.0.0" + +"@jest/core@^26.6.0", "@jest/core@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" + integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/reporters" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-changed-files "^26.6.2" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-resolve-dependencies "^26.6.3" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + jest-watcher "^26.6.2" + micromatch "^4.0.2" + p-each-series "^2.1.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^26.6.0", "@jest/environment@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" + integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== + dependencies: + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== + dependencies: + jest-get-type "^29.4.3" + +"@jest/fake-timers@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" + integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== + dependencies: + "@jest/types" "^26.6.2" + "@sinonjs/fake-timers" "^6.0.1" + "@types/node" "*" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +"@jest/globals@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" + integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/types" "^26.6.2" + expect "^26.6.2" + +"@jest/reporters@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" + integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.3" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^26.6.2" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^7.0.0" + optionalDependencies: + node-notifier "^8.0.0" + +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + +"@jest/source-map@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" + integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + +"@jest/test-result@^26.6.0", "@jest/test-result@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" + integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" + integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== + dependencies: + "@jest/test-result" "^26.6.2" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^26.6.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-regex-util "^26.0.0" + jest-util "^26.6.2" + micromatch "^4.0.2" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^26.6.0", "@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== + dependencies: + "@jest/schemas" "^29.4.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@popperjs/core@^2.11.6": + version "2.11.7" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.7.tgz#ccab5c8f7dc557a52ca3288c10075c9ccd37fff7" + integrity sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw== + +"@remix-run/router@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.5.0.tgz#57618e57942a5f0131374a9fdb0167e25a117fdc" + integrity sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg== + +"@restart/context@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@restart/context/-/context-2.1.4.tgz#a99d87c299a34c28bd85bb489cb07bfd23149c02" + integrity sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q== + +"@restart/hooks@^0.4.7": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.9.tgz#ad858fb39d99e252cccce19416adc18fc3f18fcb" + integrity sha512-3BekqcwB6Umeya+16XPooARn4qEPW6vNvwYnlofIYe6h9qG1/VeD7UvShCWx11eFz5ELYmwIEshz+MkPX3wjcQ== + dependencies: + dequal "^2.0.2" + +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== + +"@sinonjs/commons@^1.7.0": + version "1.8.6" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" + integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.18.3" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.3.tgz#dfc508a85781e5698d5b33443416b6268c4b3e8d" + integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== + dependencies: + "@babel/types" "^7.3.0" + +"@types/graceful-fs@^4.1.2": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + dependencies: + "@types/node" "*" + +"@types/invariant@^2.2.33": + version "2.2.35" + resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.35.tgz#cd3ebf581a6557452735688d8daba6cf0bd5a3be" + integrity sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.0.tgz#337b90bbcfe42158f39c2fb5619ad044bbb518ac" + integrity sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/node@*": + version "18.15.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== + +"@types/normalize-package-data@^2.4.0": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/prettier@^2.0.0": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== + +"@types/prop-types@*", "@types/prop-types@^15.7.3": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/react-transition-group@^4.4.1": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" + integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@>=16.14.8", "@types/react@>=16.9.11": + version "18.0.32" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.32.tgz#5e88b2af6833251d54ec7fe86d393224499f41d5" + integrity sha512-gYGXdtPQ9Cj0w2Fwqg5/ak6BcK3Z15YgjSqtyDizWUfx7mQ8drs0NBUzRRsAdoFVTO8kJ8L2TL8Skm7OFPnLUw== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.3" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" + integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== + +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + +"@types/warning@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52" + integrity sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA== + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^15.0.0": + version "15.0.15" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.15.tgz#e609a2b1ef9e05d90489c2f5f45bbfb2be092158" + integrity sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + dependencies: + "@types/yargs-parser" "*" + +"@vitejs/plugin-react@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz#d1091f535eab8b83d6e74034d01e27d73c773240" + integrity sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g== + dependencies: + "@babel/core" "^7.20.12" + "@babel/plugin-transform-react-jsx-self" "^7.18.6" + "@babel/plugin-transform-react-jsx-source" "^7.19.6" + magic-string "^0.27.0" + react-refresh "^0.14.0" + +abab@^2.0.3, abab@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.2.4: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.0, ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.3, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== + +asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +axios-mock-adapter@^1.21.1: + version "1.21.4" + resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.21.4.tgz#ced09b54b245b338422e3af425ae529bfa26e051" + integrity sha512-ztnENm28ONAKeRXC/6SUW6pcsaXbThKq93MRDRAA47LYTzrGSDoO/DCr1NHz7jApEl95DrBoGPvZ0r9xtSbjqw== + dependencies: + fast-deep-equal "^3.1.3" + is-buffer "^2.0.5" + +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + +babel-jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" + integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== + dependencies: + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" + +babel-plugin-istanbul@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" + integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + +babel-plugin-polyfill-corejs2@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" + +babel-plugin-polyfill-regenerator@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + +babel-plugin-transform-react-remove-prop-types@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" + integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" + integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== + dependencies: + babel-plugin-jest-hoist "^26.6.2" + babel-preset-current-node-syntax "^1.0.0" + +babel-preset-react-app@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz#ed6005a20a24f2c88521809fa9aea99903751584" + integrity sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg== + dependencies: + "@babel/core" "^7.16.0" + "@babel/plugin-proposal-class-properties" "^7.16.0" + "@babel/plugin-proposal-decorators" "^7.16.4" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.0" + "@babel/plugin-proposal-numeric-separator" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.16.0" + "@babel/plugin-proposal-private-methods" "^7.16.0" + "@babel/plugin-transform-flow-strip-types" "^7.16.0" + "@babel/plugin-transform-react-display-name" "^7.16.0" + "@babel/plugin-transform-runtime" "^7.16.4" + "@babel/preset-env" "^7.16.4" + "@babel/preset-react" "^7.16.0" + "@babel/preset-typescript" "^7.16.0" + "@babel/runtime" "^7.16.3" + babel-plugin-macros "^3.1.0" + babel-plugin-transform-react-remove-prop-types "^0.4.24" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browserslist@^4.21.3, browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +"buildbot-data-js@link:../react-data-module": + version "0.0.0" + uid "" + +"buildbot-plugin-support@link:../plugin_support": + version "0.0.0" + uid "" + +"buildbot-ui@link:../react-ui": + version "0.0.0" + uid "" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001449: + version "1.0.30001473" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz#3859898b3cab65fc8905bb923df36ad35058153c" + integrity sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +chalk@^2.0.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +"chokidar@>=3.0.0 <4.0.0": + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +ci-info@^3.2.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + +cjs-module-lexer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" + integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +classnames@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== + +core-js-compat@^3.25.1: + version "3.29.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" + integrity sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA== + dependencies: + browserslist "^4.21.5" + +core-js@^3.6.5: + version "3.29.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.29.1.tgz#40ff3b41588b091aaed19ca1aa5cb111803fa9a6" + integrity sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw== + +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +csstype@^3.0.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decimal.js@^10.2.1: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dequal@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + +dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +electron-to-chromium@^1.4.284: + version "1.4.348" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.348.tgz#f49379dc212d79f39112dd026f53e371279e433d" + integrity sha512-gM7TdwuG3amns/1rlgxMbeeyNoBFPa+4Uu0c7FeROWh4qWmvSOnvcslKmWy51ggLKZ2n/F/4i2HJ+PVNxH9uCQ== + +emittery@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" + integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +esbuild@^0.18.10: + version "0.18.20" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6" + integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== + optionalDependencies: + "@esbuild/android-arm" "0.18.20" + "@esbuild/android-arm64" "0.18.20" + "@esbuild/android-x64" "0.18.20" + "@esbuild/darwin-arm64" "0.18.20" + "@esbuild/darwin-x64" "0.18.20" + "@esbuild/freebsd-arm64" "0.18.20" + "@esbuild/freebsd-x64" "0.18.20" + "@esbuild/linux-arm" "0.18.20" + "@esbuild/linux-arm64" "0.18.20" + "@esbuild/linux-ia32" "0.18.20" + "@esbuild/linux-loong64" "0.18.20" + "@esbuild/linux-mips64el" "0.18.20" + "@esbuild/linux-ppc64" "0.18.20" + "@esbuild/linux-riscv64" "0.18.20" + "@esbuild/linux-s390x" "0.18.20" + "@esbuild/linux-x64" "0.18.20" + "@esbuild/netbsd-x64" "0.18.20" + "@esbuild/openbsd-x64" "0.18.20" + "@esbuild/sunos-x64" "0.18.20" + "@esbuild/win32-arm64" "0.18.20" + "@esbuild/win32-ia32" "0.18.20" + "@esbuild/win32-x64" "0.18.20" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +exec-sh@^0.3.2: + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^26.6.0, expect@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" + integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== + dependencies: + "@jest/types" "^26.6.2" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + +expect@^29.0.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== + dependencies: + "@jest/expect-utils" "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +follow-redirects@^1.14.9: + version "1.15.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" + integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== + dependencies: + map-cache "^0.2.2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.1.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +immutable@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" + integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-buffer@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" + integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== + dependencies: + "@jest/types" "^26.6.2" + execa "^4.0.0" + throat "^5.0.0" + +jest-circus@26.6.0: + version "26.6.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-26.6.0.tgz#7d9647b2e7f921181869faae1f90a2629fd70705" + integrity sha512-L2/Y9szN6FJPWFK8kzWXwfp+FOR7xq0cUL4lIsdbIdwz3Vh6P1nrpcqOleSzr28zOtSHQNV9Z7Tl+KkuK7t5Ng== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^26.6.0" + "@jest/test-result" "^26.6.0" + "@jest/types" "^26.6.0" + "@types/babel__traverse" "^7.0.4" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^26.6.0" + is-generator-fn "^2.0.0" + jest-each "^26.6.0" + jest-matcher-utils "^26.6.0" + jest-message-util "^26.6.0" + jest-runner "^26.6.0" + jest-runtime "^26.6.0" + jest-snapshot "^26.6.0" + jest-util "^26.6.0" + pretty-format "^26.6.0" + stack-utils "^2.0.2" + throat "^5.0.0" + +jest-cli@^26.6.0: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" + integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== + dependencies: + "@jest/core" "^26.6.3" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + import-local "^3.0.2" + is-ci "^2.0.0" + jest-config "^26.6.3" + jest-util "^26.6.2" + jest-validate "^26.6.2" + prompts "^2.0.1" + yargs "^15.4.1" + +jest-config@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" + integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^26.6.3" + "@jest/types" "^26.6.2" + babel-jest "^26.6.3" + chalk "^4.0.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.4" + jest-environment-jsdom "^26.6.2" + jest-environment-node "^26.6.2" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.6.3" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + micromatch "^4.0.2" + pretty-format "^26.6.2" + +jest-diff@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + +jest-docblock@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" + integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== + dependencies: + detect-newline "^3.0.0" + +jest-each@^26.6.0, jest-each@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" + integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + jest-get-type "^26.3.0" + jest-util "^26.6.2" + pretty-format "^26.6.2" + +jest-environment-jsdom@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" + integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + jsdom "^16.4.0" + +jest-environment-node@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" + integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-jasmine2@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" + integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^26.6.2" + is-generator-fn "^2.0.0" + jest-each "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + pretty-format "^26.6.2" + throat "^5.0.0" + +jest-leak-detector@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" + integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== + dependencies: + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-matcher-utils@^26.6.0, jest-matcher-utils@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" + integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== + dependencies: + chalk "^4.0.0" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== + dependencies: + chalk "^4.0.0" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + +jest-message-util@^26.6.0, jest-message-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" + integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + pretty-format "^26.6.2" + slash "^3.0.0" + stack-utils "^2.0.2" + +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.5.0" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.5.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" + integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-resolve-dependencies@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" + integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== + dependencies: + "@jest/types" "^26.6.2" + jest-regex-util "^26.0.0" + jest-snapshot "^26.6.2" + +jest-resolve@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" + integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^26.6.2" + read-pkg-up "^7.0.1" + resolve "^1.18.1" + slash "^3.0.0" + +jest-runner@^26.6.0, jest-runner@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" + integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.7.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-docblock "^26.0.0" + jest-haste-map "^26.6.2" + jest-leak-detector "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + jest-runtime "^26.6.3" + jest-util "^26.6.2" + jest-worker "^26.6.2" + source-map-support "^0.5.6" + throat "^5.0.0" + +jest-runtime@^26.6.0, jest-runtime@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" + integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/globals" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + cjs-module-lexer "^0.6.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^15.4.1" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^26.6.0, jest-snapshot@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" + integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.6.2" + graceful-fs "^4.2.4" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + natural-compare "^1.4.0" + pretty-format "^26.6.2" + semver "^7.3.2" + +jest-util@^26.6.0, jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" + integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== + dependencies: + "@jest/types" "^26.6.2" + camelcase "^6.0.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" + leven "^3.1.0" + pretty-format "^26.6.2" + +jest-watch-typeahead@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.6.1.tgz#45221b86bb6710b7e97baaa1640ae24a07785e63" + integrity sha512-ITVnHhj3Jd/QkqQcTqZfRgjfyRhDFM/auzgVo2RKvSwi18YMvh0WvXDJFoFED6c7jd/5jxtu4kSOb9PTu2cPVg== + dependencies: + ansi-escapes "^4.3.1" + chalk "^4.0.0" + jest-regex-util "^26.0.0" + jest-watcher "^26.3.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + +jest-watcher@^26.3.0, jest-watcher@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" + integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== + dependencies: + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^26.6.2" + string-length "^4.0.1" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest@26.6.0: + version "26.6.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.0.tgz#546b25a1d8c888569dbbe93cae131748086a4a25" + integrity sha512-jxTmrvuecVISvKFFhOkjsWRZV7sFqdSUAd1ajOKY+/QE/aLBVstsJ/dX8GczLzwiT6ZEwwmZqtCUHLHHQVzcfA== + dependencies: + "@jest/core" "^26.6.0" + import-local "^3.0.2" + jest-cli "^26.6.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsdom@^16.4.0: + version "16.7.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.6" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" + integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== + dependencies: + object-visit "^1.0.0" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.1, minimist@^1.2.0: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mobx-react-lite@^3.4.0: + version "3.4.3" + resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz#3a4c22c30bfaa8b1b2aa48d12b2ba811c0947ab7" + integrity sha512-NkJREyFTSUXR772Qaai51BnE1voWx56LOL80xG7qkZr6vo8vEaLF3sz1JNUVh+rxmUzxYaqOhfuxTfqUh0FXUg== + +mobx-react@^7.5.2: + version "7.6.0" + resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-7.6.0.tgz#ebf0456728a9bd2e5c24fdcf9b36e285a222a7d6" + integrity sha512-+HQUNuh7AoQ9ZnU6c4rvbiVVl+wEkb9WqYsVDzGLng+Dqj1XntHu79PvEWKtSMoMj67vFp/ZPXcElosuJO8ckA== + dependencies: + mobx-react-lite "^3.4.0" + +mobx@^6.6.1: + version "6.9.0" + resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.9.0.tgz#8a894c26417c05bed2cf7499322e589ee9787397" + integrity sha512-HdKewQEREEJgsWnErClfbFoVebze6rGazxFLU/XUyrII8dORfVszN1V0BMRnQSzcgsNNtkX8DHj3nC6cdWE9YQ== + +moment@^2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-notifier@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" + integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== + dependencies: + growly "^1.3.0" + is-wsl "^2.2.0" + semver "^7.3.2" + shellwords "^0.1.1" + uuid "^8.3.0" + which "^2.0.2" + +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nwsapi@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" + integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== + dependencies: + isobject "^3.0.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +p-each-series@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pirates@^4.0.1: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== + +postcss@^8.4.27: + version "8.4.33" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" + integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + +pretty-format@^26.6.0, pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + +pretty-format@^29.0.0, pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== + dependencies: + "@jest/schemas" "^29.4.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +promise@^8.1.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" + integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== + dependencies: + asap "~2.0.6" + +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types-extra@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b" + integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew== + dependencies: + react-is "^16.3.2" + warning "^4.0.0" + +prop-types@^15.6.2, prop-types@^15.7.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +raf@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + +react-app-polyfill@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-2.0.0.tgz#a0bea50f078b8a082970a9d853dc34b6dcc6a3cf" + integrity sha512-0sF4ny9v/B7s6aoehwze9vJNWcmCemAUYBVasscVr92+UYiEqDXOxfKjXN685mDaMRNF3WdhHQs76oTODMocFA== + dependencies: + core-js "^3.6.5" + object-assign "^4.1.1" + promise "^8.1.0" + raf "^3.4.1" + regenerator-runtime "^0.13.7" + whatwg-fetch "^3.4.1" + +react-bootstrap@^1.6.5: + version "1.6.6" + resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-1.6.6.tgz#3f3b274f8923b9886008a0e61485b5ac9a2b3073" + integrity sha512-pSzYyJT5u4rc8+5myM8Vid2JG52L8AmYSkpznReH/GM4+FhLqEnxUa0+6HRTaGwjdEixQNGchwY+b3xCdYWrDA== + dependencies: + "@babel/runtime" "^7.14.0" + "@restart/context" "^2.1.4" + "@restart/hooks" "^0.4.7" + "@types/invariant" "^2.2.33" + "@types/prop-types" "^15.7.3" + "@types/react" ">=16.14.8" + "@types/react-transition-group" "^4.4.1" + "@types/warning" "^3.0.0" + classnames "^2.3.1" + dom-helpers "^5.2.1" + invariant "^2.2.4" + prop-types "^15.7.2" + prop-types-extra "^1.1.0" + react-overlays "^5.1.2" + react-transition-group "^4.4.1" + uncontrollable "^7.2.1" + warning "^4.0.3" + +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-icons@^4.8.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.8.0.tgz#621e900caa23b912f737e41be57f27f6b2bff445" + integrity sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg== + +react-is@^16.13.1, react-is@^16.3.2: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-overlays@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-5.2.1.tgz#49dc007321adb6784e1f212403f0fb37a74ab86b" + integrity sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA== + dependencies: + "@babel/runtime" "^7.13.8" + "@popperjs/core" "^2.11.6" + "@restart/hooks" "^0.4.7" + "@types/warning" "^3.0.0" + dom-helpers "^5.2.0" + prop-types "^15.7.2" + uncontrollable "^7.2.1" + warning "^4.0.3" + +react-refresh@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" + integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== + +react-router-dom@^6.3.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.10.0.tgz#090ddc5c84dc41b583ce08468c4007c84245f61f" + integrity sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg== + dependencies: + "@remix-run/router" "1.5.0" + react-router "6.10.0" + +react-router@6.10.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.10.0.tgz#230f824fde9dd0270781b5cb497912de32c0a971" + integrity sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ== + dependencies: + "@remix-run/router" "1.5.0" + +react-transition-group@^4.4.1: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.7: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== + +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.18.1, resolve@^1.19.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rollup@^3.27.1: + version "3.29.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981" + integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== + optionalDependencies: + fsevents "~2.3.2" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +sass@^1.56.0: + version "1.60.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.60.0.tgz#657f0c23a302ac494b09a5ba8497b739fb5b5a81" + integrity sha512-updbwW6fNb5gGm8qMXzVO7V4sWf7LMXnMly/JEyfbfERbVH46Fn6q02BX7/eHTdKpE7d+oTkMMQpFWNUMfFbgQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +"semver@2 || 3 || 4 || 5", semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.2: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.6: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.13" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" + integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-utils@^2.0.2, stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tough-cookie@^4.0.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== + dependencies: + punycode "^2.1.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +uncontrollable@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738" + integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ== + dependencies: + "@babel/runtime" "^7.6.3" + "@types/react" ">=16.9.11" + invariant "^2.2.4" + react-lifecycles-compat "^3.0.4" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +update-browserslist-db@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-to-istanbul@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" + integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vite@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.2.tgz#d6ea8610e099851dad8c7371599969e0f8b97e82" + integrity sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w== + dependencies: + esbuild "^0.18.10" + postcss "^8.4.27" + rollup "^3.27.1" + optionalDependencies: + fsevents "~2.3.2" + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +warning@^4.0.0, warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-fetch@^3.4.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" + integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.4" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" + integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.4.6: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" From fa292e557d6a5957b2c9396217af138ac1b82ef3 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:30:06 +0200 Subject: [PATCH 55/68] reporters: Deprecate non-generator arguments of GerritStatusPush --- master/buildbot/reporters/gerrit.py | 52 +++++++ .../test/unit/reporters/test_gerrit.py | 128 +++++++++++++++--- master/docs/manual/upgrading/4.0-upgrade.rst | 25 ++++ newsfragments/gerritstatuspush-args.removal | 2 + 4 files changed, 189 insertions(+), 18 deletions(-) create mode 100644 newsfragments/gerritstatuspush-args.removal diff --git a/master/buildbot/reporters/gerrit.py b/master/buildbot/reporters/gerrit.py index 9e606d840e43..4b9f7d5f9285 100644 --- a/master/buildbot/reporters/gerrit.py +++ b/master/buildbot/reporters/gerrit.py @@ -27,6 +27,7 @@ from twisted.python import log from zope.interface import implementer +from buildbot import config from buildbot import interfaces from buildbot.process.results import EXCEPTION from buildbot.process.results import FAILURE @@ -37,6 +38,7 @@ from buildbot.reporters import utils from buildbot.reporters.base import ReporterBase from buildbot.util import bytes2unicode +from buildbot.warnings import warn_deprecated # Cache the version that the gerrit server is running for this many seconds GERRIT_VERSION_CACHE_TIMEOUT = 600 @@ -371,6 +373,31 @@ def checkConfig( generators=None, **kwargs ): + old_arg_names = { + "reviewCB": reviewCB is not DEFAULT_REVIEW, + "startCB": startCB is not None, + "reviewArg": reviewArg is not None, + "startArg": startArg is not None, + "summaryCB": summaryCB is not DEFAULT_SUMMARY, + "summaryArg": summaryArg is not None, + "builders": builders is not None, + "wantSteps": wantSteps is not False, + "wantLogs": wantLogs is not False, + } + + passed_old_arg_names = [k for k, v in old_arg_names.items() if v] + + if passed_old_arg_names: + old_arg_names_msg = ', '.join(passed_old_arg_names) + if generators is not None: + config.error("can't specify generators and deprecated GerritStatusPush " + f"arguments ({old_arg_names_msg}) at the same time") + warn_deprecated( + "3.11.0", + f"The arguments {old_arg_names_msg} passed to {self.__class__.__name__} " + "have been deprecated. Use generators instead" + ) + if generators is None: generators = self._create_generators_from_old_args( reviewCB, @@ -579,6 +606,31 @@ def sendMessage(self, reports): if extra_info is not None: labels = extra_info.get("labels", None) + if labels is None and report.get("builds", None): + # At least one build + success = False + failure = False + pending = False + + for build in report["builds"]: + if build["results"] is None: + pending = True + elif build["results"] == SUCCESS: + success = True + else: + failure = True + + if failure: + verified = -1 + elif pending: + verified = 0 + elif success: + verified = 1 + else: + verified = -1 + + labels = {GERRIT_LABEL_VERIFIED: verified} + self.send_code_review(project, revision, report["body"], labels) return defer.succeed(None) diff --git a/master/buildbot/test/unit/reporters/test_gerrit.py b/master/buildbot/test/unit/reporters/test_gerrit.py index 1c52a8b2f7f3..b49f8b69b1e7 100644 --- a/master/buildbot/test/unit/reporters/test_gerrit.py +++ b/master/buildbot/test/unit/reporters/test_gerrit.py @@ -30,15 +30,21 @@ from buildbot.process.results import RETRY from buildbot.process.results import SUCCESS from buildbot.reporters import utils +from buildbot.reporters.generators.build import BuildStartEndStatusGenerator +from buildbot.reporters.generators.buildset import BuildSetStatusGenerator from buildbot.reporters.gerrit import GERRIT_LABEL_REVIEWED from buildbot.reporters.gerrit import GERRIT_LABEL_VERIFIED from buildbot.reporters.gerrit import GerritStatusPush from buildbot.reporters.gerrit import defaultReviewCB from buildbot.reporters.gerrit import defaultSummaryCB from buildbot.reporters.gerrit import makeReviewResult +from buildbot.reporters.message import MessageFormatterFunctionRaw +from buildbot.reporters.message import MessageFormatterRenderable from buildbot.test.fake import fakemaster from buildbot.test.reactor import TestReactorMixin from buildbot.test.util.reporter import ReporterTestMixin +from buildbot.test.util.warnings import assertProducesWarnings +from buildbot.warnings import DeprecatedApiWarning warnings.filterwarnings('error', message='.*Gerrit status') @@ -208,7 +214,8 @@ def run_fake_summary_build(self, gsp, buildResults, finalResult, @defer.inlineCallbacks def check_summary_build_deferred(self, buildResults, finalResult, resultText, verifiedScore): - gsp = yield self.setupGerritStatusPush(summaryCB=sampleSummaryCBDeferred) + with assertProducesWarnings(DeprecatedApiWarning, message_pattern="Use generators instead"): + gsp = yield self.setupGerritStatusPush(summaryCB=sampleSummaryCBDeferred) msg = yield self.run_fake_summary_build(gsp, buildResults, finalResult, resultText) @@ -223,7 +230,8 @@ def check_summary_build_deferred(self, buildResults, finalResult, resultText, @defer.inlineCallbacks def check_summary_build(self, buildResults, finalResult, resultText, verifiedScore): - gsp = yield self.setupGerritStatusPush(summaryCB=sampleSummaryCB) + with assertProducesWarnings(DeprecatedApiWarning, message_pattern="Use generators instead"): + gsp = yield self.setupGerritStatusPush(summaryCB=sampleSummaryCB) msg = yield self.run_fake_summary_build(gsp, buildResults, finalResult, resultText) @@ -238,7 +246,8 @@ def check_summary_build(self, buildResults, finalResult, resultText, @defer.inlineCallbacks def check_summary_build_legacy(self, buildResults, finalResult, resultText, verifiedScore): - gsp = yield self.setupGerritStatusPush(summaryCB=legacyTestSummaryCB) + with assertProducesWarnings(DeprecatedApiWarning, message_pattern="Use generators instead"): + gsp = yield self.setupGerritStatusPush(summaryCB=legacyTestSummaryCB) msg = yield self.run_fake_summary_build(gsp, buildResults, finalResult, resultText, expWarning=True) @@ -327,7 +336,9 @@ def test_buildsetComplete_mixed_sends_summary_review_legacy(self): ]) @defer.inlineCallbacks def test_buildset_complete_filtered_builder(self, name, builders, should_call): - gsp = yield self.setupGerritStatusPush(summaryCB=sampleSummaryCB, builders=builders) + with assertProducesWarnings(DeprecatedApiWarning, message_pattern="Use generators instead"): + gsp = yield self.setupGerritStatusPush(summaryCB=sampleSummaryCB, builders=builders) + yield self.run_fake_summary_build(gsp, [FAILURE, FAILURE], FAILURE, ["failed", "failed"]) @@ -335,9 +346,16 @@ def test_buildset_complete_filtered_builder(self, name, builders, should_call): @defer.inlineCallbacks def run_fake_single_build(self, gsp, buildResult, expWarning=False): - _, builds = yield self.setupBuildResults([buildResult], buildResult) + _, builds = yield self.setupBuildResults([None], None) yield gsp._got_event(('builds', builds[0]['buildid'], 'new'), builds[0]) + + yield self.master.db.builds.finishBuild(builds[0]["buildid"], buildResult) + yield self.master.db.buildsets.completeBuildset(98, buildResult) + + res = yield utils.getDetailsForBuildset(self.master, 98, want_properties=True) + builds = res['builds'] + yield gsp._got_event(('builds', builds[0]['buildid'], 'finished'), builds[0]) if expWarning: @@ -351,9 +369,10 @@ def run_fake_single_build(self, gsp, buildResult, expWarning=False): # same goes for check_single_build and check_single_build_legacy @defer.inlineCallbacks def check_single_build(self, buildResult, verifiedScore): - - gsp = yield self.setupGerritStatusPush(reviewCB=sampleReviewCB, - startCB=sampleStartCB) + with assertProducesWarnings(DeprecatedApiWarning, message_pattern="Use generators instead"): + gsp = yield self.setupGerritStatusPush( + reviewCB=sampleReviewCB, startCB=sampleStartCB + ) msg = yield self.run_fake_single_build(gsp, buildResult) calls = [ @@ -375,9 +394,9 @@ def check_single_build(self, buildResult, verifiedScore): # same goes for check_single_build and check_single_build_legacy @defer.inlineCallbacks def check_single_build_deferred(self, buildResult, verifiedScore): - - gsp = yield self.setupGerritStatusPush(reviewCB=sampleReviewCBDeferred, - startCB=sampleStartCBDeferred) + with assertProducesWarnings(DeprecatedApiWarning, message_pattern="Use generators instead"): + gsp = yield self.setupGerritStatusPush(reviewCB=sampleReviewCBDeferred, + startCB=sampleStartCBDeferred) msg = yield self.run_fake_single_build(gsp, buildResult) calls = [ @@ -398,8 +417,10 @@ def check_single_build_deferred(self, buildResult, verifiedScore): @defer.inlineCallbacks def check_single_build_legacy(self, buildResult, verifiedScore): - gsp = yield self.setupGerritStatusPush(reviewCB=legacyTestReviewCB, - startCB=sampleStartCB) + with assertProducesWarnings(DeprecatedApiWarning, message_pattern="Use generators instead"): + gsp = yield self.setupGerritStatusPush( + reviewCB=legacyTestReviewCB, startCB=sampleStartCB + ) msg = yield self.run_fake_single_build(gsp, buildResult, expWarning=True) calls = [ @@ -438,15 +459,86 @@ def test_buildComplete_failure_sends_review_legacy(self): @defer.inlineCallbacks def test_single_build_filtered(self, name, builders, should_call): - gsp = yield self.setupGerritStatusPush( - reviewCB=sampleReviewCB, - startCB=sampleStartCB, - builders=builders - ) + with assertProducesWarnings(DeprecatedApiWarning, message_pattern="Use generators instead"): + gsp = yield self.setupGerritStatusPush( + reviewCB=sampleReviewCB, + startCB=sampleStartCB, + builders=builders + ) yield self.run_fake_single_build(gsp, SUCCESS) self.assertEqual(gsp.send_code_review.called, should_call) + @parameterized.expand([ + ("success", SUCCESS, 1), + ("failure", FAILURE, -1), + ]) + @defer.inlineCallbacks + def test_single_build_generators(self, name, build_result, verified_score): + gsp = yield self.setupGerritStatusPush(generators=[BuildStartEndStatusGenerator()]) + + yield self.run_fake_single_build(gsp, build_result) + calls = [ + call( + self.reporter_test_project, + self.reporter_test_revision, + "Build started.", + {GERRIT_LABEL_VERIFIED: 0} + ), + call( + self.reporter_test_project, + self.reporter_test_revision, + "Build done.", + {GERRIT_LABEL_VERIFIED: verified_score} + ) + ] + gsp.send_code_review.assert_has_calls(calls) + + @parameterized.expand([ + ("success", SUCCESS, 1), + ("failure", FAILURE, -1), + ]) + @defer.inlineCallbacks + def test_single_buildset_generators(self, name, build_result, verified_score): + gsp = yield self.setupGerritStatusPush(generators=[ + BuildSetStatusGenerator(message_formatter=MessageFormatterRenderable("Build done.")) + ]) + + yield self.run_fake_summary_build(gsp, [build_result], build_result, "text") + calls = [ + call( + self.reporter_test_project, + self.reporter_test_revision, + "Build done.", + {GERRIT_LABEL_VERIFIED: verified_score} + ) + ] + gsp.send_code_review.assert_has_calls(calls) + + @defer.inlineCallbacks + def test_single_buildset_generators_override_label(self): + formatter = MessageFormatterFunctionRaw(lambda _, __: { + "body": "text1", + "type": "plain", + "subject": "sub1", + "extra_info": {"labels": {"Verified": -2}} + }) + + gsp = yield self.setupGerritStatusPush(generators=[ + BuildSetStatusGenerator(message_formatter=formatter) + ]) + + yield self.run_fake_summary_build(gsp, [SUCCESS], SUCCESS, "text") + calls = [ + call( + self.reporter_test_project, + self.reporter_test_revision, + "text1", + {GERRIT_LABEL_VERIFIED: -2} + ) + ] + gsp.send_code_review.assert_has_calls(calls) + def test_defaultReviewCBSuccess(self): res = defaultReviewCB("builderName", {}, SUCCESS, None, None) self.assertEqual(res['labels'], {'Verified': 1}) diff --git a/master/docs/manual/upgrading/4.0-upgrade.rst b/master/docs/manual/upgrading/4.0-upgrade.rst index 5582cf2f7b1a..733cecd4e3ab 100644 --- a/master/docs/manual/upgrading/4.0-upgrade.rst +++ b/master/docs/manual/upgrading/4.0-upgrade.rst @@ -79,6 +79,31 @@ The equivalent is setting both ``want_logs`` and ``want_logs_content`` to the pr The ``wantSteps`` and ``wantProperties`` arguments have been renamed to ``want_steps`` and ``want_properties`` respectively. +GerritStatusPush +---------------- + +The ``reviewCB``, ``reviewArg``, ``startCB``, ``startArg``, ``summaryCB``, ``summaryArg``, +``builders`` , ``wantSteps``, ``wantLogs`` arguments of ``GerritStatusPush`` have been deprecated. +The upgrade strategy is as follows: + + - ``reviewCB``, ``reviewArg``, ``startCB``, ``startArg``: + Use :bb:reportgen:`BuildStartEndStatusGenerator` report generator (``generators`` argument). + Depending on ``reviewCB`` complexity, use :ref:`MessageFormatter` or + :ref:`MessageFormatterFunctionRaw` message formatters. To override default handling of + ``Verified`` and ``Reviewed`` labels, adjust extra information emitted by message formatter. + E.g. ``{"labels": {"Verified": 1}}``. + + - ``summaryCB``, ``summaryArg``: + Use :bb:reportgen:`BuildSetStatusGenerator` or :bb:reportgen:`BuildSetCombinedStatusGenerator` + report generator (``generators`` argument). Depending on ``summaryCB`` complexity, + use :ref:`MessageFormatter` or :ref:`MessageFormatterFunctionRaw` message formatters. + To override default handling of ``Verified`` and ``Reviewed`` labels, adjust extra + information emitted by message formatter. E.g. ``{"labels": {"Verified": 1}}``. + + - ``builders`` - use ``builders`` argument of replacement report generator + - ``wantSteps`` - use ``want_steps`` argument of replacement message formatter. + - ``wantLogs`` - use ``want_logs`` argument of replacement message formatter + buildbot.util.croniter ---------------------- diff --git a/newsfragments/gerritstatuspush-args.removal b/newsfragments/gerritstatuspush-args.removal new file mode 100644 index 000000000000..8fea8be67423 --- /dev/null +++ b/newsfragments/gerritstatuspush-args.removal @@ -0,0 +1,2 @@ +The ``reviewCB``, ``reviewArg``, ``startCB``, ``startArg``, ``summaryCB``, ``summaryArg``, +``builders`` , ``wantSteps``, ``wantLogs`` arguments of ``GerritStatusPush`` have been deprecated. From 2921aa0ebb3515da33a8d0df25b47839686d2989 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Tue, 2 Jan 2024 04:30:08 +0200 Subject: [PATCH 56/68] reporters: Fix handling of buildsets without builds in GerritStatusPush --- master/buildbot/reporters/gerrit.py | 42 ++++++++++++------- .../test/unit/reporters/test_gerrit.py | 41 ++++++++++++++++++ 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/master/buildbot/reporters/gerrit.py b/master/buildbot/reporters/gerrit.py index 4b9f7d5f9285..0010db8e8737 100644 --- a/master/buildbot/reporters/gerrit.py +++ b/master/buildbot/reporters/gerrit.py @@ -132,14 +132,23 @@ class DEFAULT_SUMMARY: pass -def extract_project_revision(report): - build = report["builds"][0] +@defer.inlineCallbacks +def extract_project_revision(master, report): + props = None + if report["builds"]: + props = report["builds"][0].get("properties", None) + + if props is None: + props = yield master.data.get(("buildsets", report["buildset"]["bsid"], "properties")) + + def get_property(props, name): + if props is None: + return None + return props.get(name, [None])[0] - def getProperty(build, name): - return build['properties'].get(name, [None])[0] # Gerrit + Repo - downloads = getProperty(build, "repo_downloads") - downloaded = getProperty(build, "repo_downloaded") + downloads = get_property(props, "repo_downloads") + downloaded = get_property(props, "repo_downloaded") if downloads is not None and downloaded is not None: downloaded = downloaded.split(" ") if downloads and 2 * len(downloads) == len(downloaded): @@ -158,12 +167,14 @@ def getProperty(build, name): # Gerrit + Git # used only to verify Gerrit source - if getProperty(build, "event.change.id") is not None: - project = getProperty(build, "event.change.project") - codebase = getProperty(build, "codebase") - revision = (getProperty(build, "event.patchSet.revision") or - getProperty(build, "got_revision") or - getProperty(build, "revision")) + if get_property(props, "event.change.id") is not None: + project = get_property(props, "event.change.project") + codebase = get_property(props, "codebase") + revision = ( + get_property(props, "event.patchSet.revision") or + get_property(props, "got_revision") or + get_property(props, "revision") + ) if isinstance(revision, dict): # in case of the revision is a codebase revision, we just take @@ -593,13 +604,14 @@ def processEnded(self, reason): else: log.msg("gerrit status: OK") + @defer.inlineCallbacks def sendMessage(self, reports): report = reports[0] - project, revision = extract_project_revision(report) + project, revision = yield extract_project_revision(self.master, report) if report["body"] is None or project is None or revision is None: - return defer.succeed(None) + return None labels = None extra_info = report.get("extra_info", None) @@ -632,7 +644,7 @@ def sendMessage(self, reports): labels = {GERRIT_LABEL_VERIFIED: verified} self.send_code_review(project, revision, report["body"], labels) - return defer.succeed(None) + return None def send_code_review(self, project, revision, message, labels): gerrit_version = self.getCachedVersion() diff --git a/master/buildbot/test/unit/reporters/test_gerrit.py b/master/buildbot/test/unit/reporters/test_gerrit.py index b49f8b69b1e7..e39cf7428f88 100644 --- a/master/buildbot/test/unit/reporters/test_gerrit.py +++ b/master/buildbot/test/unit/reporters/test_gerrit.py @@ -37,9 +37,11 @@ from buildbot.reporters.gerrit import GerritStatusPush from buildbot.reporters.gerrit import defaultReviewCB from buildbot.reporters.gerrit import defaultSummaryCB +from buildbot.reporters.gerrit import extract_project_revision from buildbot.reporters.gerrit import makeReviewResult from buildbot.reporters.message import MessageFormatterFunctionRaw from buildbot.reporters.message import MessageFormatterRenderable +from buildbot.test import fakedb from buildbot.test.fake import fakemaster from buildbot.test.reactor import TestReactorMixin from buildbot.test.util.reporter import ReporterTestMixin @@ -646,3 +648,42 @@ def test_name_as_kwarg(self): def test_default_name(self): reporter = GerritStatusPush('gerrit.server.com', 'password') self.assertEqual(reporter.name, 'GerritStatusPush') + + @defer.inlineCallbacks + def test_extract_project_revision(self): + self.insert_test_data([SUCCESS], SUCCESS) + res = yield utils.getDetailsForBuildset(self.master, 98, want_properties=True) + report = {"builds": res["builds"], "buildset": res["buildset"]} + + project, revision = yield extract_project_revision(self.master, report) + self.assertEqual(project, "testProject") + self.assertEqual(revision, "d34db33fd43db33f") + + @defer.inlineCallbacks + def test_extract_project_revision_no_build(self): + self.insert_test_data([], SUCCESS) + self.db.insert_test_data( + [ + fakedb.BuildsetProperty( + buildsetid=98, + property_name="event.change.id", + property_value='["12345", "fakedb"]' + ), + fakedb.BuildsetProperty( + buildsetid=98, + property_name="event.change.project", + property_value='["project1", "fakedb"]' + ), + fakedb.BuildsetProperty( + buildsetid=98, + property_name="event.patchSet.revision", + property_value='["abcdabcd", "fakedb"]' + ), + ] + ) + res = yield utils.getDetailsForBuildset(self.master, 98, want_properties=True) + report = {"builds": res["builds"], "buildset": res["buildset"]} + + project, revision = yield extract_project_revision(self.master, report) + self.assertEqual(project, "project1") + self.assertEqual(revision, "abcdabcd") From 9b49bbf578a682e888410652b4b11227175199fb Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Thu, 25 Jan 2024 21:08:23 +0200 Subject: [PATCH 57/68] Release notes for 3.11.0 --- master/docs/relnotes/index.rst | 28 +++++++++++++++++++ .../add-react-wsgi-dashboards.feature | 2 -- ...ombined-buildsets-status-generator.feature | 2 -- .../db-get-source-stamps-for-buildset.feature | 3 -- newsfragments/gerritstatuspush-args.removal | 2 -- .../report-generator-reports-buildset.feature | 1 - newsfragments/reporter-extra-info.feature | 2 -- newsfragments/setup-python312-compat.bugfix | 1 - 8 files changed, 28 insertions(+), 13 deletions(-) delete mode 100644 newsfragments/add-react-wsgi-dashboards.feature delete mode 100644 newsfragments/combined-buildsets-status-generator.feature delete mode 100644 newsfragments/db-get-source-stamps-for-buildset.feature delete mode 100644 newsfragments/gerritstatuspush-args.removal delete mode 100644 newsfragments/report-generator-reports-buildset.feature delete mode 100644 newsfragments/reporter-extra-info.feature delete mode 100644 newsfragments/setup-python312-compat.bugfix diff --git a/master/docs/relnotes/index.rst b/master/docs/relnotes/index.rst index e336811c4668..658489775dfa 100644 --- a/master/docs/relnotes/index.rst +++ b/master/docs/relnotes/index.rst @@ -8,6 +8,34 @@ Release Notes .. towncrier release notes start +Buildbot ``3.11.0`` ( ``2024-01-25`` ) +====================================== + +Bug fixes +--------- + +- Declare Python 3.12 compatibility in generated packages of master and worker + +Features +-------- + +- Added a new WSGI dashboards plugin for React frontend. + It is backwards compatible with AngularJS one but may require changes in CSS styling of displayed web pages. +- Implemented a report generator (``BuildSetCombinedStatusGenerator``) that can access complete + information about a buildset. +- Low level database API now has ``get_sourcestamps_for_buildset`` to get source stamps for a + buildset. "/buildsets/:buildsetid/sourcestamps" endpoint has been added to access this from the + Data API. +- Added buildset information to dictionaries returned by report generators. +- Added a way to pass additional reporter-specific data to Reporters. Added ``extra_info_cb`` + argument to ``MessageFormatter`` for this use case. +- Implemented support for report generators in ``GerritStatusPush``. + +Deprecations and Removals +------------------------- + +- The ``reviewCB``, ``reviewArg``, ``startCB``, ``startArg``, ``summaryCB``, ``summaryArg``, + ``builders`` , ``wantSteps``, ``wantLogs`` arguments of ``GerritStatusPush`` have been deprecated. Buildbot ``3.10.1`` ( ``2023-12-26`` ) ====================================== diff --git a/newsfragments/add-react-wsgi-dashboards.feature b/newsfragments/add-react-wsgi-dashboards.feature deleted file mode 100644 index 492637ba5293..000000000000 --- a/newsfragments/add-react-wsgi-dashboards.feature +++ /dev/null @@ -1,2 +0,0 @@ -Added a new WSGI dashboards plugin for React frontend. -It is backwards compatible with AngularJS one but may require changes in CSS styling of displayed web pages. diff --git a/newsfragments/combined-buildsets-status-generator.feature b/newsfragments/combined-buildsets-status-generator.feature deleted file mode 100644 index 9484643a2fdb..000000000000 --- a/newsfragments/combined-buildsets-status-generator.feature +++ /dev/null @@ -1,2 +0,0 @@ -Implemented a report generator (``BuildSetCombinedStatusGenerator``) that can access complete -information about a buildset. diff --git a/newsfragments/db-get-source-stamps-for-buildset.feature b/newsfragments/db-get-source-stamps-for-buildset.feature deleted file mode 100644 index 0f7e4143f8aa..000000000000 --- a/newsfragments/db-get-source-stamps-for-buildset.feature +++ /dev/null @@ -1,3 +0,0 @@ -Low level database API now has ``get_sourcestamps_for_buildset`` to get source stamps for a -buildset. "/buildsets/:buildsetid/sourcestamps" endpoint has been added to access this from the -Data API. diff --git a/newsfragments/gerritstatuspush-args.removal b/newsfragments/gerritstatuspush-args.removal deleted file mode 100644 index 8fea8be67423..000000000000 --- a/newsfragments/gerritstatuspush-args.removal +++ /dev/null @@ -1,2 +0,0 @@ -The ``reviewCB``, ``reviewArg``, ``startCB``, ``startArg``, ``summaryCB``, ``summaryArg``, -``builders`` , ``wantSteps``, ``wantLogs`` arguments of ``GerritStatusPush`` have been deprecated. diff --git a/newsfragments/report-generator-reports-buildset.feature b/newsfragments/report-generator-reports-buildset.feature deleted file mode 100644 index 6c5a9d3a6e21..000000000000 --- a/newsfragments/report-generator-reports-buildset.feature +++ /dev/null @@ -1 +0,0 @@ -Added buildset information to dictionaries returned by report generators. diff --git a/newsfragments/reporter-extra-info.feature b/newsfragments/reporter-extra-info.feature deleted file mode 100644 index 5fad73feb789..000000000000 --- a/newsfragments/reporter-extra-info.feature +++ /dev/null @@ -1,2 +0,0 @@ -Added a way to pass additional reporter-specific data to Reporters. Added ``extra_info_cb`` -argument to ``MessageFormatter`` for this use case. diff --git a/newsfragments/setup-python312-compat.bugfix b/newsfragments/setup-python312-compat.bugfix deleted file mode 100644 index 19092013e17b..000000000000 --- a/newsfragments/setup-python312-compat.bugfix +++ /dev/null @@ -1 +0,0 @@ -setup: Declare Python3.12 compatibility in generated packages of master and worker From f97dbeb5ad9031920d556d399441612293c7e091 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Fri, 26 Jan 2024 16:24:21 +0200 Subject: [PATCH 58/68] bbtravis: Don't use sudo to call database preparation scripts The test image has been updated to not require sudo in https://github.com/buildbot/metabbotcfg/pull/140. --- .bbtravis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.bbtravis.yml b/.bbtravis.yml index e38d1247af1d..88582e26e3aa 100644 --- a/.bbtravis.yml +++ b/.bbtravis.yml @@ -135,10 +135,10 @@ install: before_script: # create real database for tests - condition: '"mysql" in BUILDBOT_TEST_DB_URL' - cmd: sudo /prepare_mysql + cmd: /prepare_mysql - condition: '"postgresql" in BUILDBOT_TEST_DB_URL' cmd: | - sudo /prepare_postgres + /prepare_postgres # for pg8000 driver we can't use peer authentication or empty password, so set a dummy password # This also serves as a way to wait that the database is ready while ! psql -d bbtest -c 'ALTER USER "buildbot" WITH PASSWORD '"'x'"';' ; do sleep 1 ; done From b841a08e205f939a871b70c9e1de1fd40a3941e3 Mon Sep 17 00:00:00 2001 From: Thomas Desveaux Date: Fri, 2 Feb 2024 11:00:59 +0100 Subject: [PATCH 59/68] gitpoller: ensure ssh private key has trailing new line Fixes #7347 --- master/buildbot/changes/gitpoller.py | 3 ++- .../test/unit/changes/test_gitpoller.py | 17 ++++++++--------- .../gitpoller-ssh-private-newline.bugfix | 1 + 3 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 newsfragments/gitpoller-ssh-private-newline.bugfix diff --git a/master/buildbot/changes/gitpoller.py b/master/buildbot/changes/gitpoller.py index ec4e2044dffa..2f9ee388b163 100644 --- a/master/buildbot/changes/gitpoller.py +++ b/master/buildbot/changes/gitpoller.py @@ -27,6 +27,7 @@ from buildbot.util import private_tempdir from buildbot.util import runprocess from buildbot.util.git import GitMixin +from buildbot.util.git import ensureSshKeyNewline from buildbot.util.git import getSshKnownHostsContents from buildbot.util.misc import writeLocalFile from buildbot.util.state import StateMixin @@ -423,7 +424,7 @@ def _downloadSshPrivateKey(self, keyPath): # We change the permissions of the key file to be user-readable only so # that ssh does not complain. This is not used for security because the # parent directory will have proper permissions. - writeLocalFile(keyPath, self.sshPrivateKey, mode=stat.S_IRUSR) + writeLocalFile(keyPath, ensureSshKeyNewline(self.sshPrivateKey), mode=stat.S_IRUSR) def _downloadSshKnownHosts(self, path): if self.sshKnownHosts is not None: diff --git a/master/buildbot/test/unit/changes/test_gitpoller.py b/master/buildbot/test/unit/changes/test_gitpoller.py index a72ca102b4ab..1901c9adeec0 100644 --- a/master/buildbot/test/unit/changes/test_gitpoller.py +++ b/master/buildbot/test/unit/changes/test_gitpoller.py @@ -1567,7 +1567,7 @@ def test_poll_initial_2_10(self, write_local_file_mock, temp_dir_mock): self.assertEqual(temp_dir_mock.dirs, [(temp_dir_path, 0o700), (temp_dir_path, 0o700)]) - write_local_file_mock.assert_called_with(key_path, 'ssh-key', + write_local_file_mock.assert_called_with(key_path, 'ssh-key\n', mode=0o400) @mock.patch('buildbot.util.private_tempdir.PrivateTemporaryDirectory', @@ -1611,7 +1611,7 @@ def test_poll_initial_2_3(self, write_local_file_mock, temp_dir_mock): self.assertEqual(temp_dir_mock.dirs, [(temp_dir_path, 0o700), (temp_dir_path, 0o700)]) - write_local_file_mock.assert_called_with(key_path, 'ssh-key', + write_local_file_mock.assert_called_with(key_path, 'ssh-key\n', mode=0o400) @mock.patch('buildbot.util.private_tempdir.PrivateTemporaryDirectory', @@ -1647,7 +1647,7 @@ def test_poll_failFetch_git_2_10(self, write_local_file_mock, self.assertEqual(temp_dir_mock.dirs, [(temp_dir_path, 0o700), (temp_dir_path, 0o700)]) - write_local_file_mock.assert_called_with(key_path, 'ssh-key', + write_local_file_mock.assert_called_with(key_path, 'ssh-key\n', mode=0o400) @@ -1706,9 +1706,9 @@ def test_poll_initial_2_10(self, write_local_file_mock, temp_dir_mock): (temp_dir_path, 0o700)]) expected_file_writes = [ - mock.call(key_path, 'ssh-key', mode=0o400), + mock.call(key_path, 'ssh-key\n', mode=0o400), mock.call(known_hosts_path, '* ssh-host-key'), - mock.call(key_path, 'ssh-key', mode=0o400), + mock.call(key_path, 'ssh-key\n', mode=0o400), mock.call(known_hosts_path, '* ssh-host-key'), ] @@ -1717,9 +1717,8 @@ def test_poll_initial_2_10(self, write_local_file_mock, temp_dir_mock): class TestGitPollerWithSshKnownHosts(TestGitPollerBase): - def createPoller(self): - return gitpoller.GitPoller(self.REPOURL, sshPrivateKey='ssh-key', + return gitpoller.GitPoller(self.REPOURL, sshPrivateKey='ssh-key\n', sshKnownHosts='ssh-known-hosts') @mock.patch('buildbot.util.private_tempdir.PrivateTemporaryDirectory', @@ -1771,9 +1770,9 @@ def test_poll_initial_2_10(self, write_local_file_mock, temp_dir_mock): (temp_dir_path, 0o700)]) expected_file_writes = [ - mock.call(key_path, 'ssh-key', mode=0o400), + mock.call(key_path, 'ssh-key\n', mode=0o400), mock.call(known_hosts_path, 'ssh-known-hosts'), - mock.call(key_path, 'ssh-key', mode=0o400), + mock.call(key_path, 'ssh-key\n', mode=0o400), mock.call(known_hosts_path, 'ssh-known-hosts'), ] diff --git a/newsfragments/gitpoller-ssh-private-newline.bugfix b/newsfragments/gitpoller-ssh-private-newline.bugfix new file mode 100644 index 000000000000..02ebc5b61f4c --- /dev/null +++ b/newsfragments/gitpoller-ssh-private-newline.bugfix @@ -0,0 +1 @@ +``GitPoller`` now ensure the SSH Private Key it uses has a trailing newline. \ No newline at end of file From 5d9d447ac8657e080d7de7f2606e8026dac349b9 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Tue, 12 Dec 2023 01:07:32 -0500 Subject: [PATCH 60/68] migrate from python-future to six The python-future library makes some odd decisions and also does not work with modern python, ironically. The six portability library does a much more minimal and elegant handling of all the actually-needed functionality in a significantly smaller codebase. It is also much more popular and well-supported by tools. For example, `pyupgrade --py3-only` can automatically upgrade outdated six.moves imports and switch six.* functionality to the canonical python3 version. Fixes #6980 --- worker/buildbot_worker/commands/utils.py | 3 ++- worker/buildbot_worker/compat.py | 3 ++- worker/buildbot_worker/pbutil.py | 3 ++- worker/buildbot_worker/runprocess.py | 9 +++++---- worker/buildbot_worker/scripts/windows_service.py | 3 ++- worker/buildbot_worker/test/unit/test_bot.py | 3 ++- worker/buildbot_worker/test/util/misc.py | 13 ++++--------- worker/buildbot_worker/util/__init__.py | 4 ++-- worker/setup.py | 2 +- 9 files changed, 22 insertions(+), 21 deletions(-) diff --git a/worker/buildbot_worker/commands/utils.py b/worker/buildbot_worker/commands/utils.py index 148395602c43..8b2b9c4a76a3 100644 --- a/worker/buildbot_worker/commands/utils.py +++ b/worker/buildbot_worker/commands/utils.py @@ -15,10 +15,11 @@ from __future__ import absolute_import from __future__ import print_function -from future.utils import text_type import os +from six import text_type + from twisted.python import log from twisted.python import runtime from twisted.python.procutils import which diff --git a/worker/buildbot_worker/compat.py b/worker/buildbot_worker/compat.py index e9396d03899c..2da615c74bd6 100644 --- a/worker/buildbot_worker/compat.py +++ b/worker/buildbot_worker/compat.py @@ -21,7 +21,8 @@ from __future__ import absolute_import from __future__ import print_function -from future.utils import text_type + +from six import text_type if str != bytes: # On Python 3 and higher, str and bytes diff --git a/worker/buildbot_worker/pbutil.py b/worker/buildbot_worker/pbutil.py index 8202b25607c8..bbf54b10b3ba 100644 --- a/worker/buildbot_worker/pbutil.py +++ b/worker/buildbot_worker/pbutil.py @@ -19,7 +19,8 @@ from __future__ import absolute_import from __future__ import print_function -from future.utils import iteritems + +from six import iteritems from twisted.application.internet import backoffPolicy from twisted.cred import error diff --git a/worker/buildbot_worker/runprocess.py b/worker/buildbot_worker/runprocess.py index 391d23faab1d..acd55f9ce014 100644 --- a/worker/buildbot_worker/runprocess.py +++ b/worker/buildbot_worker/runprocess.py @@ -19,10 +19,6 @@ from __future__ import absolute_import from __future__ import print_function -from future.utils import PY3 -from future.utils import iteritems -from future.utils import string_types -from future.utils import text_type import os import pprint @@ -35,6 +31,11 @@ from codecs import getincrementaldecoder from tempfile import NamedTemporaryFile +from six import PY3 +from six import iteritems +from six import string_types +from six import text_type + from twisted.internet import defer from twisted.internet import error from twisted.internet import protocol diff --git a/worker/buildbot_worker/scripts/windows_service.py b/worker/buildbot_worker/scripts/windows_service.py index 32173318eb43..97c3b0b6ae89 100755 --- a/worker/buildbot_worker/scripts/windows_service.py +++ b/worker/buildbot_worker/scripts/windows_service.py @@ -67,13 +67,14 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from future.builtins import range import os import sys import threading from contextlib import contextmanager +from six.moves import range + import pywintypes import servicemanager import win32api diff --git a/worker/buildbot_worker/test/unit/test_bot.py b/worker/buildbot_worker/test/unit/test_bot.py index 459a038c05db..2f4d717a377e 100644 --- a/worker/buildbot_worker/test/unit/test_bot.py +++ b/worker/buildbot_worker/test/unit/test_bot.py @@ -15,12 +15,13 @@ from __future__ import absolute_import from __future__ import print_function -from future.builtins import range import multiprocessing import os import shutil +from six.moves import range + from twisted.internet import defer from twisted.internet import reactor from twisted.internet import task diff --git a/worker/buildbot_worker/test/util/misc.py b/worker/buildbot_worker/test/util/misc.py index cd75972054dd..1c808d37c2c4 100644 --- a/worker/buildbot_worker/test/util/misc.py +++ b/worker/buildbot_worker/test/util/misc.py @@ -20,8 +20,6 @@ from __future__ import absolute_import from __future__ import print_function -from future.utils import PY3 -from future.utils import string_types import errno import os @@ -31,6 +29,10 @@ from io import BytesIO from io import StringIO +from six import PY3 +from six import string_types +from six.moves import builtins + from twisted.python import log from buildbot_worker.scripts import base @@ -40,13 +42,6 @@ except ImportError: import mock -try: - # Python 2 - import __builtin__ as builtins -except ImportError: - # Python 3 - import builtins - def nl(s): """Convert the given string to the native newline format, assuming it is diff --git a/worker/buildbot_worker/util/__init__.py b/worker/buildbot_worker/util/__init__.py index 5e44d3439be2..f881d65b06fb 100644 --- a/worker/buildbot_worker/util/__init__.py +++ b/worker/buildbot_worker/util/__init__.py @@ -13,12 +13,12 @@ # # Copyright Buildbot Team Members -from future.utils import text_type - import itertools import textwrap import time +from six import text_type + from ._hangcheck import HangCheckFactory from ._notifier import Notifier diff --git a/worker/setup.py b/worker/setup.py index 10ba634295bf..6b1f6531f4a9 100755 --- a/worker/setup.py +++ b/worker/setup.py @@ -152,7 +152,7 @@ def make_release_tree(self, base_dir, files): if setuptools is not None: setup_args['install_requires'] = [ 'twisted ' + twisted_ver, - 'future', + 'six', ] if sys.version_info >= (3, 6): From 6b6d58d9a2580ccd122aae00baadb83bf90f4fed Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Wed, 31 Jan 2024 23:39:14 +0200 Subject: [PATCH 61/68] Add newsfragment --- newsfragments/python-future-to-six.bugfix | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 newsfragments/python-future-to-six.bugfix diff --git a/newsfragments/python-future-to-six.bugfix b/newsfragments/python-future-to-six.bugfix new file mode 100644 index 000000000000..cccdcd1f6489 --- /dev/null +++ b/newsfragments/python-future-to-six.bugfix @@ -0,0 +1,2 @@ +Migrated off python-future which prevented installing Buildbot on distributions that do not provide +that package. From ca9acecccbedc9180926e7cb4df6c5d05bfdf214 Mon Sep 17 00:00:00 2001 From: Monika Kairaityte Date: Sun, 11 Feb 2024 22:28:41 +0200 Subject: [PATCH 62/68] BuildView: Recalculate build value after buildsQuery.array update useEffect() setup function uses old value of build when buildsQuery.array is updated. Therefore, build value is calculated right in the setup function to make sure that the newest value from updated buildsQuery.array is used. --- newsfragments/use_updated_build_value.bugfix | 1 + www/react-base/src/views/BuildView/BuildView.tsx | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 newsfragments/use_updated_build_value.bugfix diff --git a/newsfragments/use_updated_build_value.bugfix b/newsfragments/use_updated_build_value.bugfix new file mode 100644 index 000000000000..0a3b7a5500e8 --- /dev/null +++ b/newsfragments/use_updated_build_value.bugfix @@ -0,0 +1 @@ +Fix sporadic navigation to builders page when new build is started (:issue:`7307`). diff --git a/www/react-base/src/views/BuildView/BuildView.tsx b/www/react-base/src/views/BuildView/BuildView.tsx index 01d942538884..e5e757ef340a 100644 --- a/www/react-base/src/views/BuildView/BuildView.tsx +++ b/www/react-base/src/views/BuildView/BuildView.tsx @@ -181,6 +181,8 @@ const BuildView = observer(() => { const project = projectsQuery.getNthOrNull(0); useEffect(() => { + // note that in case buildsQuery.array was updated, we have to recalculate build value + const build = findOrNull(buildsQuery.array, b => b.number === buildnumber); if (buildsQuery.resolved && build === null) { navigate(`/builders/${builderid}`); } From 135618e5ee101d30697fb4b3a9d797decc807d39 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sat, 24 Feb 2024 04:11:08 +0200 Subject: [PATCH 63/68] Release notes for 3.11.1 --- master/docs/relnotes/index.rst | 13 +++++++++++++ newsfragments/gitpoller-ssh-private-newline.bugfix | 1 - newsfragments/python-future-to-six.bugfix | 2 -- newsfragments/use_updated_build_value.bugfix | 1 - 4 files changed, 13 insertions(+), 4 deletions(-) delete mode 100644 newsfragments/gitpoller-ssh-private-newline.bugfix delete mode 100644 newsfragments/python-future-to-six.bugfix delete mode 100644 newsfragments/use_updated_build_value.bugfix diff --git a/master/docs/relnotes/index.rst b/master/docs/relnotes/index.rst index 658489775dfa..a8e13cd05195 100644 --- a/master/docs/relnotes/index.rst +++ b/master/docs/relnotes/index.rst @@ -8,6 +8,19 @@ Release Notes .. towncrier release notes start + +Buildbot ``3.11.1`` ( ``2024-02-24`` ) +====================================== + +Bug fixes +--------- + +- ``GitPoller`` now ensures the SSH Private Key it uses has a trailing newline. +- Migrated off python-future which prevented installing Buildbot on distributions that do not provide + that package. +- Fix sporadic navigation to builders page when new build is started (:issue:`7307`). + + Buildbot ``3.11.0`` ( ``2024-01-25`` ) ====================================== diff --git a/newsfragments/gitpoller-ssh-private-newline.bugfix b/newsfragments/gitpoller-ssh-private-newline.bugfix deleted file mode 100644 index 02ebc5b61f4c..000000000000 --- a/newsfragments/gitpoller-ssh-private-newline.bugfix +++ /dev/null @@ -1 +0,0 @@ -``GitPoller`` now ensure the SSH Private Key it uses has a trailing newline. \ No newline at end of file diff --git a/newsfragments/python-future-to-six.bugfix b/newsfragments/python-future-to-six.bugfix deleted file mode 100644 index cccdcd1f6489..000000000000 --- a/newsfragments/python-future-to-six.bugfix +++ /dev/null @@ -1,2 +0,0 @@ -Migrated off python-future which prevented installing Buildbot on distributions that do not provide -that package. diff --git a/newsfragments/use_updated_build_value.bugfix b/newsfragments/use_updated_build_value.bugfix deleted file mode 100644 index 0a3b7a5500e8..000000000000 --- a/newsfragments/use_updated_build_value.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix sporadic navigation to builders page when new build is started (:issue:`7307`). From bd7aedad554cc101673923ea6d0e768962224a8b Mon Sep 17 00:00:00 2001 From: "Yngve N. Pettersen" Date: Wed, 28 Feb 2024 10:17:00 +0100 Subject: [PATCH 64/68] Update Makefile to handle Windows paths and Python --- Makefile | 29 +++++++++++++++-------- newsfragments/makefile-for-windows.bugfix | 1 + 2 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 newsfragments/makefile-for-windows.bugfix diff --git a/Makefile b/Makefile index 46c23521d70d..6346fe8d5cfa 100644 --- a/Makefile +++ b/Makefile @@ -4,11 +4,19 @@ ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) .PHONY: docs pylint flake8 virtualenv +ifeq ($(OS),Windows_NT) + VENV_BIN_DIR := Scripts + VENV_PY_VERSION ?= python + VENV_CREATE := python -m venv +else + VENV_BIN_DIR := bin + VENV_PY_VERSION ?= python3 + VENV_CREATE := virtualenv -p $(VENV_PY_VERSION) +endif VENV_NAME := .venv$(VENV_PY_VERSION) -PIP ?= $(ROOT_DIR)/$(VENV_NAME)/bin/pip -PYTHON ?= $(ROOT_DIR)/$(VENV_NAME)/bin/python -VENV_PY_VERSION ?= python3 +PIP ?= $(ROOT_DIR)/$(VENV_NAME)/$(VENV_BIN_DIR)/pip +VENV_PYTHON ?= $(ROOT_DIR)/$(VENV_NAME)/$(VENV_BIN_DIR)/python YARN := $(shell which yarnpkg || which yarn) WWW_PKGS := www/base www/react-base www/console_view www/react-console_view www/grid_view www/react-grid_view www/waterfall_view www/react-waterfall_view www/wsgi_dashboards www/badges @@ -84,7 +92,7 @@ frontend: frontend_deps # build frontend wheels for installation elsewhere frontend_wheels: frontend_deps for i in pkg $(WWW_PKGS); \ - do (cd $$i; $(PYTHON) setup.py bdist_wheel || exit 1) || exit 1; done + do (cd $$i; $(VENV_PYTHON) setup.py bdist_wheel || exit 1) || exit 1; done # do installation tests. Test front-end can build and install for all install methods frontend_install_tests: frontend_deps @@ -115,8 +123,9 @@ docker-buildbot-master: $(DOCKERBUILD) -t buildbot/buildbot-master:master master $(VENV_NAME): - virtualenv -p $(VENV_PY_VERSION) $(VENV_NAME) - $(PIP) install -U pip setuptools wheel + $(VENV_CREATE) $(VENV_NAME) + $(VENV_PYTHON) -m pip install --upgrade pip + $(PIP) install -U setuptools wheel # helper for virtualenv creation virtualenv: $(VENV_NAME) # usage: make virtualenv VENV_PY_VERSION=python3.4 @@ -125,13 +134,13 @@ virtualenv: $(VENV_NAME) # usage: make virtualenv VENV_PY_VERSION=python3.4 -r requirements-cidocs.txt \ packaging towncrier @echo now you can type following command to activate your virtualenv - @echo . $(VENV_NAME)/bin/activate + @echo . $(VENV_NAME)/$(VENV_BIN_DIR)/activate TRIALOPTS?=buildbot .PHONY: trial trial: virtualenv - . $(VENV_NAME)/bin/activate && trial $(TRIALOPTS) + . $(VENV_NAME)/$(VENV_BIN_DIR)/activate && trial $(TRIALOPTS) release_notes: $(VENV_NAME) test ! -z "$(VERSION)" # usage: make release_notes VERSION=0.9.2 @@ -139,7 +148,7 @@ release_notes: $(VENV_NAME) git commit -m "Release notes for $(VERSION)" $(ALL_PKGS_TARGETS): cleanup_for_tarballs frontend_deps - . $(VENV_NAME)/bin/activate && ./common/maketarball.sh $(patsubst %_pkg,%,$@) + . $(VENV_NAME)/$(VENV_BIN_DIR)/activate && ./common/maketarball.sh $(patsubst %_pkg,%,$@) cleanup_for_tarballs: find master pkg worker www -name VERSION -exec rm {} \; @@ -171,4 +180,4 @@ finishrelease: pyinstaller: virtualenv $(PIP) install pyinstaller - $(VENV_NAME)/bin/pyinstaller pyinstaller/buildbot-worker.spec + $(VENV_NAME)/$(VENV_BIN_DIR)/pyinstaller pyinstaller/buildbot-worker.spec diff --git a/newsfragments/makefile-for-windows.bugfix b/newsfragments/makefile-for-windows.bugfix new file mode 100644 index 000000000000..9aefb7afcf03 --- /dev/null +++ b/newsfragments/makefile-for-windows.bugfix @@ -0,0 +1 @@ +Update `Makefile` to handle Windows paths and Python. From 0ddf95fefb1a3b1b2bf764d02b857a227e39c14f Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Fri, 19 Apr 2024 17:18:20 +0300 Subject: [PATCH 65/68] test: Remove very short test timeout overrides When running tests on oversubscribed hosts the variability is high enough that there's small probability of exceeding short timeouts even in simpliest tests. --- master/buildbot/test/unit/steps/test_http.py | 3 --- master/buildbot/test/unit/test_mq.py | 1 - master/buildbot/test/unit/test_mq_wamp.py | 3 --- 3 files changed, 7 deletions(-) diff --git a/master/buildbot/test/unit/steps/test_http.py b/master/buildbot/test/unit/steps/test_http.py index a9786b21fa28..a559c26848a9 100644 --- a/master/buildbot/test/unit/steps/test_http.py +++ b/master/buildbot/test/unit/steps/test_http.py @@ -62,9 +62,6 @@ def render_POST(self, request): class TestHTTPStep(TestBuildStepMixin, TestReactorMixin, unittest.TestCase): - - timeout = 3 # those tests should not run long - def setUp(self): self.setup_test_reactor() if txrequests is None: diff --git a/master/buildbot/test/unit/test_mq.py b/master/buildbot/test/unit/test_mq.py index df1094f90830..7ba720f2bbec 100644 --- a/master/buildbot/test/unit/test_mq.py +++ b/master/buildbot/test/unit/test_mq.py @@ -130,7 +130,6 @@ def test_waitUntilEvent_check_false(self): self.assertEqual(d.called, True) res = yield d self.assertEqual(res, (('abc',), {"x": 1})) - timeout = 3 # those tests should not run long class TestFakeMQ(TestReactorMixin, unittest.TestCase, Tests): diff --git a/master/buildbot/test/unit/test_mq_wamp.py b/master/buildbot/test/unit/test_mq_wamp.py index 47c26f91486d..819e920ac4fd 100644 --- a/master/buildbot/test/unit/test_mq_wamp.py +++ b/master/buildbot/test/unit/test_mq_wamp.py @@ -244,9 +244,6 @@ class WampMQReal(TestReactorMixin, unittest.TestCase): > crossbar start & > export WAMP_ROUTER_URL=ws://localhost:8080/ws > trial buildbot.unit.test_mq_wamp""") - # if connection is bad, this test can timeout easily - # we reduce the timeout to help maintain the sanity of the developer - timeout = 2 @defer.inlineCallbacks def setUp(self): From 358f87df0a6e0e8a32adad381788e2db9ffd8cc8 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Fri, 19 Apr 2024 17:18:21 +0300 Subject: [PATCH 66/68] test: Increase timeout of all RunMasterBase tests --- master/buildbot/test/util/integration.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/master/buildbot/test/util/integration.py b/master/buildbot/test/util/integration.py index c33c0c1fb653..8b1e7d2a7b35 100644 --- a/master/buildbot/test/util/integration.py +++ b/master/buildbot/test/util/integration.py @@ -180,6 +180,10 @@ def on_finished(_, __): class RunMasterBase(unittest.TestCase): proto = "null" + # All tests that start master need higher timeout due to test runtime variability on + # oversubscribed hosts. + timeout = 20 + if Worker is None: skip = "buildbot-worker package is not installed" From 5d70ec0195af4bc766187f950291ae50014068bf Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Thu, 4 Apr 2024 19:50:38 +0300 Subject: [PATCH 67/68] www: Extract settings fields to separate components --- .../SettingsView/Fields/FieldBoolean.tsx | 37 ++++++++++++++++++ .../views/SettingsView/Fields/FieldFloat.tsx | 35 +++++++++++++++++ .../SettingsView/Fields/FieldInteger.tsx | 35 +++++++++++++++++ .../views/SettingsView/Fields/FieldString.tsx | 35 +++++++++++++++++ .../src/views/SettingsView/SettingsView.tsx | 39 ++++++------------- 5 files changed, 154 insertions(+), 27 deletions(-) create mode 100644 www/react-base/src/views/SettingsView/Fields/FieldBoolean.tsx create mode 100644 www/react-base/src/views/SettingsView/Fields/FieldFloat.tsx create mode 100644 www/react-base/src/views/SettingsView/Fields/FieldInteger.tsx create mode 100644 www/react-base/src/views/SettingsView/Fields/FieldString.tsx diff --git a/www/react-base/src/views/SettingsView/Fields/FieldBoolean.tsx b/www/react-base/src/views/SettingsView/Fields/FieldBoolean.tsx new file mode 100644 index 000000000000..d8d4e303e51c --- /dev/null +++ b/www/react-base/src/views/SettingsView/Fields/FieldBoolean.tsx @@ -0,0 +1,37 @@ +/* + This file is part of Buildbot. Buildbot is free software: you can + redistribute it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 51 + Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Copyright Buildbot Team Members +*/ + +import {observer} from "mobx-react"; +import {SettingItem, SettingValue} from "../../../plugins/GlobalSettings"; + +type FieldBooleanProps = { + item: SettingItem; + setSetting: (value: SettingValue) => void +}; + +export const FieldBoolean = observer(({item, setSetting}: FieldBooleanProps) => { + return ( +
+ +
+ ); +}); \ No newline at end of file diff --git a/www/react-base/src/views/SettingsView/Fields/FieldFloat.tsx b/www/react-base/src/views/SettingsView/Fields/FieldFloat.tsx new file mode 100644 index 000000000000..334d0b1ec830 --- /dev/null +++ b/www/react-base/src/views/SettingsView/Fields/FieldFloat.tsx @@ -0,0 +1,35 @@ +/* + This file is part of Buildbot. Buildbot is free software: you can + redistribute it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 51 + Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Copyright Buildbot Team Members +*/ + +import {observer} from "mobx-react"; +import {SettingItem, SettingValue} from "../../../plugins/GlobalSettings"; + +type FieldFloatProps = { + item: SettingItem; + setSetting: (value: SettingValue) => void +}; + +export const FieldFloat = observer(({item, setSetting}: FieldFloatProps) => { + return ( +
+ + setSetting(event.target.value)}/> +
+ ); +}); \ No newline at end of file diff --git a/www/react-base/src/views/SettingsView/Fields/FieldInteger.tsx b/www/react-base/src/views/SettingsView/Fields/FieldInteger.tsx new file mode 100644 index 000000000000..3c7e064c52b9 --- /dev/null +++ b/www/react-base/src/views/SettingsView/Fields/FieldInteger.tsx @@ -0,0 +1,35 @@ +/* + This file is part of Buildbot. Buildbot is free software: you can + redistribute it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 51 + Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Copyright Buildbot Team Members +*/ + +import {observer} from "mobx-react"; +import {SettingItem, SettingValue} from "../../../plugins/GlobalSettings"; + +type FieldIntegerProps = { + item: SettingItem; + setSetting: (value: SettingValue) => void +}; + +export const FieldInteger = observer(({item, setSetting}: FieldIntegerProps) => { + return ( +
+ + setSetting(event.target.value)}/> +
+ ); +}); \ No newline at end of file diff --git a/www/react-base/src/views/SettingsView/Fields/FieldString.tsx b/www/react-base/src/views/SettingsView/Fields/FieldString.tsx new file mode 100644 index 000000000000..ff9bb9017c3a --- /dev/null +++ b/www/react-base/src/views/SettingsView/Fields/FieldString.tsx @@ -0,0 +1,35 @@ +/* + This file is part of Buildbot. Buildbot is free software: you can + redistribute it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 51 + Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Copyright Buildbot Team Members +*/ + +import {observer} from "mobx-react"; +import {SettingItem, SettingValue} from "../../../plugins/GlobalSettings"; + +type FieldStringProps = { + item: SettingItem; + setSetting: (value: SettingValue) => void +}; + +export const FieldString = observer(({item, setSetting}: FieldStringProps) => { + return ( +
+ + setSetting(event.target.value)}/> +
+ ); +}); \ No newline at end of file diff --git a/www/react-base/src/views/SettingsView/SettingsView.tsx b/www/react-base/src/views/SettingsView/SettingsView.tsx index 720450e9c92a..d0035366fcbb 100644 --- a/www/react-base/src/views/SettingsView/SettingsView.tsx +++ b/www/react-base/src/views/SettingsView/SettingsView.tsx @@ -25,6 +25,10 @@ import { SettingGroup, SettingItem, SettingValue } from "../../plugins/GlobalSettings"; +import {FieldBoolean} from "./Fields/FieldBoolean"; +import {FieldFloat} from "./Fields/FieldFloat"; +import {FieldInteger} from "./Fields/FieldInteger"; +import {FieldString} from "./Fields/FieldString"; const computeMasterCfgSnippet = (settings: GlobalSettings) => { let code = "c['www']['ui_default_config'] = { \n"; @@ -58,38 +62,19 @@ export const SettingsView = observer(() => { }; if (item.type === 'boolean') { - return ( -
- -
- ); + return ; } - if (item.type === 'integer' || item.type === 'float') { - return ( -
- - setSetting(event.target.value)}/> -
- ); + if (item.type === 'integer') { + return + } + + if (item.type === 'float') { + return } if (item.type === 'string') { - return ( -
- - setSetting(event.target.value)}/> -
- ); + return ; } return (
From d21f2670033ced83e879d2b5c958d044679e04a9 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Wed, 17 Apr 2024 18:20:55 +0300 Subject: [PATCH 68/68] www: Add choice_combo setting type --- www/plugin_support/src/index.ts | 3 +- www/react-base/src/plugins/GlobalSettings.ts | 12 ++++-- .../SettingsView/Fields/FieldChoiceCombo.tsx | 40 +++++++++++++++++++ .../src/views/SettingsView/SettingsView.tsx | 6 +++ 4 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 www/react-base/src/views/SettingsView/Fields/FieldChoiceCombo.tsx diff --git a/www/plugin_support/src/index.ts b/www/plugin_support/src/index.ts index 1a441f57ed3d..52b4f7b7be5e 100644 --- a/www/plugin_support/src/index.ts +++ b/www/plugin_support/src/index.ts @@ -31,12 +31,13 @@ export type RouteConfig = { } export type SettingValue = string | number | boolean; -export type SettingType = "string" | "integer" | "float" | "boolean"; +export type SettingType = "string" | "integer" | "float" | "boolean" | "choice_combo"; export type SettingItemConfig = { name: string; type: SettingType; caption: string; + choices?: string[]; defaultValue: SettingValue; } diff --git a/www/react-base/src/plugins/GlobalSettings.ts b/www/react-base/src/plugins/GlobalSettings.ts index fe654fdca77b..f85e8a7eae70 100644 --- a/www/react-base/src/plugins/GlobalSettings.ts +++ b/www/react-base/src/plugins/GlobalSettings.ts @@ -20,13 +20,14 @@ import {Config} from "buildbot-ui"; import {ISettings, registerBuildbotSettingsSingleton} from "buildbot-plugin-support"; export type SettingValue = string | number | boolean; -export type SettingType = "string" | "integer" | "float" | "boolean"; +export type SettingType = "string" | "integer" | "float" | "boolean" | "choice_combo"; export type SettingItemConfig = { name: string; type: SettingType; caption: string; defaultValue: SettingValue; + choices?: string[]; // only when type == "choice_combo" } export type SettingGroupConfig = { @@ -40,6 +41,7 @@ export type SettingItem = { type: string; value: SettingValue; defaultValue: SettingValue; + choices?: string[]; // only when type == "choice_combo" caption: string; } @@ -107,15 +109,16 @@ export class GlobalSettings implements ISettings { const storedGroups = JSON.parse(settings) as StoredSettingGroups; for (const [groupName, storedGroup] of Object.entries(storedGroups)) { if (!(groupName in this.groups)) { - console.log(`Ignoring unknown loaded setting group ${groupName}`); + console.log(`Ignoring unknown loaded setting group ${groupName} ${JSON.stringify(this.groups)}`); continue; } const group = this.groups[groupName]; for (const [itemName, item] of Object.entries(storedGroup)) { if (!(itemName in group.items)) { - console.log(`Ignoring unknown loaded setting ${groupName}.${itemName}`); + console.log(`Ignoring unknown loaded setting ${groupName}.${itemName} ${group.items}`); continue; } + console.log(`qQQ2 ${itemName} ${item}`) this.setSettingItem(group.items[itemName], item); } } @@ -184,6 +187,7 @@ export class GlobalSettings implements ISettings { @action private setSettingItem(item: SettingItem, value: SettingValue) { switch (item.type) { case "string": + case "choice_combo": item.value = value.toString(); break; case "integer": { @@ -250,6 +254,7 @@ export class GlobalSettings implements ISettings { value: item.defaultValue, defaultValue: item.defaultValue, caption: item.caption, + choices: item.choices, }; } return; @@ -263,6 +268,7 @@ export class GlobalSettings implements ISettings { value: item.defaultValue, defaultValue: item.defaultValue, caption: item.caption, + choices: item.choices, }; } this.groups[config.name] = { diff --git a/www/react-base/src/views/SettingsView/Fields/FieldChoiceCombo.tsx b/www/react-base/src/views/SettingsView/Fields/FieldChoiceCombo.tsx new file mode 100644 index 000000000000..3d130cab0573 --- /dev/null +++ b/www/react-base/src/views/SettingsView/Fields/FieldChoiceCombo.tsx @@ -0,0 +1,40 @@ +/* + This file is part of Buildbot. Buildbot is free software: you can + redistribute it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 51 + Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Copyright Buildbot Team Members +*/ + +import {observer} from "mobx-react"; +import {Form} from "react-bootstrap"; +import {SettingItem, SettingValue} from "../../../plugins/GlobalSettings"; + +type FieldChoiceCombo = { + item: SettingItem; + setSetting: (value: SettingValue) => void +}; + +export const FieldChoiceCombo = observer(({item, setSetting}: FieldChoiceCombo) => { + return ( +
+ + { console.log(`set ${event.target.value}`); setSetting(event.target.value); }}> + { + item.choices === undefined ? <> : item.choices.map(ch => ()) + } + +
+ ); +}); \ No newline at end of file diff --git a/www/react-base/src/views/SettingsView/SettingsView.tsx b/www/react-base/src/views/SettingsView/SettingsView.tsx index d0035366fcbb..aae603b8cf1d 100644 --- a/www/react-base/src/views/SettingsView/SettingsView.tsx +++ b/www/react-base/src/views/SettingsView/SettingsView.tsx @@ -26,6 +26,7 @@ import { SettingItem, SettingValue } from "../../plugins/GlobalSettings"; import {FieldBoolean} from "./Fields/FieldBoolean"; +import {FieldChoiceCombo} from "./Fields/FieldChoiceCombo"; import {FieldFloat} from "./Fields/FieldFloat"; import {FieldInteger} from "./Fields/FieldInteger"; import {FieldString} from "./Fields/FieldString"; @@ -76,6 +77,11 @@ export const SettingsView = observer(() => { if (item.type === 'string') { return ; } + + if (item.type === 'choice_combo') { + return ; + } + return (
bad item type: {item.type} should be one of: bool, choices, integer, text