diff --git a/behave/features/results.feature b/behave/features/results.feature new file mode 100644 index 000000000..1ed2aa25c --- /dev/null +++ b/behave/features/results.feature @@ -0,0 +1,141 @@ +Feature: `osc results` command + + +Scenario: Run `osc results` with no arguments + When I execute osc with args "results" + Then the exit code is 2 + And stderr is + """ + No project given + """ + + +Scenario: Run `osc results /` + When I execute osc with args "results test:factory/multibuild-pkg" + Then stdout is + """ + standard x86_64 multibuild-pkg disabled + standard x86_64 multibuild-pkg:flavor1 disabled + standard x86_64 multibuild-pkg:flavor2 disabled + standard i586 multibuild-pkg disabled + standard i586 multibuild-pkg:flavor1 disabled + standard i586 multibuild-pkg:flavor2 disabled + """ + + +Scenario: Run `osc results` from a package checkout + Given I set working directory to "{context.osc.temp}" + And I execute osc with args "checkout test:factory/multibuild-pkg" + And I set working directory to "{context.osc.temp}/test:factory/multibuild-pkg" + When I execute osc with args "results" + Then stdout is + """ + standard x86_64 multibuild-pkg disabled + standard x86_64 multibuild-pkg:flavor1 disabled + standard x86_64 multibuild-pkg:flavor2 disabled + standard i586 multibuild-pkg disabled + standard i586 multibuild-pkg:flavor1 disabled + standard i586 multibuild-pkg:flavor2 disabled + """ + + +Scenario: Run `osc results /`, no multibuild flavors + When I execute osc with args "results test:factory/multibuild-pkg --no-multibuild" + Then stdout is + """ + standard x86_64 multibuild-pkg disabled + standard i586 multibuild-pkg disabled + """ + + +Scenario: Run `osc results` from a package checkout, multibuild flavor specified + Given I set working directory to "{context.osc.temp}" + And I execute osc with args "checkout test:factory/multibuild-pkg" + And I set working directory to "{context.osc.temp}/test:factory/multibuild-pkg" + When I execute osc with args "results -M flavor1" + Then stdout is + """ + standard x86_64 multibuild-pkg:flavor1 disabled + standard i586 multibuild-pkg:flavor1 disabled + """ + +Scenario: Run `osc results /`, specified output format + When I execute osc with args "results test:factory/multibuild-pkg --format='%(repository)s|%(arch)s|%(package)s|%(code)s'" + Then stdout is + """ + standard|x86_64|multibuild-pkg|disabled + standard|x86_64|multibuild-pkg:flavor1|disabled + standard|x86_64|multibuild-pkg:flavor2|disabled + standard|i586|multibuild-pkg|disabled + standard|i586|multibuild-pkg:flavor1|disabled + standard|i586|multibuild-pkg:flavor2|disabled + """ + + +Scenario: Run `osc results /`, csv output + When I execute osc with args "results test:factory/multibuild-pkg --csv" + Then stdout matches + """ + "standard","x86_64","multibuild-pkg","publish.*","False","disabled","" + "standard","x86_64","multibuild-pkg:flavor1","publish.*","False","disabled","" + "standard","x86_64","multibuild-pkg:flavor2","publish.*","False","disabled","" + "standard","i586","multibuild-pkg","publish.*","False","disabled","" + "standard","i586","multibuild-pkg:flavor1","publish.*","False","disabled","" + "standard","i586","multibuild-pkg:flavor2","publish.*","False","disabled","" + """ + + +Scenario: Run `osc results /`, csv output, multibuild flavor specified + When I execute osc with args "results test:factory/multibuild-pkg --csv -M flavor1" + Then stdout matches + """ + "standard","x86_64","multibuild-pkg:flavor1","publish.*","False","disabled","" + "standard","i586","multibuild-pkg:flavor1","publish.*","False","disabled","" + """ + + +Scenario: Run `osc results /`, csv output, specified output format (columns) + When I execute osc with args "results test:factory/multibuild-pkg --csv --format='repository,arch,package,code'" + Then stdout is + """ + "standard","x86_64","multibuild-pkg","disabled" + "standard","x86_64","multibuild-pkg:flavor1","disabled" + "standard","x86_64","multibuild-pkg:flavor2","disabled" + "standard","i586","multibuild-pkg","disabled" + "standard","i586","multibuild-pkg:flavor1","disabled" + "standard","i586","multibuild-pkg:flavor2","disabled" + """ + + +Scenario: Run `osc results /`, xml output + When I execute osc with args "results test:factory/multibuild-pkg --xml" + Then stdout matches + """ + + + + + + + + + + + + + """ + + +Scenario: Run `osc results /`, xml output, multibuild flavor specified + When I execute osc with args "results test:factory/multibuild-pkg --xml -M flavor1" + Then stdout matches + """ + + + + + + + + + """ diff --git a/osc/commandline.py b/osc/commandline.py index 0bc7dc631..3d7c2b7f8 100644 --- a/osc/commandline.py +++ b/osc/commandline.py @@ -6054,8 +6054,11 @@ def do_rremove(self, subcmd, opts): help='generate output in XML (former results_meta)') @cmdln.option('', '--csv', action='store_true', default=False, help='generate output in CSV format') - @cmdln.option('', '--format', default='%(repository)s|%(arch)s|%(state)s|%(dirty)s|%(code)s|%(details)s', - help='format string for csv output') + @cmdln.option('', '--format', default=None, + help="Change the format of the text (default) or csv output. Not supported for xml output.\n" + "Supported fields: project, package, repository, arch, state, dirty, code, details.\n" + "Text output format requires using the field names in form of named fields for string interpolation: ``%%(field)s``.\n" + "CSV output format requires field names separated with commas.") @cmdln.option('--show-excluded', action='store_true', help='show repos that are excluded for this package') def do_results(self, subcmd, opts, *args): @@ -6090,6 +6093,12 @@ def do_results(self, subcmd, opts, *args): if opts.failed and opts.status_filter: raise oscerr.WrongArgs('-s and -f cannot be used together') + if opts.multibuild_package and opts.no_multibuild: + self.argparser.error("-M/--multibuild-package and --no-multibuild are mutually exclusive") + + if opts.xml and opts.format: + self.argparser.error("--xml and --format are mutually exclusive") + if opts.failed: opts.status_filter = 'failed' opts.brief = True @@ -6123,12 +6132,33 @@ def do_results(self, subcmd, opts, *args): print(decode_it(xml), end='') else: # csv formatting - results = [r for r, _ in result_xml_to_dicts(xml)] - print('\n'.join(format_results(results, opts.format))) + if opts.format is None: + columns = ["repository", "arch", "package", "state", "dirty", "code", "details"] + else: + # split columns by colon, semicolon or pipe + columns = opts.format.split(",") + + supported_columns = ["project", "package", "repository", "arch", "state", "dirty", "code", "details"] + unknown_columns = sorted(set(columns) - set(supported_columns)) + + if unknown_columns: + self.argparser.error(f"Unknown format fields: {''.join(unknown_columns)}") + + f = io.StringIO() + writer = csv.writer(f, dialect="unix") + + rows = [r for r, _ in result_xml_to_dicts(xml)] + for row in rows: + writer.writerow([row[i] for i in columns]) + + f.seek(0) + print(f.read(), end="") + else: kwargs['verbose'] = opts.verbose kwargs['wait'] = opts.watch kwargs['printJoin'] = '\n' + kwargs['format'] = opts.format get_results(**kwargs) # WARNING: this function is also called by do_results. You need to set a default there diff --git a/osc/core.py b/osc/core.py index 5dd96de57..041a80b57 100644 --- a/osc/core.py +++ b/osc/core.py @@ -4101,8 +4101,9 @@ def get_results(apiurl: str, project: str, package: str, verbose=False, printJoi # hmm the function name is a bit too generic - something like # get_package_results_human would be better, but this would break the existing # api (unless we keep get_results around as well)... - result_line_templ = '%(rep)-20s %(arch)-10s %(status)s' - result_line_mb_templ = '%(rep)-20s %(arch)-10s %(pkg)-30s %(status)s' + format = kwargs.pop('format') + if format is None: + format = '%(rep)-20s %(arch)-10s %(pkg)-30s %(status)s' r = [] printed = False multibuild_packages = kwargs.pop('multibuild_packages', []) @@ -4145,10 +4146,7 @@ def get_results(apiurl: str, project: str, package: str, verbose=False, printJoi # of the repository if the result is already prefiltered by the backend. So we need # to filter out the repository states. if code_filter is None or code_filter == res['code']: - if is_multi: - r.append(result_line_mb_templ % res) - else: - r.append(result_line_templ % res) + r.append(format % res) if printJoin: if printed: @@ -4159,9 +4157,9 @@ def get_results(apiurl: str, project: str, package: str, verbose=False, printJoi return r -def get_package_results(apiurl: str, project: str, package: Optional[str] = None, wait=False, *args, **kwargs): +def get_package_results(apiurl: str, project: str, package: Optional[str] = None, wait=False, multibuild_packages: Optional[List[str]] = None, *args, **kwargs): """generator that returns a the package results as an xml structure""" - xml = '' + xml = b'' waiting_states = ('blocked', 'scheduled', 'dispatching', 'building', 'signing', 'finished') while True: @@ -4194,6 +4192,33 @@ def get_package_results(apiurl: str, project: str, package: Optional[str] = None waiting = True break + # filter the result according to the specified multibuild_packages (flavors) + if multibuild_packages: + for result in list(root): + for status in list(result): + package = status.attrib["package"] + package_flavor = package.rsplit(":", 1) + + # package has flavor, check if the flavor is in multibuild_packages + flavor_match = len(package_flavor) == 2 and package_flavor[1] in multibuild_packages + + # package nas no flavor, check if "" is in multibuild_packages + no_flavor_match = len(package_flavor) == 1 and "" in multibuild_packages + + if not flavor_match and not no_flavor_match: + # package doesn't match multibuild_packages, remove the corresponding from + result.remove(status) + + # remove empty from + if len(result) == 0: + root.remove(result) + + if len(root) == 0: + break + + xmlindent(root) + xml = ET.tostring(root) + if not wait or not waiting: break else: