mirror of
https://github.com/espressif/esp-idf.git
synced 2024-09-20 00:36:01 -04:00
Merge branch 'ci/known_generate_target_test_issues' into 'master'
ci: add known warnings while generating the target test jobs Closes IDFCI-1941 See merge request espressif/esp-idf!28551
This commit is contained in:
commit
7913c42996
@ -287,6 +287,7 @@ generate_build_child_pipeline:
|
||||
dependencies: # set dependencies to null to avoid missing artifacts issue
|
||||
needs:
|
||||
- pipeline_variables
|
||||
- check_test_cases_env_markers_and_required_runners
|
||||
artifacts:
|
||||
paths:
|
||||
- build_child_pipeline.yml
|
||||
|
@ -170,3 +170,9 @@ pipeline_variables:
|
||||
artifacts:
|
||||
reports:
|
||||
dotenv: pipeline.env
|
||||
|
||||
check_test_cases_env_markers_and_required_runners:
|
||||
extends:
|
||||
- .pre_check_template
|
||||
script:
|
||||
- python tools/ci/dynamic_pipelines/scripts/generate_target_test_child_pipeline.py --check
|
||||
|
@ -157,13 +157,19 @@ repos:
|
||||
additional_dependencies:
|
||||
- PyYAML == 5.3.1
|
||||
- idf-build-apps~=2.0
|
||||
- id: sort-build-test-rules-ymls
|
||||
name: sort .build-test-rules.yml files
|
||||
entry: tools/ci/check_build_test_rules.py sort-yaml
|
||||
- id: sort-yaml-files
|
||||
name: sort yaml files
|
||||
entry: tools/ci/sort_yaml.py
|
||||
language: python
|
||||
files: '\.build-test-rules\.yml'
|
||||
files: '\.build-test-rules\.yml$|known_generate_test_child_pipeline_warnings\.yml$'
|
||||
additional_dependencies:
|
||||
- ruamel.yaml
|
||||
- id: sort-yaml-test
|
||||
name: sort yaml test
|
||||
entry: python -m unittest tools/ci/sort_yaml.py
|
||||
language: python
|
||||
files: 'tools/ci/sort_yaml\.py$'
|
||||
additional_dependencies:
|
||||
- PyYAML == 5.3.1
|
||||
- ruamel.yaml
|
||||
- id: check-build-test-rules-path-exists
|
||||
name: check path in .build-test-rules.yml exists
|
||||
|
18
conftest.py
18
conftest.py
@ -11,8 +11,6 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gitlab
|
||||
|
||||
if os.path.join(os.path.dirname(__file__), 'tools', 'ci') not in sys.path:
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'tools', 'ci'))
|
||||
|
||||
@ -161,22 +159,8 @@ def app_downloader(pipeline_id: t.Optional[str]) -> t.Optional[AppDownloader]:
|
||||
|
||||
logging.info('Downloading build report from the build pipeline %s', pipeline_id)
|
||||
test_app_presigned_urls_file = None
|
||||
try:
|
||||
gl = gitlab_api.Gitlab(os.getenv('CI_PROJECT_ID', 'espressif/esp-idf'))
|
||||
except gitlab.exceptions.GitlabAuthenticationError:
|
||||
msg = """To download artifacts from gitlab, please create ~/.python-gitlab.cfg with the following content:
|
||||
|
||||
[global]
|
||||
default = internal
|
||||
ssl_verify = true
|
||||
timeout = 5
|
||||
|
||||
[internal]
|
||||
url = <OUR INTERNAL HTTPS SERVER URL>
|
||||
private_token = <YOUR PERSONAL ACCESS TOKEN>
|
||||
api_version = 4
|
||||
"""
|
||||
raise SystemExit(msg)
|
||||
gl = gitlab_api.Gitlab(os.getenv('CI_PROJECT_ID', 'espressif/esp-idf'))
|
||||
|
||||
for child_pipeline in gl.project.pipelines.get(pipeline_id, lazy=True).bridges.list(iterator=True):
|
||||
if child_pipeline.name == 'build_child_pipeline':
|
||||
|
@ -6,7 +6,6 @@ import inspect
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
@ -345,38 +344,6 @@ def check_test_scripts(
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
def sort_yaml(files: List[str]) -> None:
|
||||
from ruamel.yaml import YAML, CommentedMap
|
||||
|
||||
yaml = YAML()
|
||||
yaml.indent(mapping=2, sequence=4, offset=2)
|
||||
yaml.width = 4096 # avoid wrap lines
|
||||
|
||||
exit_code = 0
|
||||
for f in files:
|
||||
with open(f) as fr:
|
||||
file_s = fr.read()
|
||||
fr.seek(0)
|
||||
file_d: CommentedMap = yaml.load(fr)
|
||||
|
||||
sorted_yaml = CommentedMap(dict(sorted(file_d.items())))
|
||||
file_d.copy_attributes(sorted_yaml)
|
||||
|
||||
with StringIO() as s:
|
||||
yaml.dump(sorted_yaml, s)
|
||||
|
||||
string = s.getvalue()
|
||||
if string != file_s:
|
||||
with open(f, 'w') as fw:
|
||||
fw.write(string)
|
||||
print(
|
||||
f'Sorted yaml file {f}. Please take a look. sometimes the format is a bit messy'
|
||||
)
|
||||
exit_code = 1
|
||||
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
def check_exist() -> None:
|
||||
exit_code = 0
|
||||
|
||||
@ -422,9 +389,6 @@ if __name__ == '__main__':
|
||||
help='default build test rules config file',
|
||||
)
|
||||
|
||||
_sort_yaml = action.add_parser('sort-yaml')
|
||||
_sort_yaml.add_argument('files', nargs='+', help='all specified yaml files')
|
||||
|
||||
_check_exist = action.add_parser('check-exist')
|
||||
|
||||
arg = parser.parse_args()
|
||||
@ -434,9 +398,7 @@ if __name__ == '__main__':
|
||||
os.path.join(os.path.dirname(__file__), '..', '..')
|
||||
)
|
||||
|
||||
if arg.action == 'sort-yaml':
|
||||
sort_yaml(arg.files)
|
||||
elif arg.action == 'check-exist':
|
||||
if arg.action == 'check-exist':
|
||||
check_exist()
|
||||
else:
|
||||
check_dirs = set()
|
||||
|
@ -1,6 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import os
|
||||
|
||||
from idf_ci_utils import IDF_PATH
|
||||
@ -31,3 +30,7 @@ REPORT_TEMPLATE_FILEPATH = os.path.join(
|
||||
)
|
||||
|
||||
BUILD_ONLY_LABEL = 'For Maintainers: Only Build Tests'
|
||||
|
||||
KNOWN_GENERATE_TEST_CHILD_PIPELINE_WARNINGS_FILEPATH = os.path.join(
|
||||
IDF_PATH, 'tools', 'ci', 'dynamic_pipelines', 'templates', 'known_generate_test_child_pipeline_warnings.yml'
|
||||
)
|
||||
|
@ -1,28 +1,36 @@
|
||||
# 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 logging
|
||||
import os
|
||||
import typing as t
|
||||
from collections import Counter, defaultdict
|
||||
from collections import Counter
|
||||
from collections import defaultdict
|
||||
|
||||
import __init__ # noqa: F401 # inject the system path
|
||||
from dynamic_pipelines.constants import (BUILD_ONLY_LABEL, DEFAULT_CASES_TEST_PER_JOB,
|
||||
DEFAULT_TARGET_TEST_CHILD_PIPELINE_FILEPATH, DEFAULT_TEST_PATHS)
|
||||
from dynamic_pipelines.models import EmptyJob, Job, TargetTestJob
|
||||
import yaml
|
||||
from dynamic_pipelines.constants import BUILD_ONLY_LABEL
|
||||
from dynamic_pipelines.constants import DEFAULT_CASES_TEST_PER_JOB
|
||||
from dynamic_pipelines.constants import DEFAULT_TARGET_TEST_CHILD_PIPELINE_FILEPATH
|
||||
from dynamic_pipelines.constants import DEFAULT_TEST_PATHS
|
||||
from dynamic_pipelines.constants import KNOWN_GENERATE_TEST_CHILD_PIPELINE_WARNINGS_FILEPATH
|
||||
from dynamic_pipelines.models import EmptyJob
|
||||
from dynamic_pipelines.models import Job
|
||||
from dynamic_pipelines.models import 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_build_apps.constants import BuildStatus
|
||||
from idf_ci.app import import_apps_from_txt
|
||||
from idf_pytest.script import get_all_apps
|
||||
from idf_pytest.script import get_pytest_cases
|
||||
|
||||
|
||||
@ -41,16 +49,23 @@ def get_tags_with_amount(s: str) -> t.List[str]:
|
||||
return sorted(res)
|
||||
|
||||
|
||||
def get_target_test_jobs(project: Project, paths: str, apps: t.List[App]) -> t.Tuple[t.List[Job], t.List[str]]:
|
||||
def get_target_test_jobs(
|
||||
project: Project, paths: str, apps: t.List[App]
|
||||
) -> t.Tuple[t.List[Job], t.List[str], t.Dict[str, t.List[str]]]:
|
||||
"""
|
||||
Return the target test jobs and the extra yaml files to include
|
||||
"""
|
||||
issues: t.Dict[str, t.List[str]] = {
|
||||
'no_env_marker_test_cases': [],
|
||||
'no_runner_tags': [],
|
||||
}
|
||||
|
||||
if mr_labels := os.getenv('CI_MERGE_REQUEST_LABELS'):
|
||||
print(f'MR labels: {mr_labels}')
|
||||
|
||||
if BUILD_ONLY_LABEL in mr_labels.split(','):
|
||||
print('MR has build only label, skip generating target test child pipeline')
|
||||
return [EmptyJob()], []
|
||||
return [EmptyJob()], [], issues
|
||||
|
||||
pytest_cases = get_pytest_cases(
|
||||
paths,
|
||||
@ -61,7 +76,7 @@ def get_target_test_jobs(project: Project, paths: str, apps: t.List[App]) -> t.T
|
||||
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...')
|
||||
issues['no_env_marker_test_cases'].append(case.item.nodeid)
|
||||
continue
|
||||
|
||||
res[(case.target_selector, tuple(sorted(case.env_markers)))].append(case)
|
||||
@ -72,9 +87,10 @@ def get_target_test_jobs(project: Project, paths: str, apps: t.List[App]) -> t.T
|
||||
# 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:')
|
||||
issues['no_runner_tags'].append(','.join(runner_tags))
|
||||
logging.warning(f'No runner found for {",".join(runner_tags)}, required by cases:')
|
||||
for case in cases:
|
||||
print(f' - {case.name}')
|
||||
logging.warning(f' - {case.item.nodeid}')
|
||||
continue
|
||||
|
||||
target_test_job = TargetTestJob(
|
||||
@ -95,11 +111,48 @@ def get_target_test_jobs(project: Project, paths: str, apps: t.List[App]) -> t.T
|
||||
else:
|
||||
extra_include_yml = ['tools/ci/dynamic_pipelines/templates/generate_target_test_report.yml']
|
||||
|
||||
return target_test_jobs, extra_include_yml
|
||||
issues['no_env_marker_test_cases'] = sorted(issues['no_env_marker_test_cases'])
|
||||
issues['no_runner_tags'] = sorted(issues['no_runner_tags'])
|
||||
|
||||
return target_test_jobs, extra_include_yml, issues
|
||||
|
||||
|
||||
def generate_target_test_child_pipeline(project: Project, paths: str, apps: t.List[App], output_filepath: str) -> None:
|
||||
target_test_jobs, extra_include_yml = get_target_test_jobs(project, paths, apps)
|
||||
def generate_target_test_child_pipeline(
|
||||
project: Project,
|
||||
paths: str,
|
||||
apps: t.List[App],
|
||||
output_filepath: str,
|
||||
) -> None:
|
||||
target_test_jobs, extra_include_yml, issues = get_target_test_jobs(project, paths, apps)
|
||||
|
||||
with open(KNOWN_GENERATE_TEST_CHILD_PIPELINE_WARNINGS_FILEPATH) as fr:
|
||||
known_warnings_dict = yaml.safe_load(fr) or dict()
|
||||
|
||||
failed = False
|
||||
known_no_env_marker_test_cases = set(known_warnings_dict.get('no_env_marker_test_cases', []))
|
||||
no_env_marker_test_cases = set(issues['no_env_marker_test_cases'])
|
||||
|
||||
if no_env_marker_test_cases - known_no_env_marker_test_cases:
|
||||
print('ERROR: NEW "no_env_marker_test_cases" detected:')
|
||||
for case in no_env_marker_test_cases - known_no_env_marker_test_cases:
|
||||
print(f' - {case}')
|
||||
failed = True
|
||||
|
||||
known_no_runner_tags = set(known_warnings_dict.get('no_runner_tags', []))
|
||||
no_runner_tags = set(issues['no_runner_tags'])
|
||||
|
||||
if no_runner_tags - known_no_runner_tags:
|
||||
print('ERROR: NEW "no_runner_tags" detected:')
|
||||
for tag in no_runner_tags - known_no_runner_tags:
|
||||
print(f' - {tag}')
|
||||
failed = True
|
||||
|
||||
if failed:
|
||||
raise SystemExit(
|
||||
f'Please fix the issue, '
|
||||
f'or update the known warnings file: {KNOWN_GENERATE_TEST_CHILD_PIPELINE_WARNINGS_FILEPATH}'
|
||||
)
|
||||
|
||||
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')
|
||||
|
||||
@ -134,13 +187,37 @@ if __name__ == '__main__':
|
||||
default=DEFAULT_TARGET_TEST_CHILD_PIPELINE_FILEPATH,
|
||||
help='Output child pipeline file path',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--check',
|
||||
action='store_true',
|
||||
help='Check if the child pipeline could be generated successfully. '
|
||||
'test cases without env marker or required unset runner will be printed out. '
|
||||
'(Note: All apps and test cases will be checked)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--app-info-filepattern',
|
||||
default='list_job_*.txt',
|
||||
help='glob pattern to specify the files that include built app info generated by '
|
||||
'`idf-build-apps --collect-app-info ...`. will not raise ValueError when binary '
|
||||
'paths not exist in local file system if not listed recorded in the app info.',
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
if args.check:
|
||||
apps = list(get_all_apps(args.paths)[0]) # test related apps only
|
||||
for app in apps:
|
||||
app.build_status = BuildStatus.SUCCESS # pretend they are built successfully
|
||||
else:
|
||||
apps = []
|
||||
for f in glob.glob(args.app_info_filepattern):
|
||||
apps.extend(import_apps_from_txt(f))
|
||||
|
||||
generate_target_test_child_pipeline(
|
||||
gl_project,
|
||||
args.paths,
|
||||
apps,
|
||||
args.output,
|
||||
)
|
||||
|
@ -0,0 +1,53 @@
|
||||
no_env_marker_test_cases:
|
||||
- components/fatfs/test_apps/flash_ro/pytest_fatfs_flash_ro.py::test_fatfs_flash_ro
|
||||
- components/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py::test_fatfs_flash_wl_generic[default]
|
||||
- components/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py::test_fatfs_flash_wl_generic[fastseek]
|
||||
- components/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py::test_fatfs_flash_wl_generic[release]
|
||||
- components/nvs_flash/test_apps/pytest_nvs_flash.py::test_nvs_flash[default]
|
||||
- components/pthread/test_apps/pthread_psram_tests/pytest_pthread_psram_tests.py::test_pthread_psram
|
||||
- components/vfs/test_apps/pytest_vfs.py::test_vfs_ccomp[ccomp]
|
||||
- components/vfs/test_apps/pytest_vfs.py::test_vfs_default[default]
|
||||
- components/vfs/test_apps/pytest_vfs.py::test_vfs_default[iram]
|
||||
- examples/protocols/http_server/file_serving/pytest_http_server_file_serving.py::test_examples_protocol_http_server_file_serving[spiffs]
|
||||
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_only_partition_gen]
|
||||
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_only_partition_gen_default_dt]
|
||||
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_only_partition_gen_ln]
|
||||
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_only_partition_gen_ln_default_dt]
|
||||
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_write_partition_gen]
|
||||
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_write_partition_gen_default_dt]
|
||||
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_write_partition_gen_ln]
|
||||
- examples/storage/fatfsgen/pytest_fatfsgen_example.py::test_examples_fatfsgen[test_read_write_partition_gen_ln_default_dt]
|
||||
- examples/storage/nvs_rw_blob/pytest_nvs_rw_blob.py::test_examples_nvs_rw_blob
|
||||
- examples/storage/nvs_rw_value/pytest_nvs_rw_value.py::test_examples_nvs_rw_value
|
||||
- examples/storage/nvs_rw_value_cxx/pytest_nvs_rw_value_cxx.py::test_examples_nvs_rw_value_cxx
|
||||
- examples/storage/nvsgen/pytest_nvsgen_example.py::test_nvsgen_example
|
||||
- examples/storage/partition_api/partition_find/pytest_partition_find_example.py::test_partition_find_example
|
||||
- examples/storage/partition_api/partition_mmap/pytest_partition_mmap_example.py::test_partition_mmap_example
|
||||
- examples/storage/partition_api/partition_ops/pytest_partition_ops_example.py::test_partition_ops_example
|
||||
- examples/storage/parttool/pytest_parttool_example.py::test_examples_parttool
|
||||
- examples/storage/spiffs/pytest_spiffs_example.py::test_examples_spiffs
|
||||
- examples/storage/spiffsgen/pytest_spiffsgen_example.py::test_spiffsgen_example
|
||||
- examples/storage/wear_levelling/pytest_wear_levelling_example.py::test_wear_levelling_example
|
||||
- tools/test_apps/system/panic/pytest_panic.py::test_gdbstub_coredump[gdbstub_coredump]
|
||||
- tools/test_apps/system/panic/pytest_panic.py::test_panic_delay[panic_delay]
|
||||
no_runner_tags:
|
||||
- esp32,ip101
|
||||
- esp32,psram,quad_psram
|
||||
- esp32,quad_psram
|
||||
- esp32c2,ethernet,xtal_40mhz
|
||||
- esp32c2,ethernet_ota,xtal_40mhz
|
||||
- esp32c2,jtag,xtal_40mhz
|
||||
- esp32c3,Example_ShieldBox_Basic
|
||||
- esp32c3,ethernet_flash_8m
|
||||
- esp32c3,ethernet_ota
|
||||
- esp32c3,sdcard_sdmode
|
||||
- esp32c6,jtag
|
||||
- esp32h2,jtag
|
||||
- esp32s2,Example_ShieldBox_Basic
|
||||
- esp32s2,ethernet_flash_8m
|
||||
- esp32s2,ethernet_ota
|
||||
- esp32s2,usb_host_flash_disk
|
||||
- esp32s2,wifi_high_traffic
|
||||
- esp32s3,Example_ShieldBox_Basic
|
||||
- esp32s3,ethernet_flash_8m
|
||||
- esp32s3,ethernet_ota
|
@ -50,3 +50,4 @@ tools/ci/python_packages/gitlab_api.py
|
||||
tools/ci/python_packages/idf_http_server_test/**/*
|
||||
tools/ci/python_packages/idf_iperf_test_util/**/*
|
||||
tools/esp_prov/**/*
|
||||
tools/ci/sort_yaml.py
|
||||
|
@ -77,6 +77,7 @@ tools/ci/gitlab_yaml_linter.py
|
||||
tools/ci/mirror-submodule-update.sh
|
||||
tools/ci/multirun_with_pyenv.sh
|
||||
tools/ci/push_to_github.sh
|
||||
tools/ci/sort_yaml.py
|
||||
tools/ci/test_autocomplete/test_autocomplete.py
|
||||
tools/ci/test_configure_ci_environment.sh
|
||||
tools/ci/test_reproducible_build.sh
|
||||
|
@ -1,6 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
@ -11,7 +10,12 @@ import tempfile
|
||||
import time
|
||||
import zipfile
|
||||
from functools import wraps
|
||||
from typing import Any, Callable, Dict, List, Optional, Union
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
import gitlab
|
||||
|
||||
@ -80,7 +84,24 @@ class Gitlab(object):
|
||||
def _init_gitlab_inst(self, project_id: Optional[int], config_files: Optional[List[str]]) -> None:
|
||||
gitlab_id = os.getenv('LOCAL_GITLAB_HTTPS_HOST') # if None, will use the default gitlab server
|
||||
self.gitlab_inst = gitlab.Gitlab.from_config(gitlab_id=gitlab_id, config_files=config_files)
|
||||
self.gitlab_inst.auth()
|
||||
|
||||
try:
|
||||
self.gitlab_inst.auth()
|
||||
except gitlab.exceptions.GitlabAuthenticationError:
|
||||
msg = """To call gitlab apis locally, please create ~/.python-gitlab.cfg with the following content:
|
||||
|
||||
[global]
|
||||
default = internal
|
||||
ssl_verify = true
|
||||
timeout = 5
|
||||
|
||||
[internal]
|
||||
url = <OUR INTERNAL HTTPS SERVER URL>
|
||||
private_token = <YOUR PERSONAL ACCESS TOKEN>
|
||||
api_version = 4
|
||||
"""
|
||||
raise SystemExit(msg)
|
||||
|
||||
if project_id:
|
||||
self.project = self.gitlab_inst.projects.get(project_id, lazy=True)
|
||||
else:
|
||||
|
94
tools/ci/sort_yaml.py
Executable file
94
tools/ci/sort_yaml.py
Executable file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""
|
||||
Sort yaml file
|
||||
|
||||
Exit non-zero if any file is modified
|
||||
"""
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from ruamel.yaml import CommentedMap
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
|
||||
def sort_yaml(f: str) -> int:
|
||||
yaml = YAML()
|
||||
yaml.indent(mapping=2, sequence=4, offset=2)
|
||||
yaml.width = 4096 # avoid wrap lines``
|
||||
|
||||
exit_code = 0
|
||||
with open(f) as fr:
|
||||
file_s = fr.read()
|
||||
fr.seek(0)
|
||||
|
||||
try:
|
||||
file_d: CommentedMap = yaml.load(fr)
|
||||
except Exception as e:
|
||||
print(f'Failed to load yaml file {f}: {e}')
|
||||
return 1
|
||||
|
||||
# sort dict keys
|
||||
sorted_yaml = CommentedMap(dict(sorted(file_d.items())))
|
||||
file_d.copy_attributes(sorted_yaml)
|
||||
|
||||
# sort item
|
||||
for k, v in sorted_yaml.items():
|
||||
if isinstance(v, list):
|
||||
sorted_yaml[k].sort()
|
||||
|
||||
with io.StringIO() as s:
|
||||
yaml.dump(sorted_yaml, s)
|
||||
|
||||
string = s.getvalue()
|
||||
if string != file_s:
|
||||
with open(f, 'w') as fw:
|
||||
fw.write(string)
|
||||
print(f'Sorted yaml file {f}. Please take a look. sometimes the format is a bit messy')
|
||||
exit_code = 1
|
||||
|
||||
return exit_code
|
||||
|
||||
|
||||
class TestSortYaml(unittest.TestCase):
|
||||
def test_sort_yaml(self) -> None:
|
||||
_, test_yaml = tempfile.mkstemp()
|
||||
with open(test_yaml, 'w') as fw:
|
||||
fw.write(
|
||||
'''no_runner: []
|
||||
no_env_marker:
|
||||
- 1
|
||||
- 3 # foo
|
||||
- 2 # bar'''
|
||||
)
|
||||
|
||||
sort_yaml(fw.name)
|
||||
|
||||
try:
|
||||
with open(test_yaml) as fr:
|
||||
self.assertEqual(
|
||||
fr.read(),
|
||||
'''no_env_marker:
|
||||
- 1
|
||||
- 2 # bard
|
||||
- 3 # foo
|
||||
no_runner: []''',
|
||||
)
|
||||
except AssertionError:
|
||||
print(f'Please check the sorted yaml file {test_yaml}')
|
||||
else:
|
||||
os.remove(test_yaml)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ret = 0
|
||||
for _f in sys.argv[1:]:
|
||||
exit_code = sort_yaml(_f)
|
||||
if exit_code != 0 and ret == 0:
|
||||
ret = exit_code
|
||||
|
||||
sys.exit(ret)
|
@ -1,6 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
@ -11,6 +10,7 @@ PROMPT = 'test_intr_dump>'
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.qemu
|
||||
@pytest.mark.host_test
|
||||
def test_esp_intr_dump_nonshared(dut: Dut) -> None:
|
||||
dut.expect_exact(PROMPT, timeout=10)
|
||||
|
||||
@ -24,6 +24,7 @@ def test_esp_intr_dump_nonshared(dut: Dut) -> None:
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.qemu
|
||||
@pytest.mark.host_test
|
||||
def test_esp_intr_dump_shared(dut: Dut) -> None:
|
||||
dut.expect_exact(PROMPT, timeout=10)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user