From ac95b828ec655db42e8816abaee41b2b68228dab Mon Sep 17 00:00:00 2001 From: Anton Medvedev Date: Jun 27 2025 05:19:46 +0000 Subject: feat(cli): added new arguments for request-repo command, when monitoring option is set it checks if package exist in Anitya and if not it ask to provide new arguments Signed-off-by: Anton Medvedev --- diff --git a/fedpkg/cli.py b/fedpkg/cli.py index e5ed34b..bcd8014 100644 --- a/fedpkg/cli.py +++ b/fedpkg/cli.py @@ -44,7 +44,8 @@ from fedpkg.utils import (assert_new_tests_repo, assert_valid_epel_package, do_fork, expand_release, get_dist_git_url, get_fedora_release_state, get_pagure_branches, get_release_branches, get_stream_branches, is_epel, - new_pagure_issue, sl_list_to_dict, verify_sls) + new_pagure_issue, sl_list_to_dict, verify_sls, + does_package_exist_in_anitya) RELEASE_BRANCH_REGEX = r'^(f\d+|el\d+|eln|epel\d+|epel\d+\.\d+)$' LOCAL_PACKAGE_CONFIG = 'package.cfg' @@ -330,6 +331,16 @@ class fedpkgClient(cliClient): Another example to request a module foo: fedpkg request-repo --namespace modules foo + + If you would like to create project in release-monitoring + when requesting a new dist-git repository, use the following + command with mandotory arguments. + Full list of arguments and their values is in doc: + https://release-monitoring.org/static/docs/user-guide.html: + + fedpkg request-repo foo 1234 --create_monitoring_project \\ + --homepage https://example.org --backend custom \\ + --distribution Fedora --project_ecosystem https://example.org '''.format(self.name, pagure_url_parsed)) request_repo_parser = self.subparsers.add_parser( @@ -369,6 +380,21 @@ class fedpkgClient(cliClient): help='The package is an exception to the regular package review ' 'process (specifically, it does not require a Bugzilla bug)') request_repo_parser.add_argument( + '--project-name', help='The package\'s project name', required=False + ) + backend_choices = [ + "BitBucket", "Cgit", "CPAN", "CRAN", "crates.io", "Debian project", + "Drupal6", "Drupal7", "folder", "Freshmeat", "GitHub", + "GitLab", "Gitea", "GNOME", "GNU Project", "Gogs", + "Google code", "Hackage", "Launchpad", "Maven Central", "npmjs", "Packagist", + "pagure", "PEAR", "PECL", "PyPI", "Rubygems", "Sourcefoge", + "Sourceforge (git)", "SourceHut", "Stackage", "custom", + ] + request_repo_parser.add_argument( + '--backend', choices=backend_choices, + help='The package\'s project backend', required=False + ) + request_repo_parser.add_argument( '--no-initial-commit', action='store_true', help='Do not include an initial commit in the repository.') @@ -999,6 +1025,8 @@ class fedpkgClient(cliClient): exception=self.args.exception, name=self.name, config=self.config, + project_name=self.args.project_name, + backend=self.args.backend, initial_commit=not self.args.no_initial_commit, onboard_packit=self.args.onboard_packit, ) @@ -1019,7 +1047,8 @@ class fedpkgClient(cliClient): def _request_repo(logger, repo_name, ns, description, name, config, branch=None, summary=None, upstreamurl=None, monitor=None, bug=None, exception=None, anongiturl=None, - initial_commit=True, onboard_packit=None): + project_name=None, backend=None, + initial_commit=True, onboard_packit=None,): """ Implementation of `request_repo`. Submits a request for a new dist-git repo. @@ -1052,6 +1081,9 @@ class fedpkgClient(cliClient): granted the right to waive their package review at the discretion of Release Engineering. Typically takes the value of `self.args.exception`. + :param project_name: A string representing project name for Anitya + if It's different from package name in distribution + :param backend: A string representing the backend creating Anitya project: :param anongiturl: A string with the name of the anonymous git url. Typically the value of `self.cmd.anongiturl`. :param onboard_packit: A string, whether to generate a default @@ -1061,7 +1093,6 @@ class fedpkgClient(cliClient): Typically takes the value of `self.args.onboard_packit`. :return: None """ - # bug is not a required parameter in the event the packager has an # exception, in which case, they may use the --exception flag # neither in case of modules, which don't require a formal review @@ -1118,6 +1149,24 @@ class fedpkgClient(cliClient): } if not initial_commit: ticket_body['initial_commit'] = False + if monitor != "no-monitoring": + if not does_package_exist_in_anitya(repo_name): + if backend is None or upstreamurl == '' or upstreamurl is None: + raise rpkgError( + "When monitoring or monitoring-with-scratch argument are set" + " you also required to provide `--backend`, `--upstreamurl`" + " and `--project-name` (if its different from package name) arguments" + " to create project and package for Anitya." + ) + if project_name is None: + project_name = repo_name + if backend is None: + backend = "custom" + ticket_body.update({ + 'backend': backend, + 'project_name': project_name, + 'distribution': 'Fedora', + }) ticket_body = json.dumps(ticket_body, indent=True) ticket_body = '```\n{0}\n```'.format(ticket_body) diff --git a/fedpkg/utils.py b/fedpkg/utils.py index 1ce9177..b997e62 100644 --- a/fedpkg/utils.py +++ b/fedpkg/utils.py @@ -657,3 +657,27 @@ def disable_monitoring(logger, base_url, token, repo_name, namespace, cli_name): raise rpkgError(base_error_msg.format(rv_error)) logger.info("Monitoring of the project was sucessfully disabled.") + + +def does_package_exist_in_anitya(package_name): + """ + Check if a package exists in the anitya. + :param package_name: the name of the package + :return: True if the package exists in the anitya, False otherwise + """ + project_endpoint = "https://release-monitoring.org/api/v2/packages/" + params = { + 'name': package_name, + 'distribution': "Fedora" + } + + try: + response = requests.get(project_endpoint, params=params, timeout=3000) + if response.status_code == 200: + return True + elif response.status_code == 400: + return False + else: + return False + except requests.exceptions.RequestException: + return False diff --git a/test/test_cli.py b/test/test_cli.py index a89f044..4f66123 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -491,6 +491,7 @@ class TestRequestRepo(CliTestCase): mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} mock_request_post.return_value = mock_rv + fedpkg.cli.does_package_exist_in_anitya = Mock(return_value=True) cli_cmd = ['fedpkg-stage', '--path', self.cloned_repo_path, 'request-repo', 'testpkg', '1441813'] @@ -499,13 +500,16 @@ class TestRequestRepo(CliTestCase): expected_issue_content = { 'action': 'new_repo', + 'backend': 'custom', 'branch': 'rawhide', 'bug_id': 1441813, 'description': '', + 'distribution': 'Fedora', 'exception': False, 'monitor': 'monitoring', 'namespace': 'rpms', 'onboard_packit': 'no', + 'project_name': 'testpkg', 'repo': 'testpkg', 'summary': 'a description', 'upstreamurl': '' @@ -529,6 +533,7 @@ class TestRequestRepo(CliTestCase): mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} mock_request_post.return_value = mock_rv + fedpkg.cli.does_package_exist_in_anitya = Mock(return_value=True) cli_cmd = ['fedpkg-stage', '--path', self.cloned_repo_path, 'request-repo', 'nethack', '1441813'] @@ -547,7 +552,10 @@ class TestRequestRepo(CliTestCase): 'repo': 'nethack', 'summary': ('A rogue-like single player dungeon exploration ' 'game'), - 'upstreamurl': '' + 'upstreamurl': '', + 'backend': 'custom', + 'project_name': 'nethack', + 'distribution': 'Fedora', } # Get the data that was submitted to Pagure post_data = mock_request_post.call_args_list[0][1]['data'] @@ -570,6 +578,7 @@ class TestRequestRepo(CliTestCase): mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} mock_request_post.return_value = mock_rv + fedpkg.cli.does_package_exist_in_anitya = Mock(return_value=True) cli_cmd = ['fedpkg-stage', '--path', self.cloned_repo_path, 'request-repo', '--namespace', 'modules', 'nethack'] @@ -587,7 +596,10 @@ class TestRequestRepo(CliTestCase): 'onboard_packit': 'no', 'repo': 'nethack', 'summary': (''), - 'upstreamurl': '' + 'upstreamurl': '', + 'backend': 'custom', + 'project_name': 'nethack', + 'distribution': 'Fedora', } # Get the data that was submitted to Pagure post_data = mock_request_post.call_args_list[0][1]['data'] @@ -610,6 +622,7 @@ class TestRequestRepo(CliTestCase): mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} mock_request_post.return_value = mock_rv + fedpkg.cli.does_package_exist_in_anitya = Mock(return_value=True) cli_cmd = [ 'fedpkg-stage', '--path', self.cloned_repo_path, @@ -630,7 +643,10 @@ class TestRequestRepo(CliTestCase): 'repo': 'nethack', 'summary': ('A rogue-like single player dungeon exploration ' 'game'), - 'upstreamurl': '' + 'upstreamurl': '', + 'backend': 'custom', + 'project_name': 'nethack', + 'distribution': 'Fedora', } # Get the data that was submitted to Pagure post_data = mock_request_post.call_args_list[0][1]['data'] @@ -691,6 +707,7 @@ class TestRequestRepo(CliTestCase): mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} mock_request_post.return_value = mock_rv + fedpkg.cli.does_package_exist_in_anitya = Mock(return_value=True) cli_cmd = ['fedpkg-stage', '--path', self.cloned_repo_path, 'request-repo', '--exception', 'nethack'] @@ -708,7 +725,10 @@ class TestRequestRepo(CliTestCase): 'onboard_packit': 'no', 'repo': 'nethack', 'summary': '', - 'upstreamurl': '' + 'upstreamurl': '', + 'backend': 'custom', + 'project_name': 'nethack', + 'distribution': 'Fedora', } # Get the data that was submitted to Pagure post_data = mock_request_post.call_args_list[0][1]['data'] @@ -722,6 +742,33 @@ class TestRequestRepo(CliTestCase): # Since it is an exception, Bugzilla will not have been queried mock_bz.getbug.assert_not_called() + @patch('requests.post') + @patch('sys.stdout', new=io.StringIO()) + def test_request_repo_monitoring_anitya_not_exist(self, mock_request_post, mock_bz): + """Tests a request-repo call with the monitoring flag when anitya does not exist + and arguments for creating not provided""" + self.mock_bug.summary = ('Review Request: testpkg - a description') + mock_bz.getbug.return_value = self.mock_bug + mock_rv = Mock() + mock_rv.ok = True + mock_rv.json.return_value = {'issue': {'id': 2}} + mock_request_post.return_value = mock_rv + fedpkg.cli.does_package_exist_in_anitya = Mock(return_value=False) + + cli_cmd = ['fedpkg-stage', '--path', self.cloned_repo_path, + 'request-repo', 'testpkg', '1441813'] + cli = self.get_cli(cli_cmd) + expected_error = ( + "When monitoring or monitoring-with-scratch argument are set" + " you also required to provide `--backend`, `--upstreamurl`" + " and `--project-name` (if its different from package name) arguments" + " to create project and package for Anitya." + ) + try: + cli.request_repo() + except rpkgError as error: + self.assertEqual(expected_error, str(error)) + def test_request_repo_wrong_package(self, mock_bz): """Tests request-repo errors when the package is wrong""" mock_bz.getbug.return_value = self.mock_bug @@ -1568,6 +1615,7 @@ class TestRequestTestsRepo(CliTestCase): expected_issue_content = { 'action': 'new_repo', + 'backend': 'custom', 'branch': 'main', 'bug_id': '', 'monitor': 'no-monitoring', @@ -1575,7 +1623,9 @@ class TestRequestTestsRepo(CliTestCase): 'onboard_packit': 'no', 'repo': 'foo', 'description': 'Some description', - 'upstreamurl': '' + 'distribution': 'Fedora', + 'upstreamurl': '', + 'project_name': 'foo', } # Get the data that was submitted to Pagure diff --git a/test/test_utils.py b/test/test_utils.py index 051e813..1ef5eff 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -662,3 +662,36 @@ class TestGetReleaseBranches(unittest.TestCase): result = utils.get_release_branches('https://bodhi.fedoraproject.org/releases/') self.assertEqual(result, expected_output, msg=f"Expected branches {expected_output}, but got {result}") + + +class TestDoesPackageExistInAnitya(unittest.TestCase): + """Test utils.does_package_exist_in_anitya""" + + @patch('requests.get') + def test_does_package_exist_in_anitya_everything_is_okay(self, mock_get): + """Test when package exists in Anitya (status code 200)""" + expected_output = True + mock_response = Mock() + mock_response.status_code = 200 + mock_get.return_value = mock_response + package_name = 'package_name' + rv = utils.does_package_exist_in_anitya(package_name) + self.assertEqual(rv, expected_output) + + @patch('requests.get') + def test_does_package_exist_in_anitya_no_package_exist(self, mock_get): + """Test when package doesn't exist in Anitya (status code 400)""" + expected_output = False + mock_response = Mock() + mock_response.status_code = 400 + mock_get.return_value = mock_response + package_name = 'package_name' + rv = utils.does_package_exist_in_anitya(package_name) + self.assertEqual(rv, expected_output) + + @patch('requests.get') + def test_does_package_exist_in_anitya_exception_raises(self, mock_get): + """Test when there's a request exception""" + mock_get.side_effect = requests.exceptions.RequestException("RequestException") + result = utils.does_package_exist_in_anitya("error-package") + self.assertEqual(result, False)