Merge branch 'ci/deprecate_template_app' into 'master'

ci(build): remove esp-idf-template dependency, use in-tree test apps

See merge request espressif/esp-idf!47737
This commit is contained in:
Marius Vikhammer
2026-05-07 16:48:10 +08:00
13 changed files with 104 additions and 234 deletions

View File

@@ -9,49 +9,6 @@
IDF_CCACHE_ENABLE: "1"
dependencies: # set dependencies to null to avoid missing artifacts issue
######################
# build_template_app #
######################
.build_template_app_template:
extends:
- .build_template
artifacts:
paths:
- log_template_app/*
- size_info.txt
- build_template_app/**/size*.json
expire_in: 1 week
when: always
script:
# Set the variable for 'esp-idf-template' testing
- ESP_IDF_TEMPLATE_GIT=${ESP_IDF_TEMPLATE_GIT:-"https://github.com/espressif/esp-idf-template.git"}
- retry_failed git clone ${ESP_IDF_TEMPLATE_GIT}
# Try to use the same branch name for esp-idf-template that we're
# using on esp-idf. If it doesn't exist then just stick to the default branch
- python $CHECKOUT_REF_SCRIPT esp-idf-template esp-idf-template
- export PATH="$IDF_PATH/tools:$PATH"
# Only do the default cmake build for each target, remaining part are done in the build_template_app job
- tools/ci/build_template_app.sh ${BUILD_COMMAND_ARGS}
# Build at least one project for each target at earliest stage to reduce build cost for obvious failing commits
fast_template_app:
extends:
- .build_template_app_template
- .rules:build
stage: pre_check
tags: [fast_run, shiny]
variables:
BUILD_COMMAND_ARGS: "-p"
# This job builds template app with permutations of targets and optimization levels
build_template_app:
extends:
- .build_template_app_template
- .rules:build
stage: host_test
needs:
- job: fast_template_app
artifacts: false
########################################
# Clang Build Apps Without Tests Cases #
@@ -61,7 +18,7 @@ build_template_app:
- .build_template
- .rules:build
needs:
- job: fast_template_app
- job: fast_build_check
artifacts: false
- pipeline_variables
artifacts:
@@ -152,7 +109,7 @@ generate_build_child_pipeline:
build_child_pipeline:
stage: build
needs:
- job: fast_template_app
- job: fast_build_check
optional: true
artifacts: false
- pipeline_variables

View File

@@ -13,7 +13,7 @@
- job: upload-submodules-cache
optional: true
artifacts: false
- job: fast_template_app
- job: fast_build_check
optional: true
artifacts: false
artifacts:
@@ -438,7 +438,7 @@ test_pytest_macos:
- .rules:build:check
dependencies: # set dependencies to null to avoid missing artifacts issue
needs:
- job: fast_template_app
- job: fast_build_check
artifacts: false
optional: true
artifacts:

View File

@@ -4,6 +4,30 @@
tags: [fast_run, shiny]
dependencies: # set dependencies to null to avoid missing artifacts issue
####################
# fast_build_check #
####################
# Build one config per target at earliest stage to catch obvious failures before the full build
fast_build_check:
extends:
- .build_template
- .rules:build
stage: pre_check
tags: [fast_run, shiny]
artifacts:
paths:
- "**/build*/build_log.txt"
- "**/build*/size*.json"
expire_in: 1 week
when: always
script:
- export PATH="$IDF_PATH/tools:$PATH"
- idf-build-apps build
-p tools/test_apps/system/build_tests/full_build
--config-rules "sdkconfig.ci.Og="
--default-build-targets esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c5 esp32c6 esp32h2 esp32p4 esp32c61 esp32h21 esp32h4 esp32s31
check_version:
# Don't run this for feature/bugfix branches, so that it is possible to modify
# esp_idf_version.h in a branch before tagging the next version.

View File

@@ -52,7 +52,6 @@
- "tools/ci/esp_err_to_name_exceptions.txt"
- "tools/ci/test_build_system*.sh"
- "tools/ci/test_build_system*.py"
- "tools/ci/build_template_app.sh"
- "tools/test_build_system/**/*"
.patterns-host_test_ldgen: &patterns-host_test_ldgen

View File

@@ -1,108 +0,0 @@
#!/usr/bin/env bash
#
# Usage: build_template_app.sh [-p]
# -p: optional, if specified, do a fast build for every chip target with cmake and only the default
# config. Otherwise permutations of (chip target, build system, configs) which were not built in
# the fast build will be built.
#
# Needs to be called under IDF root folder
set -euo pipefail
TEMPLATE_APP_PATH="esp-idf-template"
# Set default paths for template app builds
BUILD_PATH="${IDF_PATH}/build_template_app"
BUILD_DIR="${BUILD_PATH}/@t/@w"
LOG_PATH="${IDF_PATH}/log_template_app"
BUILD_LOG_CMAKE="${LOG_PATH}/cmake_@t_@w.txt"
gen_configs() {
# CONFIG_COMPILER_OPTIMIZATION_NONE with flag -O0
echo "CONFIG_COMPILER_OPTIMIZATION_NONE=y" > ${TEMPLATE_APP_PATH}/sdkconfig.ci.O0
# CONFIG_COMPILER_OPTIMIZATION_SIZE with flag -Os
echo "CONFIG_COMPILER_OPTIMIZATION_SIZE=y" > ${TEMPLATE_APP_PATH}/sdkconfig.ci.Os
echo "CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y" >> ${TEMPLATE_APP_PATH}/sdkconfig.ci.Os
# CONFIG_COMPILER_OPTIMIZATION_PERF with flag -O2
echo "CONFIG_COMPILER_OPTIMIZATION_PERF=y" > ${TEMPLATE_APP_PATH}/sdkconfig.ci.O2
echo "CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF=y" >> ${TEMPLATE_APP_PATH}/sdkconfig.ci.O2
# -O2 makes the bootloader too large to fit in the default space, otherwise(!)
echo "CONFIG_PARTITION_TABLE_OFFSET=0x10000" >> ${TEMPLATE_APP_PATH}/sdkconfig.ci.O2
# This part will be built in earlier stage (pre_build job) with only cmake. Built with make in later stage
# CONFIG_COMPILER_OPTIMIZATION_DEBUG with flag -Og
echo "CONFIG_COMPILER_OPTIMIZATION_DEBUG=y" > ${TEMPLATE_APP_PATH}/sdkconfig.ci2.Og
echo "CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG=y" >> ${TEMPLATE_APP_PATH}/sdkconfig.ci2.Og
# -Og makes the bootloader too large to fit in the default space, otherwise(!)
echo "CONFIG_PARTITION_TABLE_OFFSET=0x10000" >> ${TEMPLATE_APP_PATH}/sdkconfig.ci2.Og
# Needs to be built with specific extra flags
# Same as O2, but also disable assertions.
cp ${TEMPLATE_APP_PATH}/sdkconfig.ci.O2 ${TEMPLATE_APP_PATH}/sdkconfig.ci3.no_assert
echo "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y" >> ${TEMPLATE_APP_PATH}/sdkconfig.ci3.no_assert
}
get_config_str() {
CONFIG_STR=
until [ $# -eq 0 ]
do
CONFIG_STR+=" --config $1"
shift
done
echo ${CONFIG_STR}
}
build_stage2() {
CONFIG_STR=$(get_config_str sdkconfig.ci.*=)
# Override EXTRA_CFLAGS and EXTRA_CXXFLAGS in the environment
export EXTRA_CFLAGS=${PEDANTIC_CFLAGS/-Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function/}
export EXTRA_CXXFLAGS=${PEDANTIC_CXXFLAGS/-Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function/}
idf-build-apps build \
-p ${TEMPLATE_APP_PATH} \
${CONFIG_STR} \
--work-dir ${BUILD_PATH}/cmake \
--build-dir ${BUILD_DIR} \
--build-log ${BUILD_LOG_CMAKE} \
--default-build-targets esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c5 esp32c6 esp32h2 esp32p4 esp32c61 esp32h21 esp32h4 esp32s31
}
build_stage1() {
CONFIG_STR=$(get_config_str sdkconfig.ci2.*=)
idf-build-apps build \
-p ${TEMPLATE_APP_PATH} \
${CONFIG_STR} \
--work-dir ${BUILD_PATH}/cmake \
--build-dir ${BUILD_DIR} \
--build-log ${BUILD_LOG_CMAKE} \
--default-build-targets esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c5 esp32c6 esp32h2 esp32p4 esp32c61 esp32h21 esp32h4 esp32s31
}
# Default arguments
# STAGE:
# 1 (-p): fast build, 2 (default): regular build
STAGE=2
# Parse arguments: -p for STAGE
for arg in $*
do
if [ ${arg} = "-p" ]; then
STAGE=1
fi
done
mkdir -p ${BUILD_PATH}/cmake
mkdir -p ${LOG_PATH}
rm -f scan.json
gen_configs
if [ ${STAGE} = 1 ]
then
build_stage1
else
build_stage2
fi

View File

@@ -53,7 +53,6 @@ tools/bsasm.py
tools/bt/ble_log_console/build.sh
tools/bt/ble_log_console/run.sh
tools/check_python_dependencies.py
tools/ci/build_template_app.sh
tools/ci/check_api_violation.sh
tools/ci/check_build_test_rules.py
tools/ci/check_callgraph.py

View File

@@ -0,0 +1 @@
CONFIG_COMPILER_OPTIMIZATION_NONE=y

View File

@@ -0,0 +1,4 @@
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG=y
# -Og makes the bootloader too large to fit in the default space
CONFIG_PARTITION_TABLE_OFFSET=0x10000

View File

@@ -0,0 +1,2 @@
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y

View File

@@ -1,2 +1,5 @@
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
CONFIG_COMPILER_OPTIMIZATION_PERF=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF=y
# -O2 makes the bootloader too large to fit in the default space
CONFIG_PARTITION_TABLE_OFFSET=0x10000

View File

@@ -231,30 +231,27 @@ def test_app_copy(func_work_dir: Path, request: FixtureRequest) -> typing.Genera
@pytest.fixture
def test_git_template_app(func_work_dir: Path, request: FixtureRequest) -> typing.Generator[Path, None, None]:
# sanitize test name in case pytest.mark.parametrize was used
def minimal_git_app(func_work_dir: Path, request: FixtureRequest) -> typing.Generator[Path, None, None]:
test_name_sanitized = request.node.name.replace('[', '_').replace(']', '')
copy_to = test_name_sanitized + '_app'
path_to = func_work_dir / copy_to
path_to = func_work_dir / (test_name_sanitized + '_app')
logging.debug(f'cloning git-template app to {path_to}')
logging.debug(f'creating minimal git app at {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,
capture_output=True,
(path_to / 'CMakeLists.txt').write_text(
'cmake_minimum_required(VERSION 3.22)\n'
'include($ENV{IDF_PATH}/tools/cmake/project.cmake)\n'
'project(app-template)\n'
)
(path_to / 'main').mkdir()
(path_to / 'main' / 'CMakeLists.txt').write_text('idf_component_register(SRCS "main.c")\n')
(path_to / 'main' / 'main.c').write_text('void app_main(void) {}\n')
for cmd in [
['git', 'init'],
['git', 'add', '.'],
['git', '-c', 'user.email=test@test.com', '-c', 'user.name=Test', 'commit', '-m', 'init'],
['git', 'tag', 'v1.0'],
]:
subprocess.run(cmd, cwd=path_to, capture_output=True, check=True)
old_cwd = Path.cwd()
os.chdir(path_to)

View File

@@ -1,28 +1,24 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import logging
import os
import re
import shutil
import subprocess
import typing
from pathlib import Path
from test_build_system_helpers import EnvDict, run_idf_py
from test_build_system_helpers import EnvDict
from test_build_system_helpers import run_idf_py
def run_git_cmd(*args: str,
workdir: Path,
env: typing.Optional[EnvDict] = None) -> subprocess.CompletedProcess:
def run_git_cmd(*args: str, workdir: Path, env: EnvDict | None = None) -> subprocess.CompletedProcess:
cmd = ['git'] + list(args)
env_dict = dict(**os.environ)
if env:
env_dict.update(env)
logging.debug('running {} in {}'.format(' '.join(cmd), workdir))
return subprocess.run(cmd, cwd=workdir, env=env_dict,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return subprocess.run(cmd, cwd=workdir, env=env_dict, capture_output=True)
# In this test, the action needs to be performed in ESP-IDF that is valid git directory
@@ -47,15 +43,19 @@ def test_git_custom_tag() -> None:
os.unlink(idf_build_test_app_path / 'sdkconfig')
def test_support_git_worktree(test_git_template_app: Path) -> None:
def test_support_git_worktree(minimal_git_app: Path) -> None:
logging.info('Supports git worktree')
run_git_cmd('branch', 'test_build_system', workdir=test_git_template_app)
run_git_cmd('worktree', 'add', '../esp-idf-worktree-app', 'test_build_system', workdir=test_git_template_app)
run_git_cmd('branch', 'test_build_system', workdir=minimal_git_app)
run_git_cmd('worktree', 'add', '../esp-idf-worktree-app', 'test_build_system', workdir=minimal_git_app)
try:
idf_ret_template_app = run_idf_py('reconfigure', workdir=test_git_template_app)
idf_ret_worktree_app = run_idf_py('reconfigure', workdir=os.path.join(os.path.dirname(test_git_template_app), 'esp-idf-worktree-app'))
assert (re.search(r'-- App \"app-template\".*', idf_ret_template_app.stdout).group() == # type: ignore
re.search(r'-- App \"app-template\".*', idf_ret_worktree_app.stdout).group()) # type: ignore
idf_ret_template_app = run_idf_py('reconfigure', workdir=minimal_git_app)
idf_ret_worktree_app = run_idf_py(
'reconfigure', workdir=os.path.join(os.path.dirname(minimal_git_app), 'esp-idf-worktree-app')
)
assert (
re.search(r'-- App \"app-template\".*', idf_ret_template_app.stdout).group() # type: ignore
== re.search(r'-- App \"app-template\".*', idf_ret_worktree_app.stdout).group() # type: ignore
)
finally:
run_git_cmd('worktree', 'remove', '../esp-idf-worktree-app', workdir=test_git_template_app)
run_git_cmd('branch', '-d', 'test_build_system', workdir=test_git_template_app)
run_git_cmd('worktree', 'remove', '../esp-idf-worktree-app', workdir=minimal_git_app)
run_git_cmd('branch', '-d', 'test_build_system', workdir=minimal_git_app)

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import logging
import os
@@ -34,14 +34,14 @@ def test_versions_get_default_version(idf_py: IdfPyFunc, test_app_copy: Path) ->
# Test Case: Test that the build-system can set the version of an IDF app from git describe
#
# Test Steps:
# 1. Clone the idf template app from https://github.com/espressif/esp-idf-template.git
# 1. Create a minimal in-tree app with a local git repository and a version tag
# 2. Run idf.py reconfigure
# 3. Run git describe in the cloned app git repository
# 3. Run git describe in the app git repository
# 4. Verify that the app version is picked up from the git describe command
#
#############################################################################################
def test_versions_get_version_from_git_describe(
idf_py: IdfPyFunc, test_git_template_app: Path, env: EnvDict | None = None
idf_py: IdfPyFunc, minimal_git_app: Path, env: EnvDict | None = None
) -> None:
logging.info('Verify that the version of app can be set from git describe')
idf_ret = idf_py('reconfigure')
@@ -50,7 +50,7 @@ def test_versions_get_version_from_git_describe(
env_dict.update(env)
git_ret = subprocess.run(
['git', 'describe', '--always', '--tags', '--dirty'],
cwd=test_git_template_app,
cwd=minimal_git_app,
env=env_dict,
capture_output=True,
)
@@ -61,7 +61,7 @@ def test_versions_get_version_from_git_describe(
# Test Case: Test that the build-system can set the version for an IDF app from the VERSION argument
#
# Test Steps:
# 1. Clone the idf template app from https://github.com/espressif/esp-idf-template.git
# 1. Create a minimal in-tree app with a local git repository and a version tag
# 2. Replace the default project() command in the top level CMakeLists.txt file to call the version parsing
# function __parse_and_store_version_arg()
# 3. Append several calls to __parse_and_store_version_arg() with different inputs for the VERSION argument
@@ -71,44 +71,36 @@ def test_versions_get_version_from_git_describe(
#
#############################################################################################
@pytest.mark.buildv2_skip('cmakev2 does not need to parse the VERSION argument in the build system')
def test_versions_get_version_from_version_arg(idf_py: IdfPyFunc, test_git_template_app: Path) -> None:
def test_versions_get_version_from_version_arg(idf_py: IdfPyFunc, minimal_git_app: Path) -> None:
logging.info('Verify that the VERSION argument in project() is correctly parsed by cmake')
# empty VERSION argument
replace_in_file(
(test_git_template_app / 'CMakeLists.txt'),
(minimal_git_app / 'CMakeLists.txt'),
'project(app-template)',
'__parse_and_store_version_arg(app-template VERSION)',
)
# Invalid VERSION argument format
append_to_file((minimal_git_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-tempplate VERSION 1..2)')
# Invalid VERSION argument format
append_to_file(
(test_git_template_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-tempplate VERSION 1..2)'
(minimal_git_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION version_text)'
)
# Invalid VERSION argument format
append_to_file(
(test_git_template_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION version_text)'
)
# Invalid VERSION argument format
append_to_file(
(test_git_template_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 1.2.3.4.5)'
)
append_to_file(
(test_git_template_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 0)'
(minimal_git_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 1.2.3.4.5)'
)
append_to_file((minimal_git_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 0)')
# Valid VERSION argument format
append_to_file((minimal_git_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 0.1)')
# Valid VERSION argument format
append_to_file((minimal_git_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 0.1.2)')
# Valid VERSION argument format
append_to_file(
(test_git_template_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 0.1)'
)
# Valid VERSION argument format
append_to_file(
(test_git_template_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 0.1.2)'
)
# Valid VERSION argument format
append_to_file(
(test_git_template_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 0.1.2.3)'
(minimal_git_app / 'CMakeLists.txt'), '\n__parse_and_store_version_arg(app-template VERSION 0.1.2.3)'
)
# project() call with valid VERSION argument format
append_to_file((test_git_template_app / 'CMakeLists.txt'), '\nproject(app-template VERSION 0.1.2.3)')
append_to_file((minimal_git_app / 'CMakeLists.txt'), '\nproject(app-template VERSION 0.1.2.3)')
with pytest.raises(subprocess.CalledProcessError) as e:
idf_py('reconfigure')
@@ -139,12 +131,12 @@ def test_versions_get_version_from_version_arg(idf_py: IdfPyFunc, test_git_templ
# 6. Verify that the app version is picked up from the version.txt file
#
#############################################################################################
def test_versions_get_version_from_version_file(idf_py: IdfPyFunc, test_git_template_app: Path) -> None:
def test_versions_get_version_from_version_file(idf_py: IdfPyFunc, minimal_git_app: Path) -> None:
logging.info('Verify that the version of app can be set from version.txt file')
replace_in_file(
(test_git_template_app / 'CMakeLists.txt'), 'project(app-template)', 'project(app-template VERSION 0.1.2.3)'
(minimal_git_app / 'CMakeLists.txt'), 'project(app-template)', 'project(app-template VERSION 0.1.2.3)'
)
(test_git_template_app / 'version.txt').write_text('project_version_from_txt')
(minimal_git_app / 'version.txt').write_text('project_version_from_txt')
idf_ret = idf_py('reconfigure')
assert 'App "app-template" version: project_version_from_txt' in idf_ret.stdout
@@ -163,15 +155,15 @@ def test_versions_get_version_from_version_file(idf_py: IdfPyFunc, test_git_temp
# 7. Verify that the app version is picked up from the CMakeLists.txt file
#
#############################################################################################
def test_versions_get_version_from_top_level_cmake(idf_py: IdfPyFunc, test_git_template_app: Path) -> None:
def test_versions_get_version_from_top_level_cmake(idf_py: IdfPyFunc, minimal_git_app: Path) -> None:
logging.info('Verify that the version of app can be set from PROJECT_VER in CMakeLists.txt')
replace_in_file(
(test_git_template_app / 'CMakeLists.txt'),
(minimal_git_app / 'CMakeLists.txt'),
'project(app-template)',
'set(PROJECT_VER project_version_from_CMakeLists)',
)
append_to_file((test_git_template_app / 'CMakeLists.txt'), 'project(app-template VERSION 0.1.2.3)')
(test_git_template_app / 'version.txt').write_text('project_version_from_txt')
append_to_file((minimal_git_app / 'CMakeLists.txt'), 'project(app-template VERSION 0.1.2.3)')
(minimal_git_app / 'version.txt').write_text('project_version_from_txt')
idf_ret = idf_py('reconfigure')
assert 'App "app-template" version: project_version_from_CMakeLists' in idf_ret.stdout
@@ -192,15 +184,15 @@ def test_versions_get_version_from_top_level_cmake(idf_py: IdfPyFunc, test_git_t
# 9. Verify that the app version is picked up from the Kconfig option
#
#############################################################################################
def test_versions_get_version_from_kconfig_option(idf_py: IdfPyFunc, test_git_template_app: Path) -> None:
def test_versions_get_version_from_kconfig_option(idf_py: IdfPyFunc, minimal_git_app: Path) -> None:
logging.info('Verify that the version of app can be set from Kconfig option')
replace_in_file(
(test_git_template_app / 'CMakeLists.txt'),
(minimal_git_app / 'CMakeLists.txt'),
'project(app-template)',
'set(PROJECT_VER project_version_from_CMakeLists)',
)
append_to_file((test_git_template_app / 'CMakeLists.txt'), 'project(app-template VERSION 0.1.2.3)')
(test_git_template_app / 'sdkconfig.defaults').write_text(
append_to_file((minimal_git_app / 'CMakeLists.txt'), 'project(app-template VERSION 0.1.2.3)')
(minimal_git_app / 'sdkconfig.defaults').write_text(
'\n'.join(['CONFIG_APP_PROJECT_VER_FROM_CONFIG=y', 'CONFIG_APP_PROJECT_VER="project_version_from_Kconfig"'])
)
idf_ret = idf_py('reconfigure')