mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
ci(buildv2): skip patching the fake_pass build child pipeline
idf-ci emits a skip pipeline containing only a fake_pass job, with no include of test_child_pipeline.yml, when the modified files match no buildable apps. patch_buildv2_child_pipeline.py redefined generate_pytest_child_pipeline unconditionally, assuming the included template supplied its tags, image and assign_test stage via keyword merge. With the template absent, the injected job was bare: no tags, default test stage. It was scheduled on the wrong runner and failed, turning the allow_failure build_child_pipeline_buildv2 bridge red on every buildv2 MR whose changes produce an empty app matrix. Return early when a fake_pass job is present so the child pipeline is left untouched and skips, matching the default build path. Add a unit test for the patch script and a host_test job to run it.
This commit is contained in:
@@ -285,6 +285,12 @@ test_idf_build_apps_load_soc_caps:
|
||||
- cd tools/ci
|
||||
- pytest_for_ut ./test_soc_headers_load_in_idf_build_apps.py
|
||||
|
||||
test_dynamic_pipelines_scripts:
|
||||
extends: .host_test_template
|
||||
script:
|
||||
- cd ${IDF_PATH}/tools/ci/dynamic_pipelines/tests
|
||||
- pytest_for_ut ./test_patch_buildv2_child_pipeline.py
|
||||
|
||||
test_nvs_gen_check:
|
||||
extends: .host_test_template
|
||||
artifacts:
|
||||
|
||||
@@ -142,6 +142,9 @@
|
||||
|
||||
- "tools/ci/test_soc_headers_load_in_idf_build_apps.py"
|
||||
|
||||
- "tools/ci/dynamic_pipelines/scripts/patch_buildv2_child_pipeline.py"
|
||||
- "tools/ci/dynamic_pipelines/tests/**/*"
|
||||
|
||||
.patterns-docker: &patterns-docker
|
||||
- "tools/docker/**/*"
|
||||
|
||||
|
||||
@@ -33,6 +33,13 @@ def patch(path: str) -> None:
|
||||
with open(path) as f:
|
||||
d = yaml.safe_load(f)
|
||||
|
||||
if 'fake_pass' in d:
|
||||
# idf-ci emitted a skip pipeline (no apps matched the modified files): there
|
||||
# are no build jobs to activate and no included generate_pytest_child_pipeline
|
||||
# to redefine. Leave it untouched so Build system v2 skips like the default path.
|
||||
print('Skip pipeline (fake_pass) detected, leaving it untouched')
|
||||
return
|
||||
|
||||
injected = []
|
||||
for k, v in d.items():
|
||||
if isinstance(v, dict) and 'extends' in v:
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Unit tests for patch_buildv2_child_pipeline.patch()."""
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
_SCRIPT = Path(__file__).parent.parent / 'scripts' / 'patch_buildv2_child_pipeline.py'
|
||||
_spec = importlib.util.spec_from_file_location('patch_buildv2_child_pipeline', _SCRIPT)
|
||||
assert _spec is not None and _spec.loader is not None
|
||||
patch_mod: Any = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(patch_mod)
|
||||
|
||||
|
||||
def _write_and_patch(tmp_path: Path, content: dict[str, Any]) -> dict[str, Any]:
|
||||
p = tmp_path / 'buildv2_child_pipeline.yml'
|
||||
p.write_text(yaml.safe_dump(content, sort_keys=False))
|
||||
patch_mod.patch(str(p))
|
||||
result: dict[str, Any] = yaml.safe_load(p.read_text())
|
||||
return result
|
||||
|
||||
|
||||
def test_fake_pass_skip_pipeline_is_left_untouched(tmp_path: Path) -> None:
|
||||
# idf-ci emits a skip pipeline with only a `fake_pass` job when no apps match
|
||||
# the modified files. There is no included generate_pytest_child_pipeline to
|
||||
# redefine, so the patch must leave the pipeline untouched and let it skip.
|
||||
# Mirrors idf_ci.idf_gitlab.pipeline._get_fake_pass_job(): the sole job is
|
||||
# `fake_pass` and no test_child_pipeline.yml is included.
|
||||
skip_pipeline = {
|
||||
'fake_pass': {
|
||||
'stage': 'build',
|
||||
'tags': ['build', 'shiny'],
|
||||
'before_script': [],
|
||||
'after_script': [],
|
||||
'cache': [],
|
||||
'needs': [],
|
||||
'script': ['echo "skip the entire child pipeline"'],
|
||||
},
|
||||
'workflow': {'name': 'build_child_pipeline', 'rules': [{'when': 'always'}]},
|
||||
}
|
||||
|
||||
result = _write_and_patch(tmp_path, dict(skip_pipeline))
|
||||
|
||||
assert 'generate_pytest_child_pipeline' not in result
|
||||
assert result == skip_pipeline
|
||||
|
||||
|
||||
def test_build_jobs_get_buildv2_injection(tmp_path: Path) -> None:
|
||||
# A real pipeline (apps matched) carries build jobs that `extends` the dynamic
|
||||
# template. Those must get IDF_BUILD_V2 / PIPELINE_COMMIT_SHA injected and
|
||||
# generate_pytest_child_pipeline redefined with the post-processing step.
|
||||
pipeline = {
|
||||
'build_test_related_apps': {
|
||||
'extends': '.dynamic_build_template',
|
||||
'needs': [{'pipeline': '$PARENT_PIPELINE_ID', 'job': 'generate_build_child_pipeline'}],
|
||||
},
|
||||
}
|
||||
|
||||
result = _write_and_patch(tmp_path, pipeline)
|
||||
|
||||
job = result['build_test_related_apps']
|
||||
assert job['variables']['IDF_BUILD_V2'] == '1'
|
||||
assert job['variables']['PIPELINE_COMMIT_SHA'] == patch_mod.PIPELINE_COMMIT_SHA_V2
|
||||
assert 'generate_pytest_child_pipeline' in result
|
||||
assert result['generate_pytest_child_pipeline']['variables']['IDF_BUILD_V2'] == '1'
|
||||
Reference in New Issue
Block a user