From 1ce91587a23d40e8c69c418be60b94d0fe4c417c Mon Sep 17 00:00:00 2001 From: Ondrej Nosek Date: Mar 14 2022 00:33:32 +0000 Subject: Add `--custom-user-metadata` to build command Add support for `--custom-user-metadata` argument for `build` and `scratch-build` commands. This will pass a JSON string of custom metadata to Koji/Brew to be deserialized and stored under the build's extra.custom_user_metadata field. Example: fedpkg scratch-build --srpm --custom-user-metadata '{"name1":"value1","name2":"value2"}' JIRA: RHELCMP-8318 Signed-off-by: Ondrej Nosek --- diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py index fe3aa07..7162c53 100644 --- a/pyrpkg/__init__.py +++ b/pyrpkg/__init__.py @@ -2312,7 +2312,7 @@ class Commands(object): def build(self, skip_tag=False, scratch=False, background=False, url=None, chain=None, arches=None, sets=False, nvr_check=True, - fail_fast=False): + fail_fast=False, custom_user_metadata=None): """Initiate a build in build system :param bool skip_tag: Skip the tag action after the build. @@ -2328,6 +2328,7 @@ class Commands(object): :param bool fail_fast: Perform the build in fast failure mode, which will cause the entire build to fail if any subtask/architecture build fails. + :param str custom_user_metadata JSON string of custom metadata :return: task ID returned from Koji API ``build`` and ``chainBuild``. :rtype: int """ @@ -2394,6 +2395,8 @@ class Commands(object): raise rpkgError('Invalid architecture name: %s' % arch) cmd.append('--arch-override=%s' % ','.join(arches)) opts['arch_override'] = ' '.join(arches) + if custom_user_metadata: + opts['custom_user_metadata'] = custom_user_metadata cmd.append(self.target) diff --git a/pyrpkg/cli.py b/pyrpkg/cli.py index 8efb841..6423664 100644 --- a/pyrpkg/cli.py +++ b/pyrpkg/cli.py @@ -16,6 +16,7 @@ from __future__ import print_function import argparse import getpass +import json import logging import os import re @@ -505,6 +506,10 @@ class cliClient(object): help='Submit build to buildsystem without check if NVR was ' 'already built. NVR is constructed locally and may be ' 'different from NVR constructed during build on builder.') + self.build_parser_common.add_argument( + "--custom-user-metadata", type=str, + help=('Provide a JSON string of custom metadata to be deserialized and ' + 'stored under the build\'s extra.custom_user_metadata field')) def register_rpm_common(self): """Create a common parser for rpm commands""" @@ -1994,6 +1999,17 @@ class cliClient(object): if self.args.target: self.cmd.target = self.args.target + custom_user_metadata = {} + if hasattr(self.args, 'custom_user_metadata') and self.args.custom_user_metadata: + try: + custom_user_metadata = json.loads(self.args.custom_user_metadata) + # Use ValueError instead of json.JSONDecodeError for Python 2 and 3 compatibility + except ValueError as e: + self.parser.error("--custom-user-metadata is not valid JSON: %s" % e) + + if not isinstance(custom_user_metadata, dict): + self.parser.error("--custom-user-metadata must be a JSON object") + # handle uploading the srpm if we got one url = self._handle_srpm_option() @@ -2006,7 +2022,8 @@ class cliClient(object): arches=arches, sets=sets, nvr_check=nvr_check, - fail_fast=self.args.fail_fast) + fail_fast=self.args.fail_fast, + custom_user_metadata=custom_user_metadata) def chainbuild(self): """Implement chain-build command""" diff --git a/tests/test_cli.py b/tests/test_cli.py index b99b1dd..94aaaff 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3607,6 +3607,18 @@ class TestBuildPackage(FakeKojiCreds, CliTestCase): cli_opts=['--fail-fast'], expected_opts={'fail_fast': True}) + def test_option_custom_user_metadata(self): + self.assert_build('build', + cli_opts=['--custom-user-metadata', '{"a":"b"}'], + expected_opts={'custom_user_metadata': {'a': 'b'}}) + + def test_option_custom_user_metadata_scratch(self): + self.assert_build('scratch-build', + cli_opts=['--custom-user-metadata', '{"a": "b", "c": "d"}'], + expected_opts={ + 'scratch': True, + 'custom_user_metadata': {'a': 'b', 'c': 'd'}}) + @patch('pyrpkg.Commands.nvr', new_callable=PropertyMock) def test_fail_to_get_nvr_but_has_to_check_nvr_existence(self, nvr): nvr.side_effect = rpkgError