From e962d4e60f55501b7e6ba3d98f1ad6848f4a40ff Mon Sep 17 00:00:00 2001 From: Jakub Kocka Date: Mon, 17 Aug 2026 13:19:20 +0200 Subject: [PATCH] ci(tools): Fix worktree submodule copy of gitlink files --- tools/test_build_system/conftest.py | 58 ++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/tools/test_build_system/conftest.py b/tools/test_build_system/conftest.py index 6524c50d2c0..70ed3f51c01 100644 --- a/tools/test_build_system/conftest.py +++ b/tools/test_build_system/conftest.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 import datetime import logging @@ -11,10 +11,10 @@ from tempfile import mkdtemp import pytest from _pytest.fixtures import FixtureRequest -from test_build_system_helpers import EnvDict from test_build_system_helpers import EXT_IDF_PATH -from test_build_system_helpers import get_idf_build_env +from test_build_system_helpers import EnvDict from test_build_system_helpers import IdfPyFunc +from test_build_system_helpers import get_idf_build_env from test_build_system_helpers import run_idf_py @@ -66,10 +66,11 @@ def _create_idf_copy_via_worktree(path_from: Path, path_to: Path) -> str: # Only copy if source submodule exists and has content if src_submodule.exists() and any(src_submodule.iterdir()): logging.debug(f'copying submodule {submodule_rel_path}') - # Remove the empty directory created by worktree - if dst_submodule.exists(): - shutil.rmtree(dst_submodule, ignore_errors=True) - # Copy the submodule content + # Worktree submodule paths may be gitlink files; rmtree() does not remove those. + if dst_submodule.is_file() or dst_submodule.is_symlink(): + dst_submodule.unlink() + elif dst_submodule.exists(): + shutil.rmtree(dst_submodule) shutil.copytree(src_submodule, dst_submodule, symlinks=True, ignore=shutil.ignore_patterns('.git')) # Match old shutil-based idf_copy: no top-level .git (see docstring above). @@ -131,13 +132,15 @@ def should_clean_test_dir(request: FixtureRequest) -> bool: def pytest_addoption(parser: pytest.Parser) -> None: parser.addoption( - '--work-dir', action='store', default=None, - help='Directory for temporary files. If not specified, an OS-specific ' - 'temporary directory will be used.' + '--work-dir', + action='store', + default=None, + help='Directory for temporary files. If not specified, an OS-specific temporary directory will be used.', ) parser.addoption( - '--cleanup-idf-copy', action='store_true', - help='Always clean up the IDF copy after the test. By default, the copy is cleaned up only if the test passes.' + '--cleanup-idf-copy', + action='store_true', + help='Always clean up the IDF copy after the test. By default, the copy is cleaned up only if the test passes.', ) @@ -166,7 +169,9 @@ def _session_work_dir(request: FixtureRequest) -> typing.Generator[typing.Tuple[ @pytest.fixture(name='func_work_dir', autouse=True) -def work_dir(request: FixtureRequest, _session_work_dir: typing.Tuple[Path, bool]) -> typing.Generator[Path, None, None]: +def work_dir( + request: FixtureRequest, _session_work_dir: typing.Tuple[Path, bool] +) -> typing.Generator[Path, None, None]: session_work_dir, is_temp_dir = _session_work_dir if request._pyfuncitem.keywords.get('force_temp_work_dir') and not is_temp_dir: @@ -209,7 +214,9 @@ def test_app_copy(func_work_dir: Path, request: FixtureRequest) -> typing.Genera ignore = shutil.ignore_patterns( path_to.name, # also ignore files which may be present in the work directory - 'build', 'sdkconfig') + 'build', + 'sdkconfig', + ) logging.debug(f'copying {path_from} to {path_to}') shutil.copytree(path_from, path_to, ignore=ignore, symlinks=True) @@ -222,7 +229,7 @@ def test_app_copy(func_work_dir: Path, request: FixtureRequest) -> typing.Genera os.chdir(old_cwd) if should_clean_test_dir(request): - logging.debug('cleaning up work directory after a successful test: {}'.format(path_to)) + logging.debug(f'cleaning up work directory after a successful test: {path_to}') shutil.rmtree(path_to, ignore_errors=True) @@ -236,8 +243,22 @@ def test_git_template_app(func_work_dir: Path, request: FixtureRequest) -> typin logging.debug(f'cloning git-template app to {path_to}') path_to.mkdir() # No need to clone full repository, just a single master branch - subprocess.run(['git', 'clone', '--single-branch', '-b', 'master', '--depth', '1', 'https://github.com/espressif/esp-idf-template.git', '.'], - cwd=path_to, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + subprocess.run( # noqa: UP022 + [ + 'git', + 'clone', + '--single-branch', + '-b', + 'master', + '--depth', + '1', + 'https://github.com/espressif/esp-idf-template.git', + '.', + ], + cwd=path_to, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) old_cwd = Path.cwd() os.chdir(path_to) @@ -247,7 +268,7 @@ def test_git_template_app(func_work_dir: Path, request: FixtureRequest) -> typin os.chdir(old_cwd) if should_clean_test_dir(request): - logging.debug('cleaning up work directory after a successful test: {}'.format(path_to)) + logging.debug(f'cleaning up work directory after a successful test: {path_to}') shutil.rmtree(path_to, ignore_errors=True) @@ -305,4 +326,5 @@ def fixture_default_idf_env() -> EnvDict: def idf_py(default_idf_env: EnvDict) -> IdfPyFunc: def result(*args: str, check: bool = True, input_str: typing.Optional[str] = None) -> subprocess.CompletedProcess: return run_idf_py(*args, env=default_idf_env, workdir=os.getcwd(), check=check, input_str=input_str) # type: ignore + return result