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:
Sudeep Mohanty
2026-07-28 10:47:57 +02:00
parent 8550997ada
commit e69d5bb697
4 changed files with 85 additions and 0 deletions

View File

@@ -40,6 +40,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:

View File

@@ -0,0 +1,69 @@
# 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 job['needs'][0]['job'] == patch_mod.BUILDV2_GENERATOR_JOB
assert 'generate_pytest_child_pipeline' in result
assert result['generate_pytest_child_pipeline']['variables']['IDF_BUILD_V2'] == '1'