from typing import Dict, Optional, Union import argparse import json import platform from promptflow._sdk._configuration import Configuration from promptflow._sdk._telemetry.telemetry import get_telemetry_logger from xml.dom import minidom def parse_junit_xml(fle: str) -> Dict[str, Dict[str, Union[float, str]]]: """ Parse the xml in Junit xml format. :param fle: The file in JUnit xml format. :type fle: str :return: The dictionary with tests, their run times and pass/fail status. """ test_results = {} dom = minidom.parse(fle) # Take node list Document/testsuites/testsuite/ for test in dom.firstChild.firstChild.childNodes: test_name = f"{test.attributes['classname'].value}::{test.attributes['name'].value}" test_results[test_name] = {'fail_message': '', 'time': float(test.attributes['time'].value)} for child in test.childNodes: if child.nodeName == 'failure': test_results[test_name]['fail_message'] = child.attributes["message"].value return test_results def main(activity_name: str, value: Union[float, str], run_id: str, workflow: str, action: str, branch: str, junit_file: Optional[str]) -> None: """ Log the CI-CD event. :param activity_name: The name of a n activity to be logged, for example, installation time. :type activity_name: str :param value: The value of a parameter :type value: float :param run_id: The CI-CD run id. :type run_id: str :param workflow: The name of a workflow or path to a workflow file. :type workflow: str :param action: The name of running action or a step. :type action: str :param branch: The branch from which the CI-CD was triggered. :type branch: str :param junit_file: The path to junit test file results. :type junit_file: str """ # Enable telemetry config = Configuration.get_instance() config.set_config(Configuration.COLLECT_TELEMETRY, True) logger = get_telemetry_logger() activity_info = { "activity_name": activity_name, "activity_type": "ci_cd_analytics", "OS": platform.system(), "OS_release": platform.release(), "branch": branch, "git_hub_action_run_id": run_id, "git_hub_workflow": workflow } if junit_file: junit_dict = parse_junit_xml(junit_file) for k, v in junit_dict.items(): if v["fail_message"]: # Do not log time together with fail message. continue activity_info[k] = v['time'] else: if isinstance(value, str): activity_info.update(json.loads(value)) else: activity_info["value"] = value # write information to the application insights. logger.info(action, extra={"custom_dimensions": activity_info}) if __name__ == '__main__': parser = argparse.ArgumentParser( description="Log the value to application insights along with platform characteristics and run ID.") parser.add_argument('--activity', help='The activity to be logged.', required=True) parser.add_argument( '--value', help='The floating point value for activity or a set of values in key-value format.', required=False, default=-1) parser.add_argument('--junit-xml', help='The path to junit-xml file.', dest="junit_xml", required=False, default=None) parser.add_argument('--git-hub-action-run-id', dest='run_id', help='The run ID of GitHub action run.', required=True) parser.add_argument('--git-hub-workflow', dest='workflow', help='The name of a workflow or a path to workflow file.', required=True) parser.add_argument('--git-hub-action', dest='action', help='Git hub action or step.', required=True) parser.add_argument('--git-branch', dest='branch', help='Git hub Branch.', required=True) args = parser.parse_args() main(args.activity, args.value, args.run_id, args.workflow, args.action, args.branch, args.junit_xml)