mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-02 18:10:57 +02:00
Merge branch 'ci/fix_bin_size_report_generation' into 'master'
ci: fix bin size report generation See merge request espressif/esp-idf!41065
This commit is contained in:
@@ -23,7 +23,7 @@ def main() -> None:
|
|||||||
parser: argparse.ArgumentParser = setup_argument_parser()
|
parser: argparse.ArgumentParser = setup_argument_parser()
|
||||||
args: argparse.Namespace = parser.parse_args()
|
args: argparse.Namespace = parser.parse_args()
|
||||||
|
|
||||||
report_actions: t.Dict[str, t.Callable[[argparse.Namespace], None]] = {
|
report_actions: dict[str, t.Callable[[argparse.Namespace], None]] = {
|
||||||
'build': generate_build_report,
|
'build': generate_build_report,
|
||||||
'target_test': generate_target_test_report,
|
'target_test': generate_target_test_report,
|
||||||
'job': generate_jobs_report,
|
'job': generate_jobs_report,
|
||||||
@@ -42,7 +42,7 @@ def setup_argument_parser() -> argparse.ArgumentParser:
|
|||||||
'--report-type', choices=['build', 'target_test', 'job'], required=True, help='Type of report to generate'
|
'--report-type', choices=['build', 'target_test', 'job'], required=True, help='Type of report to generate'
|
||||||
)
|
)
|
||||||
report_type_args: argparse.Namespace
|
report_type_args: argparse.Namespace
|
||||||
remaining_args: t.List[str]
|
remaining_args: list[str]
|
||||||
report_type_args, remaining_args = report_type_parser.parse_known_args()
|
report_type_args, remaining_args = report_type_parser.parse_known_args()
|
||||||
|
|
||||||
parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
||||||
@@ -105,7 +105,7 @@ def generate_build_report(args: argparse.Namespace) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def generate_target_test_report(args: argparse.Namespace) -> None:
|
def generate_target_test_report(args: argparse.Namespace) -> None:
|
||||||
test_cases: t.List[t.Any] = parse_testcases_from_filepattern(args.junit_report_filepattern)
|
test_cases: list[t.Any] = parse_testcases_from_filepattern(args.junit_report_filepattern)
|
||||||
report_generator = TargetTestReportGenerator(
|
report_generator = TargetTestReportGenerator(
|
||||||
args.project_id,
|
args.project_id,
|
||||||
args.mr_iid,
|
args.mr_iid,
|
||||||
@@ -123,7 +123,7 @@ def generate_target_test_report(args: argparse.Namespace) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def generate_jobs_report(args: argparse.Namespace) -> None:
|
def generate_jobs_report(args: argparse.Namespace) -> None:
|
||||||
jobs: t.List[t.Any] = fetch_failed_jobs(args.commit_id)
|
jobs: list[t.Any] = fetch_failed_jobs(args.commit_id)
|
||||||
|
|
||||||
if not jobs:
|
if not jobs:
|
||||||
return
|
return
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import typing as t
|
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
@@ -20,7 +19,7 @@ from .models import GitlabJob
|
|||||||
from .models import TestCase
|
from .models import TestCase
|
||||||
|
|
||||||
|
|
||||||
def parse_testcases_from_filepattern(junit_report_filepattern: str) -> t.List[TestCase]:
|
def parse_testcases_from_filepattern(junit_report_filepattern: str) -> list[TestCase]:
|
||||||
"""
|
"""
|
||||||
Parses test cases from XML files matching the provided file pattern.
|
Parses test cases from XML files matching the provided file pattern.
|
||||||
|
|
||||||
@@ -38,12 +37,12 @@ def parse_testcases_from_filepattern(junit_report_filepattern: str) -> t.List[Te
|
|||||||
return test_cases
|
return test_cases
|
||||||
|
|
||||||
|
|
||||||
def load_known_failure_cases() -> t.Optional[t.Set[str]]:
|
def load_known_failure_cases() -> set[str] | None:
|
||||||
known_failures_file = os.getenv('KNOWN_FAILURE_CASES_FILE_NAME', '')
|
known_failures_file = os.getenv('KNOWN_FAILURE_CASES_FILE_NAME', '')
|
||||||
if not known_failures_file:
|
if not known_failures_file:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
with open(known_failures_file, 'r') as f:
|
with open(known_failures_file) as f:
|
||||||
file_content = f.read()
|
file_content = f.read()
|
||||||
|
|
||||||
pattern = re.compile(r'^(.*?)\s+#\s+([A-Z]+)-\d+', re.MULTILINE)
|
pattern = re.compile(r'^(.*?)\s+#\s+([A-Z]+)-\d+', re.MULTILINE)
|
||||||
@@ -66,7 +65,7 @@ def is_url(string: str) -> bool:
|
|||||||
return bool(parsed.scheme) and bool(parsed.netloc)
|
return bool(parsed.scheme) and bool(parsed.netloc)
|
||||||
|
|
||||||
|
|
||||||
def fetch_failed_jobs(commit_id: str) -> t.List[GitlabJob]:
|
def fetch_failed_jobs(commit_id: str) -> list[GitlabJob]:
|
||||||
"""
|
"""
|
||||||
Fetches a list of jobs from the specified commit_id using an API request to ci-dashboard-api.
|
Fetches a list of jobs from the specified commit_id using an API request to ci-dashboard-api.
|
||||||
:param commit_id: The commit ID for which to fetch jobs.
|
:param commit_id: The commit ID for which to fetch jobs.
|
||||||
@@ -110,7 +109,7 @@ def fetch_failed_jobs(commit_id: str) -> t.List[GitlabJob]:
|
|||||||
return combined_jobs
|
return combined_jobs
|
||||||
|
|
||||||
|
|
||||||
def fetch_failed_testcases_failure_ratio(failed_testcases: t.List[TestCase], branches_filter: dict) -> t.List[TestCase]:
|
def fetch_failed_testcases_failure_ratio(failed_testcases: list[TestCase], branches_filter: dict) -> list[TestCase]:
|
||||||
"""
|
"""
|
||||||
Fetches info about failure rates of testcases using an API request to ci-dashboard-api.
|
Fetches info about failure rates of testcases using an API request to ci-dashboard-api.
|
||||||
:param failed_testcases: The list of failed testcases models.
|
:param failed_testcases: The list of failed testcases models.
|
||||||
@@ -140,13 +139,14 @@ def fetch_failed_testcases_failure_ratio(failed_testcases: t.List[TestCase], bra
|
|||||||
def fetch_app_metrics(
|
def fetch_app_metrics(
|
||||||
source_commit_sha: str,
|
source_commit_sha: str,
|
||||||
target_commit_sha: str,
|
target_commit_sha: str,
|
||||||
) -> t.Dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
Fetches the app metrics for the given source commit SHA and target branch SHA.
|
Fetches the app metrics for the given source commit SHA and target branch SHA.
|
||||||
:param source_commit_sha: The source commit SHA.
|
:param source_commit_sha: The source commit SHA.
|
||||||
:param target_branch_sha: The commit SHA of the branch to compare app sizes against.
|
:param target_branch_sha: The commit SHA of the branch to compare app sizes against.
|
||||||
:return: A dict of sizes of built binaries.
|
:return: A dict of sizes of built binaries.
|
||||||
"""
|
"""
|
||||||
|
print(f'Fetching bin size info: {source_commit_sha=} {target_commit_sha=}')
|
||||||
build_info_map = dict()
|
build_info_map = dict()
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
f'{CI_DASHBOARD_API}/apps/metrics',
|
f'{CI_DASHBOARD_API}/apps/metrics',
|
||||||
@@ -174,7 +174,7 @@ def load_file(file_path: str) -> str:
|
|||||||
:param file_path: The path to the file needs to be loaded.
|
:param file_path: The path to the file needs to be loaded.
|
||||||
:return: The content of the file as a string.
|
:return: The content of the file as a string.
|
||||||
"""
|
"""
|
||||||
with open(file_path, 'r') as file:
|
with open(file_path) as file:
|
||||||
return file.read()
|
return file.read()
|
||||||
|
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@ from dynamic_pipelines.constants import BINARY_SIZE_METRIC_NAME
|
|||||||
from idf_build_apps import App
|
from idf_build_apps import App
|
||||||
from idf_build_apps import CMakeApp
|
from idf_build_apps import CMakeApp
|
||||||
from idf_build_apps.utils import rmdir
|
from idf_build_apps.utils import rmdir
|
||||||
|
from idf_ci_utils import idf_relpath
|
||||||
|
|
||||||
if t.TYPE_CHECKING:
|
if t.TYPE_CHECKING:
|
||||||
pass
|
pass
|
||||||
@@ -53,17 +54,17 @@ class Metrics:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
source_value: t.Optional[float] = None,
|
source_value: float | None = None,
|
||||||
target_value: t.Optional[float] = None,
|
target_value: float | None = None,
|
||||||
difference: t.Optional[float] = None,
|
difference: float | None = None,
|
||||||
difference_percentage: t.Optional[float] = None,
|
difference_percentage: float | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.source_value = source_value or 0.0
|
self.source_value = source_value or 0.0
|
||||||
self.target_value = target_value or 0.0
|
self.target_value = target_value or 0.0
|
||||||
self.difference = difference or 0.0
|
self.difference = difference or 0.0
|
||||||
self.difference_percentage = difference_percentage or 0.0
|
self.difference_percentage = difference_percentage or 0.0
|
||||||
|
|
||||||
def to_dict(self) -> t.Dict[str, t.Any]:
|
def to_dict(self) -> dict[str, t.Any]:
|
||||||
"""
|
"""
|
||||||
Converts the Metrics object to a dictionary.
|
Converts the Metrics object to a dictionary.
|
||||||
"""
|
"""
|
||||||
@@ -76,7 +77,7 @@ class Metrics:
|
|||||||
|
|
||||||
|
|
||||||
class AppWithMetricsInfo(IdfCMakeApp):
|
class AppWithMetricsInfo(IdfCMakeApp):
|
||||||
metrics: t.Dict[str, Metrics]
|
metrics: dict[str, Metrics]
|
||||||
is_new_app: bool
|
is_new_app: bool
|
||||||
|
|
||||||
def __init__(self, **kwargs: t.Any) -> None:
|
def __init__(self, **kwargs: t.Any) -> None:
|
||||||
@@ -90,13 +91,13 @@ class AppWithMetricsInfo(IdfCMakeApp):
|
|||||||
|
|
||||||
|
|
||||||
def enrich_apps_with_metrics_info(
|
def enrich_apps_with_metrics_info(
|
||||||
app_metrics_info_map: t.Dict[str, t.Dict[str, t.Any]], apps: t.List[App]
|
app_metrics_info_map: dict[str, dict[str, t.Any]], apps: list[App]
|
||||||
) -> t.List[AppWithMetricsInfo]:
|
) -> list[AppWithMetricsInfo]:
|
||||||
def _get_full_attributes(obj: App) -> t.Dict[str, t.Any]:
|
def _get_full_attributes(obj: App) -> dict[str, t.Any]:
|
||||||
"""
|
"""
|
||||||
Retrieves all attributes of an object, including properties and computed fields.
|
Retrieves all attributes of an object, including properties and computed fields.
|
||||||
"""
|
"""
|
||||||
attributes: t.Dict[str, t.Any] = obj.__dict__.copy()
|
attributes: dict[str, t.Any] = obj.__dict__.copy()
|
||||||
for attr in dir(obj):
|
for attr in dir(obj):
|
||||||
if not attr.startswith('_'): # Skip private/internal attributes
|
if not attr.startswith('_'): # Skip private/internal attributes
|
||||||
try:
|
try:
|
||||||
@@ -120,6 +121,7 @@ def enrich_apps_with_metrics_info(
|
|||||||
|
|
||||||
apps_with_metrics_info = []
|
apps_with_metrics_info = []
|
||||||
for app in apps:
|
for app in apps:
|
||||||
|
app.app_dir = idf_relpath(app.app_dir)
|
||||||
key = f'{app.app_dir}_{app.config_name}_{app.target}'
|
key = f'{app.app_dir}_{app.config_name}_{app.target}'
|
||||||
app_attributes = _get_full_attributes(app)
|
app_attributes = _get_full_attributes(app)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user