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.
This commit is contained in:
Ondrej Kosta
2026-06-26 14:09:45 +02:00
parent 315e812d5b
commit c1a37b419d
4 changed files with 77 additions and 4 deletions
+9
View File
@@ -81,6 +81,15 @@ patterns = [
] ]
if_clause = 'CI_JOB_GROUP_NAME != "build_non_test_related_apps"' if_clause = 'CI_JOB_GROUP_NAME != "build_non_test_related_apps"'
[gitlab.artifacts.s3.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.log] [gitlab.artifacts.s3.log]
bucket = "idf-artifacts" bucket = "idf-artifacts"
patterns = [ patterns = [
+41
View File
@@ -36,6 +36,8 @@ from _pytest.config import Config
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from idf_ci import PytestCase from idf_ci import PytestCase
from idf_ci.idf_pytest import IDF_CI_PYTEST_CASE_KEY 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_ci_utils import idf_relpath
from idf_pytest.constants import DEFAULT_LOGDIR from idf_pytest.constants import DEFAULT_LOGDIR
from idf_pytest.plugin import IDF_LOCAL_PLUGIN_KEY from idf_pytest.plugin import IDF_LOCAL_PLUGIN_KEY
@@ -145,6 +147,33 @@ class AppDownloader:
stderr=sys.stderr, stderr=sys.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}')
PRESIGNED_JSON = 'presigned.json' PRESIGNED_JSON = 'presigned.json'
@@ -276,6 +305,18 @@ def app_downloader(pipeline_id: t.Optional[str]) -> t.Optional[AppDownloader]:
return AppDownloader(commit_sha, pipeline_id) return AppDownloader(commit_sha, pipeline_id)
@pytest.fixture
def download_app_extra(app_downloader: t.Optional[AppDownloader]) -> 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 @pytest.fixture
@multi_dut_fixture @multi_dut_fixture
def build_dir( def build_dir(
+24 -4
View File
@@ -8,7 +8,9 @@ import typing as t
from dynamic_pipelines.constants import BINARY_SIZE_METRIC_NAME 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.constants import BuildStatus
from idf_build_apps.utils import rmdir from idf_build_apps.utils import rmdir
from idf_ci_utils import APP_EXTRA_S3_ARTIFACT_TYPE
from idf_ci_utils import idf_relpath from idf_ci_utils import idf_relpath
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
@@ -29,16 +31,34 @@ class IdfCMakeApp(CMakeApp):
# only upload in CI # only upload in CI
if os.getenv('CI_JOB_ID'): if os.getenv('CI_JOB_ID'):
subprocess.run( upload_commands = [
[ [
'idf-ci', 'idf-ci',
'gitlab', 'gitlab',
'upload-artifacts', 'upload-artifacts',
self.app_dir, self.app_dir,
], ],
stdout=sys.stdout, [
stderr=sys.stderr, '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( rmdir(
self.build_path, self.build_path,
exclude_file_patterns=['build_log.txt', 'size*.json'], exclude_file_patterns=['build_log.txt', 'size*.json'],
+3
View File
@@ -13,6 +13,9 @@ from pathlib import Path
IDF_PATH: str = os.path.abspath(os.getenv('IDF_PATH', os.path.join(os.path.dirname(__file__), '..', '..'))) 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) -> t.List[str]: def get_submodule_dirs(full_path: bool = False) -> t.List[str]:
""" """