kopia lustrzana https://github.com/espressif/esp-idf
132 wiersze
4.6 KiB
Python
132 wiersze
4.6 KiB
Python
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""This file is used for generating the child pipeline for target test jobs.
|
|
|
|
1. Check the build jobs' artifacts to get the built apps' information.
|
|
2. Post the Build Report if it's running in an MR pipeline.
|
|
3. Generate the child pipeline for target test jobs.
|
|
"""
|
|
|
|
import argparse
|
|
import glob
|
|
import os
|
|
import typing as t
|
|
from collections import Counter, defaultdict
|
|
|
|
import __init__ # noqa: F401 # inject the system path
|
|
from dynamic_pipelines.constants import (DEFAULT_CASES_TEST_PER_JOB, DEFAULT_TARGET_TEST_CHILD_PIPELINE_FILEPATH,
|
|
DEFAULT_TEST_PATHS)
|
|
from dynamic_pipelines.models import EmptyJob, Job, TargetTestJob
|
|
from dynamic_pipelines.utils import dump_jobs_to_yaml
|
|
from gitlab.v4.objects import Project
|
|
from gitlab_api import Gitlab
|
|
from idf_build_apps import App
|
|
from idf_ci.app import import_apps_from_txt
|
|
from idf_pytest.script import get_pytest_cases
|
|
|
|
|
|
def get_tags_with_amount(s: str) -> t.List[str]:
|
|
c: Counter = Counter()
|
|
for _t in s.split(','):
|
|
c[_t] += 1
|
|
|
|
res = set()
|
|
for target, amount in c.items():
|
|
if amount > 1:
|
|
res.add(f'{target}_{amount}')
|
|
else:
|
|
res.add(target)
|
|
|
|
return sorted(res)
|
|
|
|
|
|
def generate_target_test_child_pipeline(project: Project, paths: str, apps: t.List[App], output_filepath: str) -> None:
|
|
pytest_cases = get_pytest_cases(
|
|
paths,
|
|
apps=apps,
|
|
marker_expr='not host_test', # since it's generating target-test child pipeline
|
|
)
|
|
|
|
res = defaultdict(list)
|
|
for case in pytest_cases:
|
|
if not case.env_markers:
|
|
print(f'No env markers found for {case.item.originalname} in {case.path}. Ignoring...')
|
|
continue
|
|
|
|
res[(case.target_selector, tuple(sorted(case.env_markers)))].append(case)
|
|
|
|
target_test_jobs: t.List[Job] = []
|
|
for (target_selector, env_markers), cases in res.items():
|
|
runner_tags = get_tags_with_amount(target_selector) + list(env_markers)
|
|
# we don't need to get all runner, as long as we get one runner, it's fine
|
|
runner_list = project.runners.list(status='online', tag_list=','.join(runner_tags), get_all=False)
|
|
if not runner_list:
|
|
print(f'WARNING: No runner found with tag {",".join(runner_tags)}, ignoring the following test cases:')
|
|
for case in cases:
|
|
print(f' - {case.name}')
|
|
continue
|
|
|
|
target_test_job = TargetTestJob(
|
|
name=f'{target_selector} - {",".join(env_markers)}',
|
|
tags=runner_tags,
|
|
parallel=len(cases) // DEFAULT_CASES_TEST_PER_JOB + 1,
|
|
)
|
|
target_test_job.set_variable('TARGET_SELECTOR', f"'{target_selector}'")
|
|
target_test_job.set_variable('ENV_MARKERS', "'" + ' and '.join(env_markers) + "'")
|
|
target_test_job.set_variable('PYTEST_NODES', ' '.join([f"'{case.item.nodeid}'" for case in cases]))
|
|
|
|
target_test_jobs.append(target_test_job)
|
|
|
|
if not target_test_jobs:
|
|
print('No target test cases required, create one empty job instead')
|
|
target_test_jobs.append(EmptyJob())
|
|
extra_include_yml = []
|
|
else:
|
|
extra_include_yml = ['tools/ci/dynamic_pipelines/templates/generate_target_test_report.yml']
|
|
|
|
dump_jobs_to_yaml(target_test_jobs, output_filepath, extra_include_yml)
|
|
print(f'Generate child pipeline yaml file {output_filepath} with {sum(j.parallel for j in target_test_jobs)} jobs')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(
|
|
description='Generate Target Test Child Pipeline. Update Build Report in MR pipelines',
|
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
)
|
|
parser.add_argument(
|
|
'-p',
|
|
'--paths',
|
|
nargs='+',
|
|
default=DEFAULT_TEST_PATHS,
|
|
help='Paths to the apps to build.',
|
|
)
|
|
parser.add_argument(
|
|
'--project-id',
|
|
type=int,
|
|
default=os.getenv('CI_PROJECT_ID'),
|
|
help='Project ID',
|
|
)
|
|
parser.add_argument(
|
|
'--pipeline-id',
|
|
type=int,
|
|
default=os.getenv('PARENT_PIPELINE_ID'),
|
|
help='Pipeline ID',
|
|
)
|
|
parser.add_argument(
|
|
'-o',
|
|
'--output',
|
|
default=DEFAULT_TARGET_TEST_CHILD_PIPELINE_FILEPATH,
|
|
help='Output child pipeline file path',
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
app_list_filepattern = 'list_job_*.txt'
|
|
apps = []
|
|
for f in glob.glob(app_list_filepattern):
|
|
apps.extend(import_apps_from_txt(f))
|
|
|
|
gl_project = Gitlab(args.project_id).project
|
|
generate_target_test_child_pipeline(gl_project, args.paths, apps, args.output)
|