From 5e4f3308e43c33c2cecd05ab73c96586b5e78d94 Mon Sep 17 00:00:00 2001 From: Ondrej Kosta Date: Wed, 3 Jun 2026 09:45:32 +0200 Subject: [PATCH] feat(ci): support downloading extra app artifacts in pytest Add app_extra S3 artifact type, extend idf-ci download plumbing, and expose a download_app_extra pytest fixture for managed_components deps. --- .idf_ci.toml | 9 ++++++++ conftest.py | 40 ++++++++++++++++++++++++++++++++++++ tools/ci/idf_ci_local/app.py | 29 +++++++++++++++++++------- tools/ci/idf_ci_utils.py | 3 +++ 4 files changed, 74 insertions(+), 7 deletions(-) diff --git a/.idf_ci.toml b/.idf_ci.toml index 9d4f23d3fc0..c8ff21020f6 100644 --- a/.idf_ci.toml +++ b/.idf_ci.toml @@ -91,6 +91,15 @@ patterns = [ ] if_clause = 'CI_JOB_GROUP_NAME != "build_non_test_related_apps"' +[gitlab.artifacts.s3.configs.app_extra] +bucket = "idf-artifacts" +# App-dir files needed at target-test time but outside build_* (uploaded without --build-dir). +# Add patterns here as needed, e.g. coverage info produced during build. +patterns = [ + '**/managed_components/**/*.py', +] +if_clause = 'CI_JOB_GROUP_NAME != "build_non_test_related_apps"' + [gitlab.artifacts.s3.configs.log] bucket = "idf-artifacts" patterns = [ diff --git a/conftest.py b/conftest.py index b07f7670f10..0eadd337877 100644 --- a/conftest.py +++ b/conftest.py @@ -36,6 +36,7 @@ from _pytest.config import Config from _pytest.fixtures import FixtureRequest from idf_ci import PytestCase from idf_ci.idf_pytest import IDF_CI_PYTEST_CASE_KEY +from idf_ci_utils import APP_EXTRA_S3_ARTIFACT_TYPE from idf_ci_utils import IDF_PATH from idf_ci_utils import idf_relpath from idf_pytest.constants import DEFAULT_LOGDIR @@ -158,6 +159,33 @@ class AppDownloader: if result.stderr: logging.info(result.stderr) + def download_app_extra(self, app_dir: str) -> None: + """Download app-dir artifacts defined under app_extra in .idf_ci.toml.""" + args = [ + 'idf-ci', + 'gitlab', + 'download-artifacts', + '--commit-sha', + self.commit_sha, + '--type', + APP_EXTRA_S3_ARTIFACT_TYPE, + app_dir, + ] + if self.pipeline_id: + args.extend(['--pipeline-id', self.pipeline_id]) + + result = subprocess.run( + args, + capture_output=True, + text=True, + cwd=IDF_PATH, + ) + logging.info(result.stdout) + if result.stderr: + logging.info(result.stderr) + if result.returncode != 0: + raise RuntimeError(f'Failed to download {APP_EXTRA_S3_ARTIFACT_TYPE} artifacts for {app_dir}') + class OpenOCD: def __init__(self, dut: 'IdfDut'): @@ -303,6 +331,18 @@ def app_downloader( return AppDownloader(commit_sha, pipeline_id) +@pytest.fixture +def download_app_extra(app_downloader: AppDownloader | None) -> t.Callable[[str], None]: + """Download app_extra S3 artifacts for the given app path (no-op outside CI).""" + + def _download_app_extra(app_path: str) -> None: + if app_downloader is None: + return + app_downloader.download_app_extra(idf_relpath(app_path)) + + return _download_app_extra + + @pytest.fixture @multi_dut_fixture def build_dir( diff --git a/tools/ci/idf_ci_local/app.py b/tools/ci/idf_ci_local/app.py index 3cd4ccc21c4..8c7306fdef5 100644 --- a/tools/ci/idf_ci_local/app.py +++ b/tools/ci/idf_ci_local/app.py @@ -10,6 +10,7 @@ from idf_build_apps import App from idf_build_apps import CMakeApp from idf_build_apps.constants import BuildStatus from idf_build_apps.utils import rmdir +from idf_ci_utils import APP_EXTRA_S3_ARTIFACT_TYPE from idf_ci_utils import idf_relpath if t.TYPE_CHECKING: @@ -30,7 +31,7 @@ class IdfCMakeApp(CMakeApp): # only upload in CI if os.getenv('CI_JOB_ID'): - result = subprocess.run( + upload_commands = [ [ 'idf-ci', 'gitlab', @@ -39,12 +40,26 @@ class IdfCMakeApp(CMakeApp): '--build-dir', self.build_dir, ], - stdout=sys.stdout, - stderr=sys.stderr, - ) - if result.returncode != 0: - self.build_status = BuildStatus.FAILED - self.build_comment = 'Failed to upload artifacts' + [ + 'idf-ci', + 'gitlab', + 'upload-artifacts', + self.app_dir, + '--type', + APP_EXTRA_S3_ARTIFACT_TYPE, + ], + ] + + for command in upload_commands: + result = subprocess.run( + command, + stdout=sys.stdout, + stderr=sys.stderr, + ) + if result.returncode != 0: + self.build_status = BuildStatus.FAILED + self.build_comment = 'Failed to upload artifacts' + break rmdir( self.build_path, diff --git a/tools/ci/idf_ci_utils.py b/tools/ci/idf_ci_utils.py index 73183e31e48..f67dede6fbe 100644 --- a/tools/ci/idf_ci_utils.py +++ b/tools/ci/idf_ci_utils.py @@ -12,6 +12,9 @@ from functools import cached_property IDF_PATH: str = os.path.abspath(os.getenv('IDF_PATH', os.path.join(os.path.dirname(__file__), '..', '..'))) +# S3 artifact type for app-dir files outside build_* +APP_EXTRA_S3_ARTIFACT_TYPE = 'app_extra' + def get_submodule_dirs(full_path: bool = False) -> list[str]: """