Merge branch 'ci/build_test_apps_according_to_required_components' into 'master'

CI: build test apps according to `requires_components` in `.build-test-rules.yml`s

Closes IDFCI-1651

See merge request espressif/esp-idf!22633
This commit is contained in:
Fu Hanxi 2023-05-31 09:05:38 +08:00
commit 3c8a782113
16 changed files with 331 additions and 292 deletions

2
.gitignore vendored
View File

@ -97,3 +97,5 @@ managed_components
# pytest log # pytest log
pytest_embedded_log/ pytest_embedded_log/
list_job_*.txt
size_info.txt

View File

@ -20,9 +20,11 @@ workflow:
- if: $CI_OPEN_MERGE_REQUESTS != null - if: $CI_OPEN_MERGE_REQUESTS != null
variables: variables:
PIPELINE_COMMIT_SHA: $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA PIPELINE_COMMIT_SHA: $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA
IS_MR_PIPELINE: 1
- if: $CI_OPEN_MERGE_REQUESTS == null - if: $CI_OPEN_MERGE_REQUESTS == null
variables: variables:
PIPELINE_COMMIT_SHA: $CI_COMMIT_SHA PIPELINE_COMMIT_SHA: $CI_COMMIT_SHA
IS_MR_PIPELINE: 0
- when: always - when: always
variables: variables:
@ -57,7 +59,6 @@ variables:
PYTHON_VER: 3.7.10 PYTHON_VER: 3.7.10
CLANG_TIDY_RUNNER_PROJ: 2107 # idf/clang-tidy-runner CLANG_TIDY_RUNNER_PROJ: 2107 # idf/clang-tidy-runner
IDF_BUILD_APPS_PROJ: 2818 # espressif/idf-build-apps
# Docker images # Docker images
BOT_DOCKER_IMAGE_TAG: ":latest" BOT_DOCKER_IMAGE_TAG: ":latest"

View File

@ -21,11 +21,7 @@
- [Shell Script Related](#shell-script-related) - [Shell Script Related](#shell-script-related)
- [Manifest File to Control the Build/Test apps](#manifest-file-to-control-the-buildtest-apps) - [Manifest File to Control the Build/Test apps](#manifest-file-to-control-the-buildtest-apps)
- [Grammar](#grammar) - [Grammar](#grammar)
- [Operands](#operands) - [Special Rules](#special-rules)
- [Operators](#operators)
- [Limitation:](#limitation)
- [How does it work?](#how-does-it-work)
- [Example](#example)
## General Workflow ## General Workflow
@ -242,3 +238,10 @@ We're using the latest version of [idf-build-apps][idf-build-apps]. Please refer
[idf-build-apps]: https://github.com/espressif/idf-build-apps [idf-build-apps]: https://github.com/espressif/idf-build-apps
[manifest-doc]: https://docs.espressif.com/projects/idf-build-apps/en/latest/manifest.html [manifest-doc]: https://docs.espressif.com/projects/idf-build-apps/en/latest/manifest.html
### Special Rules
In ESP-IDF CI, there's a few more special rules are additionally supported to disable the check app dependencies feature:
- Add MR labels `BUILD_AND_TEST_ALL_APPS`
- Run in protected branches

View File

@ -14,36 +14,6 @@
script: script:
- run_cmd python tools/ci/python_packages/ttfw_idf/IDFAssignTest.py $TEST_TYPE $TEST_DIR -c $CI_TARGET_TEST_CONFIG_FILE -o $TEST_DIR/test_configs - run_cmd python tools/ci/python_packages/ttfw_idf/IDFAssignTest.py $TEST_TYPE $TEST_DIR -c $CI_TARGET_TEST_CONFIG_FILE -o $TEST_DIR/test_configs
assign_example_test:
extends:
- .assign_test_template
- .rules:build:example_test
needs:
- job: build_examples_cmake_esp32
artifacts: false
optional: true
- job: build_examples_cmake_esp32s2
artifacts: false
optional: true
- job: build_examples_cmake_esp32c2
artifacts: false
optional: true
- job: build_examples_cmake_esp32c3
artifacts: false
optional: true
- job: build_examples_cmake_esp32c6
artifacts: false
optional: true
- job: build_examples_cmake_esp32h2
artifacts: false
optional: true
- job: build_examples_cmake_esp32s3
artifacts: false
optional: true
variables:
TEST_TYPE: example_test
TEST_DIR: examples
assign_unit_test: assign_unit_test:
extends: extends:
- .assign_test_template - .assign_test_template

View File

@ -21,6 +21,8 @@
needs: needs:
- job: fast_template_app - job: fast_template_app
artifacts: false artifacts: false
- job: mr_variables
optional: true # only MR pipelines would have this
artifacts: artifacts:
paths: paths:
- "**/build*/size.json" - "**/build*/size.json"
@ -37,7 +39,7 @@
- "**/build*/sdkconfig" - "**/build*/sdkconfig"
- "**/build*/bootloader/*.bin" - "**/build*/bootloader/*.bin"
- "**/build*/partition_table/*.bin" - "**/build*/partition_table/*.bin"
- list_job_*.json - list_job_*.txt
- size_info.txt - size_info.txt
# unit test specific # unit test specific
- components/idf_test/unit_test/*.yml - components/idf_test/unit_test/*.yml
@ -66,18 +68,18 @@
# would be clean up after 4 days # would be clean up after 4 days
- mc share download shiny-s3/idf-artifacts/${CI_PIPELINE_ID}/${CI_JOB_ID}.zip --expire=96h - mc share download shiny-s3/idf-artifacts/${CI_PIPELINE_ID}/${CI_JOB_ID}.zip --expire=96h
script: script:
# CI specific options start from "--collect-size-info xxx". could ignore when running locally # CI specific options start from "--parallel-count xxx". could ignore when running locally
- run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v - run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v
-t $IDF_TARGET -t $IDF_TARGET
--copy-sdkconfig --copy-sdkconfig
--collect-size-info size_info.txt
--collect-app-info list_job_${CI_NODE_INDEX:-1}.json
--parallel-count ${CI_NODE_TOTAL:-1} --parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1} --parallel-index ${CI_NODE_INDEX:-1}
--extra-preserve-dirs --extra-preserve-dirs
examples/bluetooth/esp_ble_mesh/ble_mesh_console examples/bluetooth/esp_ble_mesh/ble_mesh_console
examples/bluetooth/hci/controller_hci_uart_esp32 examples/bluetooth/hci/controller_hci_uart_esp32
examples/wifi/iperf examples/wifi/iperf
--modified-components ${MR_MODIFIED_COMPONENTS}
--modified-files ${MR_MODIFIED_FILES}
.build_cmake_clang_template: .build_cmake_clang_template:
extends: extends:
@ -87,14 +89,14 @@
TEST_BUILD_OPTS_EXTRA: "" TEST_BUILD_OPTS_EXTRA: ""
TEST_DIR: tools/test_apps/system/cxx_pthread_bluetooth TEST_DIR: tools/test_apps/system/cxx_pthread_bluetooth
script: script:
# CI specific options start from "--collect-size-info xxx". could ignore when running locally # CI specific options start from "--parallel-count xxx". could ignore when running locally
- run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v - run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v
-t $IDF_TARGET -t $IDF_TARGET
--copy-sdkconfig --copy-sdkconfig
--collect-size-info size_info.txt
--collect-app-info list_job_${CI_NODE_INDEX:-1}.json
--parallel-count ${CI_NODE_TOTAL:-1} --parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1} --parallel-index ${CI_NODE_INDEX:-1}
--modified-components ${MR_MODIFIED_COMPONENTS}
--modified-files ${MR_MODIFIED_FILES}
$TEST_BUILD_OPTS_EXTRA $TEST_BUILD_OPTS_EXTRA
.build_pytest_template: .build_pytest_template:
@ -114,30 +116,32 @@
- "**/build*/config/sdkconfig.json" - "**/build*/config/sdkconfig.json"
- "**/build*/bootloader/*.bin" - "**/build*/bootloader/*.bin"
- "**/build*/partition_table/*.bin" - "**/build*/partition_table/*.bin"
- list_job_*.json - list_job_*.txt
- size_info.txt - size_info.txt
when: always when: always
expire_in: 4 days expire_in: 4 days
script: script:
# CI specific options start from "--collect-size-info xxx". could ignore when running locally # CI specific options start from "--parallel-count xxx". could ignore when running locally
- run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v - run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v
-t $IDF_TARGET -t $IDF_TARGET
--pytest-apps --pytest-apps
--collect-size-info size_info.txt
--parallel-count ${CI_NODE_TOTAL:-1} --parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1} --parallel-index ${CI_NODE_INDEX:-1}
--modified-components ${MR_MODIFIED_COMPONENTS}
--modified-files ${MR_MODIFIED_FILES}
.build_pytest_no_jtag_template: .build_pytest_no_jtag_template:
extends: .build_pytest_template extends: .build_pytest_template
script: script:
# CI specific options start from "--collect-size-info xxx". could ignore when running locally # CI specific options start from "--parallel-count xxx". could ignore when running locally
- run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v - run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v
-t $IDF_TARGET -t $IDF_TARGET
-m \"not host_test and not jtag\" -m \"not host_test and not jtag\"
--pytest-apps --pytest-apps
--collect-size-info size_info.txt
--parallel-count ${CI_NODE_TOTAL:-1} --parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1} --parallel-index ${CI_NODE_INDEX:-1}
--modified-components ${MR_MODIFIED_COMPONENTS}
--modified-files ${MR_MODIFIED_FILES}
.build_pytest_jtag_template: .build_pytest_jtag_template:
extends: extends:
@ -156,19 +160,20 @@
- "**/build*/config/sdkconfig.json" - "**/build*/config/sdkconfig.json"
- "**/build*/bootloader/*.bin" - "**/build*/bootloader/*.bin"
- "**/build*/partition_table/*.bin" - "**/build*/partition_table/*.bin"
- list_job_*.json - list_job_*.txt
- size_info.txt - size_info.txt
when: always when: always
expire_in: 4 days expire_in: 4 days
script: script:
# CI specific options start from "--collect-size-info xxx". could ignore when running locally # CI specific options start from "--parallel-count xxx". could ignore when running locally
- run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v - run_cmd python tools/ci/ci_build_apps.py $TEST_DIR -v
-t $IDF_TARGET -t $IDF_TARGET
-m \"not host_test and jtag\" -m \"not host_test and jtag\"
--pytest-apps --pytest-apps
--collect-size-info size_info.txt
--parallel-count ${CI_NODE_TOTAL:-1} --parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1} --parallel-index ${CI_NODE_INDEX:-1}
--modified-components ${MR_MODIFIED_COMPONENTS}
--modified-files ${MR_MODIFIED_FILES}
build_pytest_examples_esp32: build_pytest_examples_esp32:
extends: extends:
@ -310,13 +315,13 @@ build_only_components_apps:
parallel: 5 parallel: 5
script: script:
- set_component_ut_vars - set_component_ut_vars
# CI specific options start from "--collect-size-info xxx". could ignore when running locally # CI specific options start from "--parallel-count xxx". could ignore when running locally
- run_cmd python tools/ci/ci_build_apps.py $COMPONENT_UT_DIRS -v - run_cmd python tools/ci/ci_build_apps.py $COMPONENT_UT_DIRS -v
-t all -t all
--collect-size-info size_info.txt
--collect-app-info list_job_${CI_NODE_INDEX:-1}.json
--parallel-count ${CI_NODE_TOTAL:-1} --parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1} --parallel-index ${CI_NODE_INDEX:-1}
--modified-components ${MR_MODIFIED_COMPONENTS}
--modified-files ${MR_MODIFIED_FILES}
.build_pytest_test_apps_template: .build_pytest_test_apps_template:
extends: .build_pytest_template extends: .build_pytest_template
@ -336,7 +341,7 @@ build_only_components_apps:
- "**/build*/bootloader/*.bin" - "**/build*/bootloader/*.bin"
- "**/build*/partition_table/*.bin" - "**/build*/partition_table/*.bin"
- "**/build*/project_description.json" - "**/build*/project_description.json"
- list_job_*.json - list_job_*.txt
- size_info.txt - size_info.txt
when: always when: always
expire_in: 4 days expire_in: 4 days
@ -404,13 +409,13 @@ build_only_tools_test_apps:
- .rules:build:custom_test - .rules:build:custom_test
parallel: 9 parallel: 9
script: script:
# CI specific options start from "--collect-size-info xxx". could ignore when running locally # CI specific options start from "--parallel-count xxx". could ignore when running locally
- run_cmd python tools/ci/ci_build_apps.py tools/test_apps -v - run_cmd python tools/ci/ci_build_apps.py tools/test_apps -v
-t all -t all
--collect-size-info size_info.txt
--collect-app-info list_job_${CI_NODE_INDEX:-1}.json
--parallel-count ${CI_NODE_TOTAL:-1} --parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1} --parallel-index ${CI_NODE_INDEX:-1}
--modified-components ${MR_MODIFIED_COMPONENTS}
--modified-files ${MR_MODIFIED_FILES}
.build_template_app_template: .build_template_app_template:
extends: extends:
@ -529,20 +534,18 @@ build_ssc_esp32h2:
- "**/build*/sdkconfig" - "**/build*/sdkconfig"
- "**/build*/bootloader/*.bin" - "**/build*/bootloader/*.bin"
- "**/build*/partition_table/*.bin" - "**/build*/partition_table/*.bin"
- list_job_*.json - list_job_*.txt
- size_info.txt - size_info.txt
- components/idf_test/unit_test/*.yml - components/idf_test/unit_test/*.yml
when: always when: always
expire_in: 4 days expire_in: 4 days
script: script:
# CI specific options start from "--collect-size-info xxx". could ignore when running locally # CI specific options start from "--parallel-count xxx". could ignore when running locally
- run_cmd python tools/ci/ci_build_apps.py tools/unit-test-app -v - run_cmd python tools/ci/ci_build_apps.py tools/unit-test-app -v
-t $IDF_TARGET -t $IDF_TARGET
--config "configs/*=" --config "configs/*="
--copy-sdkconfig --copy-sdkconfig
--preserve-all --preserve-all
--collect-size-info size_info.txt
--collect-app-info list_job_${CI_NODE_INDEX:-1}.json
--parallel-count ${CI_NODE_TOTAL:-1} --parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1} --parallel-index ${CI_NODE_INDEX:-1}
- run_cmd python tools/unit-test-app/tools/UnitTestParser.py tools/unit-test-app ${CI_NODE_INDEX:-1} - run_cmd python tools/unit-test-app/tools/UnitTestParser.py tools/unit-test-app ${CI_NODE_INDEX:-1}

View File

@ -39,14 +39,6 @@
- build_system - build_system
- downloadable-tools - downloadable-tools
"build:windows":
labels:
- build
- windows
patterns:
- build_system
- windows
"build:macos": "build:macos":
labels: labels:
- build - build
@ -140,7 +132,6 @@ build:integration_test:
- i154 - i154
- flash_multi - flash_multi
- ecdsa - ecdsa
- ccs811 # pytest*ccs811*
- nvs_encr_hmac - nvs_encr_hmac
patterns: patterns:
- "{0}-{1}-{2}" - "{0}-{1}-{2}"

View File

@ -186,3 +186,22 @@ check_configure_ci_environment_parsing:
script: script:
- cd tools/ci - cd tools/ci
- python -m unittest ci_build_apps.py - python -m unittest ci_build_apps.py
mr_variables:
extends:
- .pre_check_template
- .rules:mr
- .before_script_minimal
tags:
- build
script:
- echo "MR_MODIFIED_FILES=$(python tools/ci/ci_get_mr_info.py files ${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} | xargs)" >> mr.env
- echo "MR_MODIFIED_COMPONENTS=$(python tools/ci/ci_get_mr_info.py components ${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} | xargs)" >> mr.env
- >
if echo "$CI_MERGE_REQUEST_LABELS" | egrep "^([^,\n\r]+,)*BUILD_AND_TEST_ALL_APPS(,[^,\n\r]+)*$"; then
echo "BUILD_AND_TEST_ALL_APPS=1" >> mr.env
fi
artifacts:
reports:
dotenv: mr.env
expire_in: 4 days

View File

@ -166,9 +166,6 @@
- "tools/split_paths_by_spaces.py" - "tools/split_paths_by_spaces.py"
.patterns-windows: &patterns-windows
- "tools/windows/**/*"
.patterns-docker: &patterns-docker .patterns-docker: &patterns-docker
- "tools/docker/**/*" - "tools/docker/**/*"
@ -261,13 +258,6 @@
- "components/driver/include/driver/sdmmc*.h" - "components/driver/include/driver/sdmmc*.h"
- "components/sdmmc/**/*" - "components/sdmmc/**/*"
.patterns-example_test-ccs811: &patterns-example_test-ccs811
# components
- "examples/system/console/advanced/components/**/*"
- "components/driver/i2c/**/*"
# tests
- "examples/peripherals/i2c/i2c_tools/**/*"
# for jobs: UT_xx_SDSPI related # for jobs: UT_xx_SDSPI related
.patterns-unit_test-sdio: &patterns-unit_test-sdio .patterns-unit_test-sdio: &patterns-unit_test-sdio
- "components/hal/sdio*.c" - "components/hal/sdio*.c"
@ -374,6 +364,7 @@
######### #########
# Rules # # Rules #
######### #########
### Branches ###
.rules:protected: .rules:protected:
rules: rules:
- <<: *if-protected - <<: *if-protected
@ -382,6 +373,30 @@
rules: rules:
- <<: *if-protected-no_label - <<: *if-protected-no_label
.rules:dev:
rules:
- <<: *if-trigger
- <<: *if-dev-push
.rules:mr:
rules:
- <<: *if-dev-push
.rules:tag:release:
rules:
- <<: *if-tag-release
.rules:ref:master-schedule:
rules:
- <<: *if-ref-master
- <<: *if-schedule
.rules:ref:master-always:
rules:
- <<: *if-ref-master
when: always
### Patterns ###
.rules:patterns:python-cache: .rules:patterns:python-cache:
rules: rules:
- *if-schedule - *if-schedule
@ -404,25 +419,6 @@
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-danger-npm changes: *patterns-danger-npm
.rules:dev:
rules:
- <<: *if-trigger
- <<: *if-dev-push
.rules:tag:release:
rules:
- <<: *if-tag-release
.rules:ref:master-schedule:
rules:
- <<: *if-ref-master
- <<: *if-schedule
.rules:ref:master-always:
rules:
- <<: *if-ref-master
when: always
.rules:patterns:clang_tidy: .rules:patterns:clang_tidy:
rules: rules:
- <<: *if-protected - <<: *if-protected
@ -588,9 +584,6 @@
.if-label-unit_test_esp32s3: &if-label-unit_test_esp32s3 .if-label-unit_test_esp32s3: &if-label-unit_test_esp32s3
if: '$BOT_LABEL_UNIT_TEST_ESP32S3 || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*unit_test_esp32s3(?:,[^,\n\r]+)*$/i' if: '$BOT_LABEL_UNIT_TEST_ESP32S3 || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*unit_test_esp32s3(?:,[^,\n\r]+)*$/i'
.if-label-windows: &if-label-windows
if: '$BOT_LABEL_WINDOWS || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*windows(?:,[^,\n\r]+)*$/i'
.rules:build: .rules:build:
rules: rules:
- <<: *if-revert-branch - <<: *if-revert-branch
@ -1136,57 +1129,6 @@
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-submodule changes: *patterns-submodule
.rules:build:example_test:
rules:
- <<: *if-revert-branch
when: never
- <<: *if-protected
- <<: *if-example_test-ota-include_nightly_run-rule
- <<: *if-label-build
- <<: *if-label-example_test
- <<: *if-label-example_test_esp32
- <<: *if-label-example_test_esp32c2
- <<: *if-label-example_test_esp32c3
- <<: *if-label-example_test_esp32c6
- <<: *if-label-example_test_esp32h2
- <<: *if-label-example_test_esp32s2
- <<: *if-label-example_test_esp32s3
- <<: *if-label-target_test
- <<: *if-dev-push
changes: *patterns-build-example_test
- <<: *if-dev-push
changes: *patterns-build_components
- <<: *if-dev-push
changes: *patterns-build_system
- <<: *if-dev-push
changes: *patterns-downloadable-tools
- <<: *if-dev-push
changes: *patterns-example_test
- <<: *if-dev-push
changes: *patterns-example_test-bt
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
- <<: *if-dev-push
changes: *patterns-example_test-ethernet
- <<: *if-dev-push
changes: *patterns-example_test-i154
- <<: *if-dev-push
changes: *patterns-example_test-nvs_encr_hmac
- <<: *if-dev-push
changes: *patterns-example_test-sdio
- <<: *if-dev-push
changes: *patterns-example_test-usb
- <<: *if-dev-push
changes: *patterns-example_test-wifi
- <<: *if-dev-push
changes: *patterns-target_test-adc
- <<: *if-dev-push
changes: *patterns-target_test-ecdsa
- <<: *if-dev-push
changes: *patterns-target_test-i154
- <<: *if-dev-push
changes: *patterns-target_test-wifi
.rules:build:example_test-esp32: .rules:build:example_test-esp32:
rules: rules:
- <<: *if-revert-branch - <<: *if-revert-branch
@ -1209,8 +1151,6 @@
changes: *patterns-example_test changes: *patterns-example_test
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-bt changes: *patterns-example_test-bt
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-ethernet changes: *patterns-example_test-ethernet
- <<: *if-dev-push - <<: *if-dev-push
@ -1253,8 +1193,6 @@
changes: *patterns-example_test changes: *patterns-example_test
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-bt changes: *patterns-example_test-bt
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-ethernet changes: *patterns-example_test-ethernet
- <<: *if-dev-push - <<: *if-dev-push
@ -1298,8 +1236,6 @@
changes: *patterns-example_test changes: *patterns-example_test
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-bt changes: *patterns-example_test-bt
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-ethernet changes: *patterns-example_test-ethernet
- <<: *if-dev-push - <<: *if-dev-push
@ -1342,8 +1278,6 @@
changes: *patterns-example_test changes: *patterns-example_test
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-bt changes: *patterns-example_test-bt
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-ethernet changes: *patterns-example_test-ethernet
- <<: *if-dev-push - <<: *if-dev-push
@ -1386,8 +1320,6 @@
changes: *patterns-example_test changes: *patterns-example_test
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-bt changes: *patterns-example_test-bt
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-ethernet changes: *patterns-example_test-ethernet
- <<: *if-dev-push - <<: *if-dev-push
@ -1430,8 +1362,6 @@
changes: *patterns-example_test changes: *patterns-example_test
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-bt changes: *patterns-example_test-bt
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-ethernet changes: *patterns-example_test-ethernet
- <<: *if-dev-push - <<: *if-dev-push
@ -1474,8 +1404,6 @@
changes: *patterns-example_test changes: *patterns-example_test
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-bt changes: *patterns-example_test-bt
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-ethernet changes: *patterns-example_test-ethernet
- <<: *if-dev-push - <<: *if-dev-push
@ -1599,8 +1527,6 @@
changes: *patterns-example_test changes: *patterns-example_test
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-bt changes: *patterns-example_test-bt
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-example_test-ethernet changes: *patterns-example_test-ethernet
- <<: *if-dev-push - <<: *if-dev-push
@ -2403,19 +2329,6 @@
- <<: *if-dev-push - <<: *if-dev-push
changes: *patterns-target_test-adc changes: *patterns-target_test-adc
.rules:test:example_test-esp32-ccs811:
rules:
- <<: *if-revert-branch
when: never
- <<: *if-protected
- <<: *if-label-build-only
when: never
- <<: *if-label-example_test
- <<: *if-label-example_test_esp32
- <<: *if-label-target_test
- <<: *if-dev-push
changes: *patterns-example_test-ccs811
.rules:test:example_test-esp32-ethernet: .rules:test:example_test-esp32-ethernet:
rules: rules:
- <<: *if-revert-branch - <<: *if-revert-branch

View File

@ -39,6 +39,7 @@
--parallel-count ${CI_NODE_TOTAL:-1} --parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1} --parallel-index ${CI_NODE_INDEX:-1}
${PYTEST_EXTRA_FLAGS} ${PYTEST_EXTRA_FLAGS}
--app-info-filepattern \"list_job_*.txt\"
.pytest_examples_dir_template: .pytest_examples_dir_template:
extends: .pytest_template extends: .pytest_template
@ -100,7 +101,7 @@ pytest_examples_esp32_jtag:
pytest_examples_esp32_ccs811: pytest_examples_esp32_ccs811:
extends: extends:
- .pytest_examples_dir_template - .pytest_examples_dir_template
- .rules:test:example_test-esp32-ccs811 - .rules:test:example_test-esp32
needs: needs:
- build_pytest_examples_esp32 - build_pytest_examples_esp32
tags: [ esp32, ccs811 ] tags: [ esp32, ccs811 ]
@ -1132,36 +1133,6 @@ pytest_test_apps_esp32s3_mspi_f4r4:
- cd tools/ci/python_packages/tiny_test_fw/bin - cd tools/ci/python_packages/tiny_test_fw/bin
- run_cmd python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE -e $ENV_FILE --known_failure_cases_file $CI_PROJECT_DIR/known_failure_cases/known_failure_cases.txt - run_cmd python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE -e $ENV_FILE --known_failure_cases_file $CI_PROJECT_DIR/known_failure_cases/known_failure_cases.txt
.example_test_template:
extends: .target_test_job_template
needs:
- assign_example_test
variables:
TEST_CASE_PATH: "$CI_PROJECT_DIR/examples"
CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/examples/test_configs"
.example_test_esp32_template:
extends:
- .example_test_template
- .rules:test:example_test-esp32
.example_test_esp32c3_template:
extends:
- .example_test_template
- .rules:test:example_test-esp32c3
example_test_001C:
extends: .example_test_esp32_template
tags:
- ESP32
- Example_GENERIC
example_test_C3_GENERIC:
extends: .example_test_esp32c3_template
tags:
- ESP32C3
- Example_GENERIC
.unit_test_template: .unit_test_template:
extends: .target_test_job_template extends: .target_test_job_template
needs: # the assign already needs all the build jobs needs: # the assign already needs all the build jobs
@ -1208,7 +1179,7 @@ example_test_C3_GENERIC:
UT_001: UT_001:
extends: .unit_test_esp32_template extends: .unit_test_esp32_template
parallel: 16 parallel: 2
tags: tags:
- ESP32_IDF - ESP32_IDF
- UT_T1_1 - UT_T1_1
@ -1217,7 +1188,6 @@ UT_001:
UT_002: UT_002:
extends: .unit_test_esp32_template extends: .unit_test_esp32_template
parallel: 7
tags: tags:
- ESP32_IDF - ESP32_IDF
- UT_T1_1 - UT_T1_1
@ -1264,7 +1234,7 @@ UT_028:
UT_035: UT_035:
extends: .unit_test_esp32s2_template extends: .unit_test_esp32s2_template
parallel: 16 parallel: 2
tags: tags:
- ESP32S2_IDF - ESP32S2_IDF
- UT_T1_1 - UT_T1_1
@ -1279,7 +1249,6 @@ UT_S2_SDSPI:
UT_C2: UT_C2:
extends: .unit_test_esp32c2_template extends: .unit_test_esp32c2_template
parallel: 8
tags: tags:
- ESP32C2_IDF - ESP32C2_IDF
- UT_T1_1 - UT_T1_1
@ -1287,7 +1256,6 @@ UT_C2:
UT_C3: UT_C3:
extends: .unit_test_esp32c3_template extends: .unit_test_esp32c3_template
parallel: 11
tags: tags:
- ESP32C3_IDF - ESP32C3_IDF
- UT_T1_1 - UT_T1_1
@ -1302,21 +1270,19 @@ UT_C3_SDSPI:
UT_C6: UT_C6:
extends: .unit_test_esp32c6_template extends: .unit_test_esp32c6_template
parallel: 8
tags: tags:
- ESP32C6_IDF - ESP32C6_IDF
- UT_T1_1 - UT_T1_1
UT_H2: UT_H2:
extends: .unit_test_esp32h2_template extends: .unit_test_esp32h2_template
parallel: 5
tags: tags:
- ESP32H2_IDF - ESP32H2_IDF
- UT_T1_1 - UT_T1_1
UT_S3: UT_S3:
extends: .unit_test_esp32s3_template extends: .unit_test_esp32s3_template
parallel: 9 parallel: 2
tags: tags:
- ESP32S3_IDF - ESP32S3_IDF
- UT_T1_1 - UT_T1_1

View File

@ -143,7 +143,7 @@ repos:
require_serial: true require_serial: true
additional_dependencies: additional_dependencies:
- PyYAML == 5.3.1 - PyYAML == 5.3.1
- idf_build_apps - idf_build_apps~=1.0
- id: sort-build-test-rules-ymls - id: sort-build-test-rules-ymls
name: sort .build-test-rules.yml files name: sort .build-test-rules.yml files
entry: tools/ci/check_build_test_rules.py sort-yaml entry: tools/ci/check_build_test_rules.py sort-yaml

View File

@ -13,6 +13,8 @@
# This is an experimental feature, and if you found any bug or have any question, please report to # This is an experimental feature, and if you found any bug or have any question, please report to
# https://github.com/espressif/pytest-embedded/issues # https://github.com/espressif/pytest-embedded/issues
import glob
import json
import logging import logging
import os import os
import re import re
@ -36,11 +38,11 @@ from pytest_embedded.utils import find_by_suffix
from pytest_embedded_idf.dut import IdfDut from pytest_embedded_idf.dut import IdfDut
try: try:
from idf_ci_utils import to_list from idf_ci_utils import IDF_PATH, to_list
from idf_unity_tester import CaseTester from idf_unity_tester import CaseTester
except ImportError: except ImportError:
sys.path.append(os.path.join(os.path.dirname(__file__), 'tools', 'ci')) sys.path.append(os.path.join(os.path.dirname(__file__), 'tools', 'ci'))
from idf_ci_utils import to_list from idf_ci_utils import IDF_PATH, to_list
from idf_unity_tester import CaseTester from idf_unity_tester import CaseTester
try: try:
@ -252,7 +254,7 @@ def test_case_name(request: FixtureRequest, target: str, config: str) -> str:
@pytest.fixture @pytest.fixture
@multi_dut_fixture @multi_dut_fixture
def build_dir(app_path: str, target: Optional[str], config: Optional[str]) -> str: def build_dir(request: FixtureRequest, app_path: str, target: Optional[str], config: Optional[str]) -> str:
""" """
Check local build dir with the following priority: Check local build dir with the following priority:
@ -261,11 +263,6 @@ def build_dir(app_path: str, target: Optional[str], config: Optional[str]) -> st
3. build_<config> 3. build_<config>
4. build 4. build
Args:
app_path: app path
target: target
config: config
Returns: Returns:
valid build directory valid build directory
""" """
@ -278,6 +275,25 @@ def build_dir(app_path: str, target: Optional[str], config: Optional[str]) -> st
check_dirs.append(f'build_{config}') check_dirs.append(f'build_{config}')
check_dirs.append('build') check_dirs.append('build')
idf_pytest_embedded = request.config.stash[_idf_pytest_embedded_key]
build_dir = None
if idf_pytest_embedded.apps_list is not None:
for check_dir in check_dirs:
binary_path = os.path.join(app_path, check_dir)
if binary_path in idf_pytest_embedded.apps_list:
build_dir = check_dir
break
if build_dir is None:
pytest.skip(
f'app path {app_path} with target {target} and config {config} is not listed in app info list files'
)
return '' # not reachable, to fool mypy
if build_dir:
check_dirs = [build_dir]
for check_dir in check_dirs: for check_dir in check_dirs:
binary_path = os.path.join(app_path, check_dir) binary_path = os.path.join(app_path, check_dir)
if os.path.isdir(binary_path): if os.path.isdir(binary_path):
@ -286,9 +302,8 @@ def build_dir(app_path: str, target: Optional[str], config: Optional[str]) -> st
logging.warning('checking binary path: %s... missing... try another place', binary_path) logging.warning('checking binary path: %s... missing... try another place', binary_path)
recommend_place = check_dirs[0]
raise ValueError( raise ValueError(
f'no build dir valid. Please build the binary via "idf.py -B {recommend_place} build" and run pytest again' f'no build dir valid. Please build the binary via "idf.py -B {check_dirs[0]} build" and run pytest again'
) )
@ -412,20 +427,32 @@ def dev_user(request: FixtureRequest) -> str:
# Hook functions # # Hook functions #
################## ##################
def pytest_addoption(parser: pytest.Parser) -> None: def pytest_addoption(parser: pytest.Parser) -> None:
base_group = parser.getgroup('idf') idf_group = parser.getgroup('idf')
base_group.addoption( idf_group.addoption(
'--sdkconfig', '--sdkconfig',
help='sdkconfig postfix, like sdkconfig.ci.<config>. (Default: None, which would build all found apps)', help='sdkconfig postfix, like sdkconfig.ci.<config>. (Default: None, which would build all found apps)',
) )
base_group.addoption('--known-failure-cases-file', help='known failure cases file path') idf_group.addoption('--known-failure-cases-file', help='known failure cases file path')
base_group.addoption( idf_group.addoption(
'--dev-user', '--dev-user',
help='user name associated with some specific device/service used during the test execution', help='user name associated with some specific device/service used during the test execution',
) )
base_group.addoption( idf_group.addoption(
'--dev-passwd', '--dev-passwd',
help='password associated with some specific device/service used during the test execution', help='password associated with some specific device/service used during the test execution',
) )
idf_group.addoption(
'--app-info-basedir',
default=IDF_PATH,
help='app info base directory. specify this value when you\'re building under a '
'different IDF_PATH. (Default: $IDF_PATH)',
)
idf_group.addoption(
'--app-info-filepattern',
help='glob pattern to specify the files that include built app info generated by '
'`idf-build-apps --collect-app-info ...`. will not raise ValueError when binary '
'paths not exist in local file system if not listed recorded in the app info.',
)
_idf_pytest_embedded_key = pytest.StashKey['IdfPytestEmbedded']() _idf_pytest_embedded_key = pytest.StashKey['IdfPytestEmbedded']()
@ -446,10 +473,34 @@ def pytest_configure(config: Config) -> None:
if not target: # also could specify through markexpr via "-m" if not target: # also could specify through markexpr via "-m"
target = get_target_marker_from_expr(config.getoption('markexpr') or '') target = get_target_marker_from_expr(config.getoption('markexpr') or '')
apps_list = None
app_info_basedir = config.getoption('app_info_basedir')
app_info_filepattern = config.getoption('app_info_filepattern')
if app_info_filepattern:
apps_list = []
for file in glob.glob(os.path.join(IDF_PATH, app_info_filepattern)):
with open(file) as fr:
for line in fr.readlines():
if not line.strip():
continue
# each line is a valid json
app_info = json.loads(line.strip())
if app_info_basedir and app_info['app_dir'].startswith(app_info_basedir):
relative_app_dir = os.path.relpath(app_info['app_dir'], app_info_basedir)
apps_list.append(os.path.join(IDF_PATH, os.path.join(relative_app_dir, app_info['build_dir'])))
print('Detected app: ', apps_list[-1])
else:
print(
f'WARNING: app_info base dir {app_info_basedir} not recognizable in {app_info["app_dir"]}, skipping...'
)
continue
config.stash[_idf_pytest_embedded_key] = IdfPytestEmbedded( config.stash[_idf_pytest_embedded_key] = IdfPytestEmbedded(
target=target, target=target,
sdkconfig=config.getoption('sdkconfig'), sdkconfig=config.getoption('sdkconfig'),
known_failure_cases_file=config.getoption('known_failure_cases_file'), known_failure_cases_file=config.getoption('known_failure_cases_file'),
apps_list=apps_list,
) )
config.pluginmanager.register(config.stash[_idf_pytest_embedded_key]) config.pluginmanager.register(config.stash[_idf_pytest_embedded_key])
@ -470,11 +521,13 @@ class IdfPytestEmbedded:
target: str, target: str,
sdkconfig: Optional[str] = None, sdkconfig: Optional[str] = None,
known_failure_cases_file: Optional[str] = None, known_failure_cases_file: Optional[str] = None,
apps_list: Optional[List[str]] = None,
): ):
# CLI options to filter the test cases # CLI options to filter the test cases
self.target = target.lower() self.target = target.lower()
self.sdkconfig = sdkconfig self.sdkconfig = sdkconfig
self.known_failure_patterns = self._parse_known_failure_cases_file(known_failure_cases_file) self.known_failure_patterns = self._parse_known_failure_cases_file(known_failure_cases_file)
self.apps_list = apps_list
self._failed_cases: List[Tuple[str, bool, bool]] = [] # (test_case_name, is_known_failure_cases, is_xfail) self._failed_cases: List[Tuple[str, bool, bool]] = [] # (test_case_name, is_known_failure_cases, is_xfail)
@ -599,7 +652,11 @@ class IdfPytestEmbedded:
test_case_name = item.funcargs.get('test_case_name', '') test_case_name = item.funcargs.get('test_case_name', '')
if test_case_name: if test_case_name:
self._failed_cases.append( self._failed_cases.append(
(test_case_name, self._is_known_failure(test_case_name), report.keywords.get('xfail', False)) (
test_case_name,
self._is_known_failure(test_case_name),
report.keywords.get('xfail', False),
)
) )
return report return report

View File

@ -1,5 +1,12 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps # Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
.i2c_dependencies: &i2c_dependencies
depends_filepatterns:
# components
- examples/system/console/advanced/components/**/*
- components/driver/i2c/**/*
- components/driver/Kconfig
examples/peripherals/adc/continuous_read: examples/peripherals/adc/continuous_read:
disable: disable:
- if: SOC_ADC_DMA_SUPPORTED != 1 - if: SOC_ADC_DMA_SUPPORTED != 1
@ -24,11 +31,13 @@ examples/peripherals/i2c/i2c_self_test:
disable: disable:
- if: SOC_I2C_SUPPORT_SLAVE != 1 - if: SOC_I2C_SUPPORT_SLAVE != 1
reason: the test requires both master and slave reason: the test requires both master and slave
<<: *i2c_dependencies
examples/peripherals/i2c/i2c_simple: examples/peripherals/i2c/i2c_simple:
disable: disable:
- if: SOC_I2C_SUPPORT_SLAVE != 1 - if: SOC_I2C_SUPPORT_SLAVE != 1
reason: the test requires both master and slave reason: the test requires both master and slave
<<: *i2c_dependencies
examples/peripherals/i2c/i2c_tools: examples/peripherals/i2c/i2c_tools:
disable: disable:
@ -37,6 +46,7 @@ examples/peripherals/i2c/i2c_tools:
- if: IDF_TARGET != "esp32" - if: IDF_TARGET != "esp32"
temporary: true temporary: true
reason: lack of runners reason: lack of runners
<<: *i2c_dependencies
examples/peripherals/i2s/i2s_adc_dac: examples/peripherals/i2s/i2s_adc_dac:
disable: disable:

View File

@ -5,7 +5,7 @@ python_files = pytest_*.py
# ignore PytestExperimentalApiWarning for record_xml_attribute # ignore PytestExperimentalApiWarning for record_xml_attribute
# set traceback to "short" to prevent the overwhelming tracebacks # set traceback to "short" to prevent the overwhelming tracebacks
addopts = addopts =
-s -s -vv
--embedded-services esp,idf --embedded-services esp,idf
--tb short --tb short
--strict-markers --strict-markers

View File

@ -8,10 +8,10 @@ This file is used in CI generate binary files for different kinds of apps
import argparse import argparse
import os import os
import sys import sys
import typing as t
import unittest import unittest
from collections import defaultdict from collections import defaultdict
from pathlib import Path from pathlib import Path
from typing import List, Optional, Set
import yaml import yaml
from idf_build_apps import LOGGER, App, build_apps, find_apps, setup_logging from idf_build_apps import LOGGER, App, build_apps, find_apps, setup_logging
@ -20,25 +20,28 @@ from idf_ci_utils import IDF_PATH, PytestApp, get_pytest_cases, get_ttfw_app_pat
CI_ENV_VARS = { CI_ENV_VARS = {
'EXTRA_CFLAGS': '-Werror -Werror=deprecated-declarations -Werror=unused-variable ' 'EXTRA_CFLAGS': '-Werror -Werror=deprecated-declarations -Werror=unused-variable '
'-Werror=unused-but-set-variable -Werror=unused-function -Wstrict-prototypes', '-Werror=unused-but-set-variable -Werror=unused-function -Wstrict-prototypes',
'EXTRA_CXXFLAGS': '-Werror -Werror=deprecated-declarations -Werror=unused-variable ' 'EXTRA_CXXFLAGS': '-Werror -Werror=deprecated-declarations -Werror=unused-variable '
'-Werror=unused-but-set-variable -Werror=unused-function', '-Werror=unused-but-set-variable -Werror=unused-function',
'LDGEN_CHECK_MAPPING': '1', 'LDGEN_CHECK_MAPPING': '1',
} }
def get_pytest_apps( def get_pytest_apps(
paths: List[str], paths: t.List[str],
target: str, target: str,
config_rules_str: List[str], config_rules_str: t.List[str],
marker_expr: str, marker_expr: str,
filter_expr: str, filter_expr: str,
preserve_all: bool = False, preserve_all: bool = False,
extra_default_build_targets: Optional[List[str]] = None, extra_default_build_targets: t.Optional[t.List[str]] = None,
) -> List[App]: modified_components: t.Optional[t.List[str]] = None,
modified_files: t.Optional[t.List[str]] = None,
ignore_app_dependencies_filepatterns: t.Optional[t.List[str]] = None,
) -> t.List[App]:
pytest_cases = get_pytest_cases(paths, target, marker_expr, filter_expr) pytest_cases = get_pytest_cases(paths, target, marker_expr, filter_expr)
_paths: Set[str] = set() _paths: t.Set[str] = set()
test_related_app_configs = defaultdict(set) test_related_app_configs = defaultdict(set)
for case in pytest_cases: for case in pytest_cases:
for app in case.apps: for app in case.apps:
@ -53,6 +56,9 @@ def get_pytest_apps(
if not case.nightly_run: if not case.nightly_run:
test_related_app_configs[app.path].add(app.config) test_related_app_configs[app.path].add(app.config)
if not extra_default_build_targets:
extra_default_build_targets = []
app_dirs = list(_paths) app_dirs = list(_paths)
if not app_dirs: if not app_dirs:
raise RuntimeError('No apps found') raise RuntimeError('No apps found')
@ -68,9 +74,12 @@ def get_pytest_apps(
build_log_path='build_log.txt', build_log_path='build_log.txt',
size_json_path='size.json', size_json_path='size.json',
check_warnings=True, check_warnings=True,
manifest_rootpath=IDF_PATH,
manifest_files=[str(p) for p in Path(IDF_PATH).glob('**/.build-test-rules.yml')], manifest_files=[str(p) for p in Path(IDF_PATH).glob('**/.build-test-rules.yml')],
default_build_targets=SUPPORTED_TARGETS + extra_default_build_targets, default_build_targets=SUPPORTED_TARGETS + extra_default_build_targets,
manifest_rootpath=IDF_PATH, modified_components=modified_components,
modified_files=modified_files,
ignore_app_dependencies_filepatterns=ignore_app_dependencies_filepatterns,
) )
for app in apps: for app in apps:
@ -85,12 +94,15 @@ def get_pytest_apps(
def get_cmake_apps( def get_cmake_apps(
paths: List[str], paths: t.List[str],
target: str, target: str,
config_rules_str: List[str], config_rules_str: t.List[str],
preserve_all: bool = False, preserve_all: bool = False,
extra_default_build_targets: Optional[List[str]] = None, extra_default_build_targets: t.Optional[t.List[str]] = None,
) -> List[App]: modified_components: t.Optional[t.List[str]] = None,
modified_files: t.Optional[t.List[str]] = None,
ignore_app_dependencies_filepatterns: t.Optional[t.List[str]] = None,
) -> t.List[App]:
ttfw_app_dirs = get_ttfw_app_paths(paths, target) ttfw_app_dirs = get_ttfw_app_paths(paths, target)
apps = find_apps( apps = find_apps(
@ -103,9 +115,12 @@ def get_cmake_apps(
size_json_path='size.json', size_json_path='size.json',
check_warnings=True, check_warnings=True,
preserve=False, preserve=False,
manifest_rootpath=IDF_PATH,
manifest_files=[str(p) for p in Path(IDF_PATH).glob('**/.build-test-rules.yml')], manifest_files=[str(p) for p in Path(IDF_PATH).glob('**/.build-test-rules.yml')],
default_build_targets=SUPPORTED_TARGETS + extra_default_build_targets, default_build_targets=SUPPORTED_TARGETS + extra_default_build_targets,
manifest_rootpath=IDF_PATH, modified_components=modified_components,
modified_files=modified_files,
ignore_app_dependencies_filepatterns=ignore_app_dependencies_filepatterns,
) )
apps_for_build = [] apps_for_build = []
@ -130,7 +145,7 @@ APPS_BUILD_PER_JOB = 30
def main(args: argparse.Namespace) -> None: def main(args: argparse.Namespace) -> None:
extra_default_build_targets: List[str] = [] extra_default_build_targets: t.List[str] = []
if args.default_build_test_rules: if args.default_build_test_rules:
with open(args.default_build_test_rules) as fr: with open(args.default_build_test_rules) as fr:
configs = yaml.safe_load(fr) configs = yaml.safe_load(fr)
@ -148,6 +163,9 @@ def main(args: argparse.Namespace) -> None:
args.filter_expr, args.filter_expr,
args.preserve_all, args.preserve_all,
extra_default_build_targets, extra_default_build_targets,
args.modified_components,
args.modified_files,
args.ignore_app_dependencies_filepatterns,
) )
else: else:
LOGGER.info('build apps. will skip pytest apps with pytest scripts') LOGGER.info('build apps. will skip pytest apps with pytest scripts')
@ -157,6 +175,9 @@ def main(args: argparse.Namespace) -> None:
args.config, args.config,
args.preserve_all, args.preserve_all,
extra_default_build_targets, extra_default_build_targets,
args.modified_components,
args.modified_files,
args.ignore_app_dependencies_filepatterns,
) )
LOGGER.info('Found %d apps after filtering', len(apps)) LOGGER.info('Found %d apps after filtering', len(apps))
@ -175,22 +196,28 @@ def main(args: argparse.Namespace) -> None:
if abs_extra_preserve_dir == abs_app_dir or abs_extra_preserve_dir in abs_app_dir.parents: if abs_extra_preserve_dir == abs_app_dir or abs_extra_preserve_dir in abs_app_dir.parents:
app.preserve = True app.preserve = True
sys.exit( res = build_apps(
build_apps( apps,
apps, parallel_count=args.parallel_count,
parallel_count=args.parallel_count, parallel_index=args.parallel_index,
parallel_index=args.parallel_index, dry_run=False,
dry_run=False, build_verbose=args.build_verbose,
build_verbose=args.build_verbose, keep_going=True,
keep_going=True, collect_size_info='size_info.txt',
collect_size_info=args.collect_size_info, collect_app_info='list_job_@p.txt',
collect_app_info=args.collect_app_info, ignore_warning_strs=args.ignore_warning_str,
ignore_warning_strs=args.ignore_warning_str, ignore_warning_file=args.ignore_warning_file,
ignore_warning_file=args.ignore_warning_file, copy_sdkconfig=args.copy_sdkconfig,
copy_sdkconfig=args.copy_sdkconfig, modified_components=args.modified_components,
) modified_files=args.modified_files,
ignore_app_dependencies_filepatterns=args.ignore_app_dependencies_filepatterns,
) )
if isinstance(res, tuple):
sys.exit(res[0])
else:
sys.exit(res)
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@ -249,8 +276,7 @@ if __name__ == '__main__':
parser.add_argument( parser.add_argument(
'--ignore-warning-str', '--ignore-warning-str',
nargs='+', nargs='+',
help='Ignore the warning string that match the specified regex in the build output. ' help='Ignore the warning string that match the specified regex in the build output. space-separated list',
'Can be specified multiple times.',
) )
parser.add_argument( parser.add_argument(
'--ignore-warning-file', '--ignore-warning-file',
@ -298,6 +324,30 @@ if __name__ == '__main__':
help='by default this script would set the build flags exactly the same as the CI ones. ' help='by default this script would set the build flags exactly the same as the CI ones. '
'Set this flag to use your local build flags.', 'Set this flag to use your local build flags.',
) )
parser.add_argument(
'--modified-components',
nargs='*',
default=None,
help='space-separated list which specifies the modified components. app with `depends_components` set in the '
'corresponding manifest files would only be built if depends on any of the specified components.',
)
parser.add_argument(
'--modified-files',
nargs='*',
default=None,
help='space-separated list which specifies the modified files. app with `depends_filepatterns` set in the '
'corresponding manifest files would only be built if any of the specified file pattern matches any of the '
'specified modified files.',
)
parser.add_argument(
'-if',
'--ignore-app-dependencies-filepatterns',
nargs='*',
default=None,
help='space-separated list which specifies the file patterns used for ignoring checking the app dependencies. '
'The `depends_components` and `depends_filepatterns` set in the manifest files will be ignored when any of the '
'specified file patterns matches any of the modified files. Must be used together with --modified-files',
)
arguments = parser.parse_args() arguments = parser.parse_args()
@ -309,6 +359,37 @@ if __name__ == '__main__':
os.environ[_k] = _v os.environ[_k] = _v
LOGGER.info(f'env var {_k} set to "{_v}"') LOGGER.info(f'env var {_k} set to "{_v}"')
if os.getenv('IS_MR_PIPELINE') == '0' or os.getenv('BUILD_AND_TEST_ALL_APPS') == '1':
# if it's not MR pipeline or env var BUILD_AND_TEST_ALL_APPS=1,
# remove component dependency related arguments
if 'modified_components' in arguments:
arguments.modified_components = None
if 'modified_files' in arguments:
arguments.modified_files = None
# file patterns to tigger full build
if 'modified_components' in arguments and not arguments.ignore_app_dependencies_filepatterns:
arguments.ignore_app_dependencies_filepatterns = [
# tools
'tools/cmake/**/*',
'tools/tools.json',
# components
'components/cxx/**/*',
'components/esp_common/**/*',
'components/esp_hw_support/**/*',
'components/esp_rom/**/*',
'components/esp_system/**/*',
'components/esp_timer/**/*',
'components/freertos/**/*',
'components/hal/**/*',
'components/heap/**/*',
'components/log/**/*',
'components/newlib/**/*',
'components/riscv/**/*',
'components/soc/**/*',
'components/xtensa/**/*',
]
main(arguments) main(arguments)

View File

@ -10,22 +10,20 @@
import argparse import argparse
import os import os
import subprocess import subprocess
import typing as t
from pathlib import Path
from gitlab_api import Gitlab from gitlab_api import Gitlab
try: if t.TYPE_CHECKING:
from typing import Any, Union from gitlab.v4.objects import ProjectCommit, ProjectMergeRequest
except ImportError:
# Only used for type annotations
pass
def _get_mr_obj(source_branch): # type: (str) -> Union[Gitlab, None] def _get_mr_obj(source_branch: str) -> t.Optional['ProjectMergeRequest']:
if not source_branch:
return None
gl = Gitlab(os.getenv('CI_PROJECT_ID', 'espressif/esp-idf')) gl = Gitlab(os.getenv('CI_PROJECT_ID', 'espressif/esp-idf'))
if not gl.project: if not gl.project:
return None return None
mrs = gl.project.mergerequests.list(state='opened', source_branch=source_branch) mrs = gl.project.mergerequests.list(state='opened', source_branch=source_branch)
if mrs: if mrs:
return mrs[0] # one source branch can only have one opened MR at one moment return mrs[0] # one source branch can only have one opened MR at one moment
@ -33,7 +31,7 @@ def _get_mr_obj(source_branch): # type: (str) -> Union[Gitlab, None]
return None return None
def get_mr_iid(source_branch): # type: (str) -> str def get_mr_iid(source_branch: str) -> str:
mr = _get_mr_obj(source_branch) mr = _get_mr_obj(source_branch)
if not mr: if not mr:
return '' return ''
@ -41,40 +39,65 @@ def get_mr_iid(source_branch): # type: (str) -> str
return str(mr.iid) return str(mr.iid)
def get_mr_changed_files(source_branch): # type: (str) -> Any def get_mr_changed_files(source_branch: str) -> t.List[str]:
mr = _get_mr_obj(source_branch) mr = _get_mr_obj(source_branch)
if not mr: if not mr:
return '' return []
return subprocess.check_output(['git', 'diff', '--name-only', git_output = subprocess.check_output(
'origin/{}...origin/{}'.format(mr.target_branch, source_branch)]).decode('utf8') ['git', 'diff', '--name-only', f'origin/{mr.target_branch}...origin/{source_branch}']
).decode('utf8')
return [line.strip() for line in git_output.splitlines() if line.strip()]
def get_mr_commits(source_branch): # type: (str) -> str def get_mr_commits(source_branch: str) -> t.List['ProjectCommit']:
mr = _get_mr_obj(source_branch) mr = _get_mr_obj(source_branch)
if not mr: if not mr:
return '' return []
return '\n'.join([commit.id for commit in mr.commits()])
return list(mr.commits())
def get_mr_components(source_branch: str) -> t.List[str]:
components: t.Set[str] = set()
for f in get_mr_changed_files(source_branch):
file = Path(f)
if (
file.parts[0] == 'components'
and 'test_apps' not in file.parts
and file.parts[-1] != '.build-test-rules.yml'
):
components.add(file.parts[1])
return list(components)
def _print_list(_list: t.List[str], separator: str = '\n') -> None:
print(separator.join(_list))
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Get the latest merge request info by pipeline') parser = argparse.ArgumentParser(description='Get the latest merge request info by pipeline')
actions = parser.add_subparsers(dest='action', help='info type') actions = parser.add_subparsers(dest='action', help='info type', required=True)
common_args = argparse.ArgumentParser(add_help=False) common_args = argparse.ArgumentParser(add_help=False)
common_args.add_argument('src_branch', nargs='?', help='source branch') common_args.add_argument('src_branch', help='source branch')
actions.add_parser('id', parents=[common_args]) actions.add_parser('id', parents=[common_args])
actions.add_parser('files', parents=[common_args]) actions.add_parser('files', parents=[common_args])
actions.add_parser('commits', parents=[common_args]) actions.add_parser('commits', parents=[common_args])
actions.add_parser('components', parents=[common_args])
args = parser.parse_args() args = parser.parse_args()
if args.action == 'id': if args.action == 'id':
print(get_mr_iid(args.src_branch)) print(get_mr_iid(args.src_branch))
elif args.action == 'files': elif args.action == 'files':
print(get_mr_changed_files(args.src_branch)) _print_list(get_mr_changed_files(args.src_branch))
elif args.action == 'commits': elif args.action == 'commits':
print(get_mr_commits(args.src_branch)) _print_list([commit.id for commit in get_mr_commits(args.src_branch)])
elif args.action == 'components':
_print_list(get_mr_components(args.src_branch))
else: else:
raise NotImplementedError('not possible to get here') raise NotImplementedError('not possible to get here')

View File

@ -54,7 +54,7 @@ class IDFAssignTest(CIAssignTest.AssignTest):
super(IDFAssignTest, self).__init__(test_case_path, ci_config_file, case_group) super(IDFAssignTest, self).__init__(test_case_path, ci_config_file, case_group)
def format_build_log_path(self, parallel_num): def format_build_log_path(self, parallel_num):
return 'list_job_{}.json'.format(parallel_num) return 'list_job_{}.txt'.format(parallel_num)
def create_artifact_index_file(self, project_id=None, pipeline_id=None): def create_artifact_index_file(self, project_id=None, pipeline_id=None):
if project_id is None: if project_id is None: