diff --git a/no-testcontainers.patch b/no-testcontainers.patch new file mode 100644 index 0000000..34f07dd --- /dev/null +++ b/no-testcontainers.patch @@ -0,0 +1,61 @@ +Index: grimoirelab-sortinghat-1.7.1/tests/runners.py +=================================================================== +--- grimoirelab-sortinghat-1.7.1.orig/tests/runners.py ++++ grimoirelab-sortinghat-1.7.1/tests/runners.py +@@ -18,54 +18,22 @@ + + from django.conf import settings + from django.test.runner import DiscoverRunner +-from testcontainers.mysql import MySqlContainer + from unittest.suite import TestSuite + + +-class TestContainersRunner(DiscoverRunner): +- _mysql_container: MySqlContainer = None +- +- def __init__(self, *args, **kwargs): +- super().__init__(*args, **kwargs) +- +- db_image = getattr(settings, "TEST_DATABASE_IMAGE", "mariadb:latest") +- +- self._mysql_container = MySqlContainer(image=db_image, +- root_password="root") +- +- def _setup_container(self): +- self._mysql_container.start() +- +- for database in settings.DATABASES: +- settings.DATABASES[database]["HOST"] = "127.0.0.1" +- settings.DATABASES[database]["PORT"] = self._mysql_container.get_exposed_port(3306) +- settings.DATABASES[database]["USER"] = "root" +- settings.DATABASES[database]["PASSWORD"] = self._mysql_container.root_password +- +- def _teardown_container(self): +- self._mysql_container.stop() +- +- def setup_databases(self, **kwargs): +- self._setup_container() +- return super().setup_databases(**kwargs) +- +- def teardown_databases(self, old_config, **kwargs): +- super().teardown_databases(old_config, **kwargs) +- self._teardown_container() +- + + def from_tenant_module(test): + return test.__module__.startswith('tests.tenants') + + +-class SkipMultiTenantTestRunner(TestContainersRunner): ++class SkipMultiTenantTestRunner(DiscoverRunner): + def build_suite(self, test_labels=None, extra_tests=None, **kwargs): + suite = super().build_suite(test_labels=test_labels, extra_tests=extra_tests, **kwargs) + tests = [t for t in suite._tests if not from_tenant_module(t)] + return TestSuite(tests=tests) + + +-class OnlyMultiTenantTestRunner(TestContainersRunner): ++class OnlyMultiTenantTestRunner(DiscoverRunner): + def build_suite(self, test_labels=None, extra_tests=None, **kwargs): + suite = super().build_suite(test_labels=test_labels, extra_tests=extra_tests, **kwargs) + tests = [t for t in suite._tests if from_tenant_module(t)] diff --git a/python-sortinghat.changes b/python-sortinghat.changes index 27d9921..0d6ccb9 100644 --- a/python-sortinghat.changes +++ b/python-sortinghat.changes @@ -1,3 +1,21 @@ +------------------------------------------------------------------- +Thu May 15 02:33:32 UTC 2025 - Steve Kowalik + +- Update to 1.7.1: + * New features: + + More detailed individual information when reviewing recommendations + + Renamed recommendations view labels + + Improved session cookie security + + More icons for sources + + Skip recommendations + + Sort organizations by number of enrollments + * Bug fixes: + + Wrong order of affiliations +- Add patch no-testcontainers.patch: + * Do not rely on testcontainers to use mysql +- Add patch support-click-8.2.patch: + * Support click 8.2+ changes. + ------------------------------------------------------------------- Wed Dec 11 04:53:10 UTC 2024 - Steve Kowalik diff --git a/python-sortinghat.spec b/python-sortinghat.spec index f32ee64..104ef38 100644 --- a/python-sortinghat.spec +++ b/python-sortinghat.spec @@ -1,7 +1,7 @@ # # spec file for package python-sortinghat # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: python-sortinghat -Version: 1.5.1 +Version: 1.7.1 Release: 0 Summary: A tool to manage identities License: GPL-3.0-only @@ -27,6 +27,10 @@ Source: https://github.com/chaoss/grimoirelab-sortinghat/archive/refs/ta Patch0: allow-database-config-overrides.patch # PATCH-FIX-OPENSUSE Support django-graphql-jwt 0.4.0 Patch1: support-new-django-graphql-jwt.patch +# PATCH-FIX-OPENSUSE Do not use testcontainers module +Patch2: no-testcontainers.patch +# PATCH-FIX-OPENSUSE Support click 8.2+ +Patch3: support-click-8.2.patch BuildRequires: %{python_module base >= 3.9} BuildRequires: %{python_module pip} BuildRequires: %{python_module poetry-core} @@ -35,12 +39,12 @@ BuildRequires: fdupes BuildRequires: python-rpm-macros Requires: python-Django >= 4.2 Requires: python-Jinja2 >= 3.1 -Requires: python-PyJWT +Requires: python-PyJWT >= 2.4 Requires: python-PyMySQL >= 0.7.0 Requires: python-PyYAML >= 3.12 Requires: python-SQLAlchemy >= 1.2 Requires: python-click >= 7.1 -Requires: python-django-cors-headers >= 3.7 +Requires: python-django-cors-headers >= 4.6 Requires: python-django-graphql-jwt >= 0.3 Requires: python-django-rq >= 2.3 Requires: python-django-treebeard >= 4.5 @@ -50,12 +54,12 @@ Requires: python-grimoirelab-toolkit >= 0.3 Requires: python-importlib-resources Requires: python-mysqlclient >= 2.0 Requires: python-numpy -Requires: python-pandas >= 1.3 +Requires: python-pandas >= 2.2 Requires: python-python-dateutil >= 2.8.0 Requires: python-requests >= 2.7 Requires: python-rq Requires: python-setuptools -Requires: python-sgqlc +Requires: python-sgqlc >= 16.1 Requires(post): update-alternatives Requires(postun): update-alternatives BuildArch: noarch @@ -66,7 +70,7 @@ BuildRequires: %{python_module PyMySQL >= 0.7.0} BuildRequires: %{python_module PyYAML >= 3.12} BuildRequires: %{python_module SQLAlchemy >= 1.2} BuildRequires: %{python_module click >= 7.1} -BuildRequires: %{python_module django-cors-headers >= 3.7} +BuildRequires: %{python_module django-cors-headers >= 4.6} BuildRequires: %{python_module django-graphql-jwt >= 0.3} BuildRequires: %{python_module django-rq >= 2.3} BuildRequires: %{python_module django-treebeard >= 4.5} @@ -77,12 +81,12 @@ BuildRequires: %{python_module httpretty >= 0.9.5} BuildRequires: %{python_module importlib-resources} BuildRequires: %{python_module mysqlclient >= 2.0} BuildRequires: %{python_module numpy} -BuildRequires: %{python_module pandas >= 1.3} +BuildRequires: %{python_module pandas >= 2.2} BuildRequires: %{python_module pytest} BuildRequires: %{python_module python-dateutil >= 2.8.0} BuildRequires: %{python_module requests >= 2.7} BuildRequires: %{python_module rq} -BuildRequires: %{python_module sgqlc} +BuildRequires: %{python_module sgqlc >= 16.1} BuildRequires: mariadb-rpm-macros # /SECTION %python_subpackages diff --git a/sortinghat-1.5.1.tar.gz b/sortinghat-1.5.1.tar.gz deleted file mode 100644 index ad46616..0000000 --- a/sortinghat-1.5.1.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7a8efcc5ded3717d5962202c089f5472f05562186734cd04fc52f986c14a5229 -size 2275252 diff --git a/sortinghat-1.7.1.tar.gz b/sortinghat-1.7.1.tar.gz new file mode 100644 index 0000000..68f7c3d --- /dev/null +++ b/sortinghat-1.7.1.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26e091403d3d2699d55b65a79a67494e95654bf78d66d884adff41e30f2af3ca +size 2264700 diff --git a/support-click-8.2.patch b/support-click-8.2.patch new file mode 100644 index 0000000..8ce4d91 --- /dev/null +++ b/support-click-8.2.patch @@ -0,0 +1,856 @@ +Index: grimoirelab-sortinghat-1.7.1/tests/cli/test_cmd_add.py +=================================================================== +--- grimoirelab-sortinghat-1.7.1.orig/tests/cli/test_cmd_add.py ++++ grimoirelab-sortinghat-1.7.1/tests/cli/test_cmd_add.py +@@ -20,6 +20,7 @@ + # Santiago Dueñas + # + ++import inspect + import unittest + import unittest.mock + +@@ -107,7 +108,10 @@ class TestAddCommand(unittest.TestCase): + client = MockClient(responses) + mock_client.return_value = client + +- runner = click.testing.CliRunner() ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + # Create a new identity + params = [ +@@ -135,7 +139,10 @@ class TestAddCommand(unittest.TestCase): + client = MockClient(responses) + mock_client.return_value = client + +- runner = click.testing.CliRunner() ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + # Create a new identity setting partial data + params = [ +@@ -161,7 +168,10 @@ class TestAddCommand(unittest.TestCase): + client = MockClient(responses) + mock_client.return_value = client + +- runner = click.testing.CliRunner() ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + # Assign to John Smith - a9b403e150dd4af8953a52a4bb841051e4b705d9 + # individual +@@ -190,7 +200,11 @@ class TestAddCommand(unittest.TestCase): + client = MockClient(responses) + mock_client.return_value = client + +- runner = click.testing.CliRunner() ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() ++ + + params = [ + '--source', 'scm', +@@ -225,7 +239,10 @@ class TestAddCommand(unittest.TestCase): + client = MockClient(responses) + mock_client.return_value = client + +- runner = click.testing.CliRunner(mix_stderr=False) ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + params = [ + '--source', 'scm', +Index: grimoirelab-sortinghat-1.7.1/tests/cli/test_cmd_config.py +=================================================================== +--- grimoirelab-sortinghat-1.7.1.orig/tests/cli/test_cmd_config.py ++++ grimoirelab-sortinghat-1.7.1/tests/cli/test_cmd_config.py +@@ -20,6 +20,7 @@ + # Santiago Dueñas + # + ++import inspect + import os.path + import shutil + import unittest +@@ -53,7 +54,10 @@ class TestInitConfig(unittest.TestCase): + def test_init(self): + """Check if it initializes a configuration file.""" + +- runner = click.testing.CliRunner() ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + with runner.isolated_filesystem() as fs: + filepath = os.path.join(fs, MOCK_CONFIG_FILE) +@@ -79,7 +83,10 @@ class TestInitConfig(unittest.TestCase): + def test_default_filename(self, mock_basepath): + """Check if it uses the default filename when filepath is not given""" + +- runner = click.testing.CliRunner() ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + with runner.isolated_filesystem() as fs: + dirpath = os.path.join(fs, '.sortinghat') +@@ -104,7 +111,10 @@ class TestInitConfig(unittest.TestCase): + def test_config_is_not_overwritten(self): + """Check whether an existing config file is not replaced""" + +- runner = click.testing.CliRunner(mix_stderr=False) ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + with runner.isolated_filesystem() as fs: + shutil.copy(MOCK_CONFIG_FILEPATH, fs) +@@ -132,7 +142,10 @@ class TestInitConfig(unittest.TestCase): + def test_overwrite_config(self): + """Check whether an existing config file is overwritten""" + +- runner = click.testing.CliRunner(mix_stderr=False) ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + with runner.isolated_filesystem() as fs: + shutil.copy(MOCK_CONFIG_FILEPATH, fs) +@@ -168,7 +181,10 @@ class TestSetConfig(unittest.TestCase): + def test_set_value(self): + """Check set method""" + +- runner = click.testing.CliRunner() ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + with runner.isolated_filesystem() as fs: + shutil.copy(MOCK_CONFIG_FILEPATH, fs) +@@ -210,7 +226,10 @@ class TestSetConfig(unittest.TestCase): + def test_default_filename(self, mock_basepath): + """Check if it uses the default filename when filepath is not given""" + +- runner = click.testing.CliRunner() ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + with runner.isolated_filesystem() as fs: + dirpath = os.path.join(fs, '.sortinghat') +@@ -240,7 +259,10 @@ class TestSetConfig(unittest.TestCase): + def test_not_available_keys(self): + """Check if it raises an error when the key is not available""" + +- runner = click.testing.CliRunner(mix_stderr=False) ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + with runner.isolated_filesystem() as fs: + shutil.copy(MOCK_CONFIG_FILEPATH, fs) +@@ -260,7 +282,10 @@ class TestSetConfig(unittest.TestCase): + def test_invalid_keys(self): + """Check if it raises an error when the key is invalid""" + +- runner = click.testing.CliRunner(mix_stderr=False) ++ if inspect.signature(click.testing.CliRunner).parameters.get("mix_stderr"): ++ runner = click.testing.CliRunner(mix_stderr=False) ++ else: ++ runner = click.testing.CliRunner() + + # Test keys that do not follow '
.