Răsfoiți Sursa

Merge branch 'ci/detect_file_changes_to_assign_jobs' into 'master'

CI: Detect file changes to assign jobs

See merge request espressif/esp-idf!10652
Ivan Grokhotkov 5 ani în urmă
părinte
comite
22cc1121fb

+ 1 - 1
.editorconfig

@@ -38,6 +38,6 @@ indent_style = space
 indent_size = 4
 max_line_length = 120
 
-[{*.sh,*.yml}]
+[{*.sh,*.yml,*.yaml}]
 indent_style = space
 indent_size = 2

+ 21 - 31
.gitlab-ci.yml

@@ -2,23 +2,24 @@ stages:
   - pre_check
   - build
   - assign_test
+  - build_doc
   - host_test
   - target_test
   - test_deploy
-  - post_check
   - deploy
   - post_deploy
 
-# pipelines will not be created in such two cases:
-# 1. MR push
-# 2. push not on "master/release" branches, and not tagged
-# This behavior could be changed after the `rules: changes` feature is implemented
 workflow:
   rules:
-    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
-      when: never
+    # Disable those non-protected push triggered pipelines
     - if: '$CI_COMMIT_REF_NAME != "master" && $CI_COMMIT_BRANCH !~ /^release\/v/ && $CI_COMMIT_TAG !~ /^v\d+\.\d+(\.\d+)?($|-)/ && $CI_PIPELINE_SOURCE == "push"'
       when: never
+    - if: '$CI_COMMIT_TITLE =~ /^test[ _]ci:/'
+      when: always
+    - if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_TITLE =~ /^wip|draft:/i'
+      when: never
+    - if: '$CI_COMMIT_TITLE =~ /^wip|draft/i'
+      when: never
     - when: always
 
 variables:
@@ -55,7 +56,7 @@ variables:
   BOT_DOCKER_IMAGE_TAG: ":latest"
 
   # target test config file, used by assign test job
-  CI_TARGET_TEST_CONFIG_FILE: "$CI_PROJECT_DIR/tools/ci/config/target-test.yml"
+  CI_TARGET_TEST_CONFIG_FILE: "$CI_PROJECT_DIR/.gitlab/ci/target-test.yml"
 
   # target test repo parameters
   TEST_ENV_CONFIG_REPO: "https://gitlab-ci-token:${BOT_TOKEN}@${CI_SERVER_HOST}:${CI_SERVER_PORT}/qa/ci-test-runner-configs.git"
@@ -76,38 +77,27 @@ variables:
 before_script:
   - source tools/ci/utils.sh
   - source tools/ci/setup_python.sh
-  - apply_bot_filter
   - add_gitlab_ssh_keys
   - source tools/ci/configure_ci_environment.sh
   - *setup_tools_unless_target_test
   - fetch_submodules
 
 # used for check scripts which we want to run unconditionally
-.before_script_lesser_nofilter:
-  before_script:
-    - echo "Not setting up GitLab key, not fetching submodules, not applying bot filter"
-    - source tools/ci/utils.sh
-    - source tools/ci/setup_python.sh
-    - source tools/ci/configure_ci_environment.sh
-
-# used for everything else where we want to do no prep, except for bot filter
-.before_script_lesser:
+.before_script_no_sync_submodule:
   before_script:
     - echo "Not setting up GitLab key, not fetching submodules"
     - source tools/ci/utils.sh
     - source tools/ci/setup_python.sh
-    - apply_bot_filter
     - source tools/ci/configure_ci_environment.sh
 
-.before_script_slim:
+.before_script_minimal:
   before_script:
-    - echo "Only load utils.sh inside"
+    - echo "Only load utils.sh"
     - source tools/ci/utils.sh
 
 .before_script_macos:
   before_script:
     - source tools/ci/utils.sh
-    - apply_bot_filter
     - $IDF_PATH/tools/idf_tools.py install-python-env
     # On macOS, these tools need to be installed
     - $IDF_PATH/tools/idf_tools.py --non-interactive install cmake ninja
@@ -121,12 +111,12 @@ before_script:
     - fetch_submodules
 
 include:
-  - '/tools/ci/config/rules.yml'
-  - '/tools/ci/config/pre_check.yml'
-  - '/tools/ci/config/build.yml'
-  - '/tools/ci/config/assign-test.yml'
-  - '/tools/ci/config/host-test.yml'
-  - '/tools/ci/config/target-test.yml'
-  - '/tools/ci/config/post_check.yml'
-  - '/tools/ci/config/deploy.yml'
-  - '/tools/ci/config/post_deploy.yml'
+  - '.gitlab/ci/rules.yml'
+  - '.gitlab/ci/docs.yml'
+  - '.gitlab/ci/static-code-analysis.yml'
+  - '.gitlab/ci/pre_check.yml'
+  - '.gitlab/ci/build.yml'
+  - '.gitlab/ci/assign-test.yml'
+  - '.gitlab/ci/host-test.yml'
+  - '.gitlab/ci/target-test.yml'
+  - '.gitlab/ci/deploy.yml'

+ 251 - 0
.gitlab/ci/README.md

@@ -0,0 +1,251 @@
+# IDF CI
+
+- [IDF CI](#idf-ci)
+  - [General Workflow](#general-workflow)
+  - [What if Expected Jobs ARE NOT Created?](#what-if-expected-jobs-are-not-created)
+  - [MR labels for additional jobs](#mr-labels-for-additional-jobs)
+    - [Supported MR Labels](#supported-mr-labels)
+    - [Usages](#usages)
+    - [How to trigger a `detached` pipeline without pushing new commits?](#how-to-trigger-a-detached-pipeline-without-pushing-new-commits)
+  - [How to Develop With `rules.yml`?](#how-to-develop-with-rulesyml)
+    - [General Concepts](#general-concepts)
+    - [How to Add a New `Job`?](#how-to-add-a-new-job)
+    - [How to Add a New `Rules` Template?](#how-to-add-a-new-rules-template)
+    - [How to Add a New `if` Anchor?](#how-to-add-a-new-if-anchor)
+    - [Naming Rules](#naming-rules)
+      - [Common Naming Rules](#common-naming-rules)
+      - [`if` Anchors Naming Rules](#if-anchors-naming-rules)
+      - [`rules` Template Naming Rules](#rules-template-naming-rules)
+  - [Reusable Shell Script `tools/ci/utils.sh`](#reusable-shell-script-toolsciutilssh)
+    - [Functions](#functions)
+      - [CI Job Related](#ci-job-related)
+      - [Shell Script Related](#shell-script-related)
+
+## General Workflow
+
+1. Push to a remote branch
+2. Create an MR, choose related labels (not required)
+3. A `detached` pipeline will be created.
+4. if you push a new commit, a new pipeline will be created automatically.
+
+**Details:**
+
+1. If an MR title starts with `WIP:` or `Draft:`, push commit will NOT trigger a merge-request pipeline
+2. If a commit message starts with `test ci:`, pushing a commit will trigger a merge-request pipeline even when the MR title starts with `WIP:` or `Draft:`.
+3. If a commit message starts with `WIP:` or `Draft:`, push commit will NOT trigger a pipeline
+
+## What if Expected Jobs ARE NOT Created?
+
+1. check the file patterns
+
+   If you found a job that is not running as expected with some file changes, a git commit to improve the `pattern` will be appreciated.
+
+2. please add MR labels to run additional tests
+
+## MR labels for additional jobs
+
+### Supported MR Labels
+
+- `build`
+- `build_docs`
+- `component_ut[_esp32/esp32s2/...]`
+- `custom_test[_esp32/esp32s2/...]`
+- `docker`
+- `docs`
+- `example_test[_esp32/esp32s2/...]`
+- `fuzzer_test`
+- `host_test`
+- `integration_test`
+- `iperf_stress_test`
+- `macos`
+- `macos_test`
+- `nvs_coverage`
+- `unit_test[_esp32/esp32s2/...]`
+- `weekend_test`
+- `windows`
+
+There are two general labels (not recommended since these two labels will trigger a lot of jobs)
+
+- `target_test`: includes all target for `example_test`, `custom_test`, `component_ut`, `unit_test`, `integration_test`
+- `all_test`: includes all test labels
+
+### Usages
+
+We have two ways to run additional jobs
+
+- Add these labels in the MR `labels`
+- Add these labels in the commit message (not the first line). For example:
+
+    ```
+    ci: detect file changes to assign jobs
+  
+    test labels: example_test_esp32, custom_test_esp32
+    ```
+
+  The additional test labels line should start with `test label(s):` and the labels should be separated by space or comma.
+
+### How to trigger a `detached` pipeline without pushing new commits?
+
+Go to MR web page -> `Pipelines` tab -> click `Run pipeline` button
+
+## How to Develop With `rules.yml`?
+
+### General Concepts
+
+- `pattern`: Defined in an array. A GitLab job will be created if the changed files in this MR matched one of the patterns. For example:
+
+    ```yaml
+    .patterns-python-files: &patterns-python-files
+      - "**/*.py"
+    ```
+
+- `label`: (deprecated). Defined in an if clause, similar as the previous bot command. A GitLab job will be created if the pipeline variables contains variables in `BOT_LABEL_xxx` format. For example:
+
+    ```yaml
+    .if-label-build_docs: &if-label-build_docs
+      if: '$BOT_LABEL_BUILD_DOCS'
+    ```
+
+- `title`: Defined in an if clause. A GitLab job will be created if this title included in the MR labels or in the commit message title. For example:
+
+    ```yaml
+    .if-title-docs: &if-title-docs
+      if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*docs(?:,\w+)*$/i || $CI_COMMIT_TITLE =~ /\((?:\w+\s+)*docs(?:\s+\w+)*\)$/i'
+    ```
+
+- `rule`: A combination of various patterns, labels, and titles. It will be used by GitLab YAML `extends` keyword to tell GitLab in what conditions will this job be created. For example:
+
+    ```yaml
+    .rules:build:docs:
+      rules:
+        - <<: *if-protected
+        - <<: *if-label-build
+        - <<: *if-title-build
+        - <<: *if-label-build_docs
+        - <<: *if-title-build_docs
+        - <<: *if-label-docs
+        - <<: *if-title-docs
+        - <<: *if-dev-push
+          changes: *patterns-docs
+    ```
+
+  An example for GitLab job on how to use extends:
+
+    ```yaml
+    check_docs_lang_sync:
+      extends:
+        - .pre_check_job_template
+        - .rules:build:docs
+      script:
+        - cd docs
+        - ./check_lang_folder_sync.sh
+    ```
+
+### How to Add a New `Job`?
+
+check if there's a suitable `.rules:<rules-you-need>` template
+
+1. if there is, put this in the job `extends`. All done, now you can close this window. (`extends` could be array or string)
+2. if there isn't
+    1. check [How to Add a New `Rules` Template?](#how-to-add-a-new-rules-template), create a suitable one
+    2. follow step 1
+
+### How to Add a New `Rules` Template?
+
+check if this rule is related to `labels`, `patterns`
+
+1. if it is, please refer to [dependencies/README.md](./dependencies/README.md) and add new rules by auto-generating
+2. if it isn't, please continue reading
+
+check if there's a suitable `.if-<if-anchor-you-need>` anchor
+
+1. if there is, create a rule following [`rules` Template Naming Rules](#rules-template-naming-rules).For detail information, please refer to [GitLab Documentation `rules-if`](https://docs.gitlab.com/ee/ci/yaml/README.html#rulesif). Here's an example.
+
+    ```yaml
+    .rules:dev:
+      rules:
+        - <<: *if-trigger
+        - <<: *if-dev-push
+    ```
+
+2. if there isn't
+
+    1. check [How to Add a New `if` Anchor?](#how-to-add-a-new-if-anchor), create a suitable one
+    2. follow step 1
+
+### How to Add a New `if` Anchor?
+
+Create an `if` anchor following [`if` Anchors Naming Rules](#if-anchors-naming-rules). For detailed information about how to write the condition clause, please refer to [GitLab Documentation `only/except (advanced)](https://docs.gitlab.com/ee/ci/yaml/README.html#onlyexcept-advanced). Here's an example.
+
+```yaml
+.if-schedule: &if-schedule:
+  if: '$CI_PIPELINE_SOURCE == "schedule"'
+```
+
+### Naming Rules
+
+#### Common Naming Rules
+
+if a phrase has multi words, use `_` to concatenate them.
+
+> e.g. `regular_test`
+
+if a name has multi phrases, use `-` to concatenate them.
+
+> e.g. `regular_test-example_test`
+
+#### `if` Anchors Naming Rules
+
+- if it's a label: `.if-label-<label_name>`
+- if it's a ref: `.if-ref-<ref_name>`
+- if it's a branch: `.if-branch-<branch_name>`
+- if it's a tag: `.if-tag-<tag_name>`
+- if it's multi-type combination: `.if-ref-<release_name>-branch-<branch_name>`
+
+  **Common Phrases/Abbreviations**
+
+    - `no_label`
+
+      `$BOT_TRIGGER_WITH_LABEL == null`
+
+    - `protected`
+
+      `($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_BRANCH =~ /^release\/v/ || $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/)`
+
+    - `target_test`
+
+      a combination of `example_test`, `custom_test`, `unit_test`, `component_ut`, `integration_test` and all targets
+
+#### `rules` Template Naming Rules
+
+- if it's tag related: `.rules:tag:<tag_1>-<tag_2>`
+- if it's label related: `.rules:labels:<label_1>-<label_2>`
+- if it's test related: `.rules:test:<test_type>`
+- if it's build related: `.rules:build:<build_type>`
+- if it's pattern related: `.rules:patterns:<patterns>`
+
+## Reusable Shell Script `tools/ci/utils.sh`
+
+It is used to put all the reusable shell scripts as small functions. If you want to set `before_script: []` for you job, now you can set `extends: .before_script_slim` instead. it will only run `source tools/ci/utils.sh`
+
+If you're developing CI shell scripts, you can use these functions without `source` them. They're already included in all `before_script`
+
+To run these commands in shell script locally, place `source tools/ci/utils.sh` at the very beginning.
+
+### Functions
+
+#### CI Job Related
+
+- `add_gitlab_ssh_keys`
+- `add_github_ssh_keys`
+- `add_doc_server_ssh_keys`
+- `fetch_submodules`
+- `get_all_submodules`
+
+#### Shell Script Related
+
+- `error`: log in red color
+- `warning`: log in orange color
+- `info`: log in green color
+- `run_cmd`: run the command with duration seconds info
+- `retry_failed`: run the command with duration seconds info, retry when failed

+ 1 - 2
tools/ci/config/assign-test.yml → .gitlab/ci/assign-test.yml

@@ -1,6 +1,5 @@
 assign_test:
-  extends:
-    - .rules:assign_test:target_test-integration_test-weekend_test
+  extends: .rules:test:any_test
   tags:
     - assign_test
   image: $CI_DOCKER_REGISTRY/ubuntu-test-env$BOT_DOCKER_IMAGE_TAG

+ 64 - 186
tools/ci/config/build.yml → .gitlab/ci/build.yml

@@ -8,9 +8,7 @@
   dependencies: []
 
 .build_template_app_template:
-  extends:
-    - .build_template
-    - .rules:labels:build
+  extends: .build_template
   variables:
     LOG_PATH: "${CI_PROJECT_DIR}/log_template_app"
     BUILD_PATH: "${CI_PROJECT_DIR}/build_template_app"
@@ -46,26 +44,16 @@
 fast_template_app:
   extends:
     - .build_template_app_template
-    - .rules:build_tests:target_test-weekend_test
+    - .rules:test:target_test
   stage: pre_check
   variables:
     BUILD_COMMAND_ARGS: "-p"
-
-check_docs_gh_links:
-  extends: .build_docs_template
-  stage: pre_check
-  variables:
-    SUBMODULES_TO_FETCH: "none"
-  script:
-    - cd docs
-    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 pip install -r requirements.txt
-    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 ./build_docs.py gh-linkcheck
 #------------------------------------------------------------------------------
 
 .build_ssc_template:
   extends:
     - .build_template
-    - .rules:build_tests:integration_test
+    - .rules:build:integration_test
   artifacts:
     paths:
       - SSC/ssc_bin
@@ -94,10 +82,8 @@ build_ssc_esp32c3:
   variables:
     TARGET_NAME: "ESP32C3"
 
-.build_esp_idf_tests_cmake:
-  extends:
-    - .build_template
-    - .rules:build_tests:unit_test
+.build_esp_idf_tests_cmake_template:
+  extends: .build_template
   dependencies:  # set dependencies to null to avoid missing artifacts issue
   needs:
     - job: fast_template_app
@@ -127,29 +113,35 @@ build_ssc_esp32c3:
     - python tools/UnitTestParser.py ${BUILD_PATH}
 
 build_esp_idf_tests_cmake_esp32:
-  extends: .build_esp_idf_tests_cmake
+  extends:
+    - .build_esp_idf_tests_cmake_template
+    - .rules:build:unit_test-esp32
   variables:
     IDF_TARGET: esp32
 
 build_esp_idf_tests_cmake_esp32s2:
-  extends: .build_esp_idf_tests_cmake
+  extends:
+    - .build_esp_idf_tests_cmake_template
+    - .rules:build:unit_test-esp32s2
   variables:
     IDF_TARGET: esp32s2
 
 build_esp_idf_tests_cmake_esp32s3:
-  extends: .build_esp_idf_tests_cmake
+  extends:
+    - .build_esp_idf_tests_cmake_template
+    - .rules:build:unit_test-esp32s3
   variables:
     IDF_TARGET: esp32s3
 
 build_esp_idf_tests_cmake_esp32c3:
-  extends: .build_esp_idf_tests_cmake
+  extends:
+    - .build_esp_idf_tests_cmake_template
+    - .rules:build:unit_test-esp32c3
   variables:
     IDF_TARGET: esp32c3
 
 .build_examples_template:
-  extends:
-    - .build_template
-    - .rules:build_tests:example_test-weekend_test
+  extends: .build_template
   dependencies:  # set dependencies to null to avoid missing artifacts issue
   needs:
     - job: fast_template_app
@@ -172,6 +164,7 @@ build_esp_idf_tests_cmake_esp32c3:
 build_examples_make:
   extends:
     - .build_examples_template
+    - .rules:build:example_test-esp32
   # This is a workaround for a rarely encountered issue with building examples in CI.
   # Probably related to building of Kconfig in 'make clean' stage
   retry: 1
@@ -188,7 +181,7 @@ build_examples_make:
     IDF_TARGET: esp32  # currently we only support esp32
 
 # same as above, but for CMake
-.build_examples_cmake:
+.build_examples_cmake_template:
   extends: .build_examples_template
   artifacts:
     paths:
@@ -210,27 +203,31 @@ build_examples_make:
     BUILD_SYSTEM: cmake
 
 build_examples_cmake_esp32:
-  extends: .build_examples_cmake
+  extends:
+    - .build_examples_cmake_template
+    - .rules:build:example_test-esp32
   parallel: 10
   variables:
     IDF_TARGET: esp32
 
 build_examples_cmake_esp32s2:
-  extends: .build_examples_cmake
+  extends:
+    - .build_examples_cmake_template
+    - .rules:build:example_test-esp32s2
   parallel: 8
   variables:
     IDF_TARGET: esp32s2
 
 build_examples_cmake_esp32c3:
-  extends: .build_examples_cmake
+  extends:
+    - .build_examples_cmake_template
+    - .rules:build:example_test-esp32c3
   parallel: 8
   variables:
     IDF_TARGET: esp32c3
 
-.build_test_apps:
-  extends:
-    - .build_examples_cmake
-    - .rules:build_tests:custom_test-weekend_test
+.build_test_apps_template:
+  extends: .build_examples_cmake_template
   variables:
     TEST_PREFIX: test_apps
     TEST_RELATIVE_DIR: tools/test_apps
@@ -239,96 +236,68 @@ build_examples_cmake_esp32c3:
     - ${IDF_PATH}/tools/ci/find_apps_build_apps.sh
 
 build_test_apps_esp32:
-  extends: .build_test_apps
+  extends:
+    - .build_test_apps_template
+    - .rules:build:custom_test-esp32
   parallel: 8
   variables:
     IDF_TARGET: esp32
 
 build_test_apps_esp32s2:
-  extends: .build_test_apps
+  extends:
+    - .build_test_apps_template
+    - .rules:build:custom_test-esp32s2
   parallel: 8
   variables:
     IDF_TARGET: esp32s2
 
 build_test_apps_esp32s3:
-  extends: .build_test_apps
+  extends:
+    - .build_test_apps_template
+    - .rules:build:custom_test-esp32s3
   parallel: 8
   variables:
     IDF_TARGET: esp32s3
 
 build_test_apps_esp32c3:
-  extends: .build_test_apps
+  extends:
+    - .build_test_apps_template
+    - .rules:build:custom_test-esp32c3
   parallel: 8
   variables:
     IDF_TARGET: esp32c3
 
-.build_component_ut:
-  extends:
-    - .build_test_apps
-    - .rules:build_tests:unit_test
+.build_component_ut_template:
+  extends: .build_test_apps_template
   variables:
     TEST_PREFIX: component_ut
     TEST_RELATIVE_DIR: component_ut
 
 build_component_ut_esp32:
-  extends: .build_component_ut
+  extends:
+    - .build_component_ut_template
+    - .rules:build:component_ut-esp32
   variables:
     IDF_TARGET: esp32
 
 build_component_ut_esp32s2:
-  extends: .build_component_ut
+  extends:
+    - .build_component_ut_template
+    - .rules:build:component_ut-esp32s2
   variables:
     IDF_TARGET: esp32s2
 
 build_component_ut_esp32c3:
-  extends: .build_component_ut
-  variables:
-    IDF_TARGET: esp32c3
-
-.build_docs_template:
-  stage: build
-  image: $ESP_IDF_DOC_ENV_IMAGE
-  tags:
-    - build_docs
-  script:
-    - cd docs
-    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 pip install -r requirements.txt
-    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 ./build_docs.py -bs $DOC_BUILDERS -l $DOCLANG -t $DOCTGT build
-  parallel:
-    matrix:
-      - DOCLANG: [ "en", "zh_CN" ]
-        DOCTGT: [ "esp32", "esp32s2",  "esp32c3"]
-
-build_docs_html:
   extends:
-    - .build_docs_template
-    - .rules:labels:build_docs
-  artifacts:
-    when: always
-    paths:
-      - docs/_build/*/*/*.txt
-      - docs/_build/*/*/html/*
-    expire_in: 4 days
+    - .build_component_ut_template
+    - .rules:build:component_ut-esp32c3
   variables:
-    DOC_BUILDERS: "html"
-
-build_docs_pdf:
-  extends:
-    - .build_docs_template
-    - .rules:labels:build_docs-slim
-  artifacts:
-    when: always
-    paths:
-      - docs/_build/*/*/latex/*
-    expire_in: 4 days
-  variables:
-    DOC_BUILDERS: "latex"
+    IDF_TARGET: esp32c3
 
 .test_build_system_template:
   extends:
     - .build_template
-    - .rules:build_tests:weekend_test
-  dependencies:  # set dependencies to null to avoid missing artifacts issue
+    - .rules:build
   needs:
     - job: fast_template_app
       artifacts: false
@@ -353,7 +322,7 @@ test_build_system_cmake_macos:
   extends:
     - .test_build_system_template
     - .before_script_macos
-    - .rules:os:mac_os
+    - .rules:build:macos
   tags:
     - macos_shell
   variables:
@@ -361,8 +330,8 @@ test_build_system_cmake_macos:
 
 build_docker:
   extends:
-    - .before_script_slim
-    - .rules:protected-schedule
+    - .before_script_minimal
+    - .rules:build:docker
   stage: build
   image: espressif/docker-builder:1
   tags:
@@ -383,8 +352,8 @@ build_docker:
 
 .test-on-windows:
   extends:
-    - .before_script_slim
-    - .rules:protected-schedule
+    - .before_script_minimal
+    - .rules:build:windows
   stage: build
   image: $CI_DOCKER_REGISTRY/esp32-toolchain-win-cross
   tags:
@@ -415,14 +384,10 @@ build_cmdlinerunner:
     TEST_DIR: tools/windows/tool_setup/cmdlinerunner
 
 build_installer:
-  extends:
-    - .before_script_slim
-    - .rules:protected-schedule
+  extends: .test-on-windows
   # using a different stage here to be able to use artifacts from build_cmdlinerunner job
   stage: host_test
   image: $CI_DOCKER_REGISTRY/wine-innosetup:1
-  tags:
-    - build
   dependencies:  # set dependencies to null to avoid missing artifacts issue
   needs:
     - build_cmdlinerunner
@@ -432,96 +397,9 @@ build_installer:
 
 # This job builds template app with permutations of targets and optimization levels
 build_template_app:
+  extends:
+    - .build_template_app_template
+    - .rules:build
   needs:
     - job: fast_template_app
       artifacts: false
-  extends: .build_template_app_template
-
-# Sonarqube related jobs put here for this reason:
-# Here we have two jobs. code_quality_check and code_quality_report.
-#
-# code_quality_check will analyze the code changes between your MR and
-# code repo stored in sonarqube server. The analysis result is only shown in
-# the comments under this MR and won't be transferred to the server.
-#
-# code_quality_report will analyze and transfer both of the newly added code
-# and the analysis result to the server.
-#
-# Put in the front to ensure that the newly merged code can be stored in
-# sonarqube server ASAP, in order to avoid reporting unrelated code issues
-.sonar_scan_template:
-  stage: build
-  image:
-    name: $CI_DOCKER_REGISTRY/sonarqube-scanner:2
-  before_script:
-    - source tools/ci/utils.sh
-    - export PYTHONPATH="$CI_PROJECT_DIR/tools:$CI_PROJECT_DIR/tools/ci/python_packages:$PYTHONPATH"
-    - fetch_submodules
-    # Exclude the submodules, all paths ends with /**
-    - export SUBMODULES=$(get_all_submodules)
-    # get all exclude paths specified in tools/ci/sonar_exclude_list.txt | ignore lines start with # | xargs | replace all <space> to <comma>
-    - export CUSTOM_EXCLUDES=$(cat $CI_PROJECT_DIR/tools/ci/sonar_exclude_list.txt | grep -v '^#' | xargs | sed -e 's/ /,/g')
-    # Exclude the report dir
-    - export EXCLUSIONS="$SUBMODULES,$REPORT_DIR/**,docs/_static/**,**/*.png,**/*.jpg"
-    - python $NORMALIZE_CLANGTIDY_PY $CI_PROJECT_DIR/$REPORT_DIR/warnings.txt $CI_PROJECT_DIR/$REPORT_DIR/clang_tidy_report.txt $CI_PROJECT_DIR
-  variables:
-    GIT_DEPTH: 0
-    NORMALIZE_CLANGTIDY_PY: $CI_PROJECT_DIR/tools/ci/normalize_clangtidy_path.py
-    REPORT_DIR: examples/get-started/hello_world/tidybuild/report
-  tags:
-    - host_test
-  dependencies:  # set dependencies to null to avoid missing artifacts issue
-  needs:
-    - clang_tidy_check_regular
-
-code_quality_check:
-  extends:
-    - .sonar_scan_template
-    - .rules:trigger
-  allow_failure: true
-  script:
-    - export CI_MR_IID=$(python ${CI_PROJECT_DIR}/tools/ci/ci_get_mr_info.py id ${CI_COMMIT_BRANCH})
-    - export CI_MR_COMMITS=$(python ${CI_PROJECT_DIR}/tools/ci/ci_get_mr_info.py commits ${CI_COMMIT_BRANCH} | tr '\n' ',')
-    # test if this branch have merge request, if not, exit 0
-    - test -n "$CI_MR_IID" || exit 0
-    - test -n "$CI_MR_COMMITS" || exit 0
-    - sonar-scanner
-      -Dsonar.analysis.mode=preview
-      -Dsonar.host.url=$SONAR_HOST_URL
-      -Dsonar.login=$SONAR_LOGIN
-      -Dsonar.sources=$CI_PROJECT_DIR
-      -Dsonar.sourceEncoding=UTF-8
-      -Dsonar.projectKey=esp-idf
-      -Dsonar.projectBaseDir=$CI_PROJECT_DIR
-      -Dsonar.exclusions=$EXCLUSIONS
-      -Dsonar.gitlab.project_id=$CI_PROJECT_ID
-      -Dsonar.gitlab.commit_sha=$CI_MR_COMMITS
-      -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME
-      -Dsonar.gitlab.failure_notification_mode=exit-code
-      -Dsonar.cxx.clangtidy.reportPath=$REPORT_DIR/clang_tidy_report.txt
-      -Dsonar.cxx.includeDirectories=components,/usr/include
-      -Dsonar.python.pylint_config=.pylintrc
-      -Dsonar.gitlab.ci_merge_request_iid=$CI_MR_IID
-      -Dsonar.gitlab.merge_request_discussion=true
-      -Dsonar.branch.name=$CI_COMMIT_REF_NAME
-
-code_quality_report:
-  extends:
-    - .sonar_scan_template
-    - .rules:protected-schedule
-  script:
-    - sonar-scanner
-      -Dsonar.host.url=$SONAR_HOST_URL
-      -Dsonar.login=$SONAR_LOGIN
-      -Dsonar.sources=$CI_PROJECT_DIR
-      -Dsonar.sourceEncoding=UTF-8
-      -Dsonar.projectKey=esp-idf
-      -Dsonar.projectBaseDir=$CI_PROJECT_DIR
-      -Dsonar.exclusions=$EXCLUSIONS
-      -Dsonar.gitlab.project_id=$CI_PROJECT_ID
-      -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA
-      -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME
-      -Dsonar.cxx.clangtidy.reportPath=$REPORT_DIR/clang_tidy_report.txt
-      -Dsonar.cxx.includeDirectories=components,/usr/include
-      -Dsonar.python.pylint_config=.pylintrc
-      -Dsonar.branch.name=$CI_COMMIT_REF_NAME

+ 100 - 0
.gitlab/ci/dependencies/README.md

@@ -0,0 +1,100 @@
+# How the `generate_rules.py` works
+
+## Functionalities
+
+This script can do only two things:
+
+1. Auto-generate some labels/titles/rules we need and update them in `rules.yml`
+2. Generate a dependency tree graph
+
+## Schema
+
+This file only used basic YAML grammar and has nothing to do with the GitLab version YAML file.
+
+It has five custom keywords:
+
+- `matrix`: An array of sub-arrays, used to replicate rules by formatting strings. You can use the format string everywhere, it will be formatted recursively
+- `labels`: An array of `labels`. Also indicates `titles` with the same names
+- `patterns`: An array of `patterns`. Patterns that not included
+- `included_in`: An array of other `rule` names. It indicates the `labels` and `patterns` will be included in all specified `rules` as well
+- `deploy`: An array of strings, used to replicate rules by adding postfix `-<item in deploy array>`. It indicates the extra `label` used in `rules`, which will explain later.
+
+## How to use this file to generate `rules.yml`
+
+Let's take a complicated example to help understand the process
+
+```yaml
+"test-{0}-{1}":
+    matrix:
+        - [a, b]
+        - [c, d]
+    labels:
+        - "{0}-{1}"
+    patterns:
+        - "{0}"
+        - pattern-not-exist
+    included_in:
+        - build-{0}
+```
+
+1. expand the mapping dicts defined by `matrix`
+
+   After this step, it will turn into 4 dicts:
+
+   | key      | labels | patterns | included_in |
+   | -------- | ------ | -------- | ----------- |
+   | test-a-c | a-c    | a        | build-a     |
+   | test-a-d | a-d    | a        | build-a     |
+   | test-b-c | b-c    | b        | build-b     |
+   | test-b-d | b-d    | b        | build-b     |
+
+   **Advanced Usage: You can overwrite a mapping by declaring it again later**, For example:
+
+   If we concatenate this part to the previous example,
+
+    ```yaml
+    # ... The same as the previous example
+    
+    test-a-c:
+        labels:
+            - overwrite
+    ```
+
+   `rule` `test-a-c` will be turned into:
+
+   | key      | labels    |
+   | -------- | --------- |
+   | test-a-c | overwrite |
+
+   **Mappings with the keyword `deploy` will also replicate by adding a postfix `-<item in deploy array>` to the mapping key**
+
+2. create rules by `included_in`
+
+   After this step, it will turn into 6 mapping dicts:
+
+   | key      | labels   | patterns |
+   | -------- | -------- | -------- |
+   | test-a-c | a-c      | a        |
+   | test-a-d | a-d      | a        |
+   | test-b-c | b-c      | b        |
+   | test-b-d | b-d      | b        |
+   | build-a  | a-c, a-d | a        |
+   | build-b  | b-c, b-d | b        |
+
+3. replace the auto-generated region in `rules.yml` with `labels`, `titles`, and `rules`. Each mapping will generate a `rule` and all the required labels/titles. `patterns` are pre-defined in `rules.yml` and could not be generated automatically. If a mapping is using a `pattern` undefined, the `pattern` will be ignored.
+
+   - If a mapping key has postfix '-preview', no `if-protected-xxx` clause will be added
+   - else if a mapping key has postfix '-production', `if-protected-no_label` clause will be added
+   - else: a mapping key `if-protected` clause will be added
+
+## Graph
+
+All `label` nodes are in green, `pattern` nodes are in cyan, `rule` nodes are in blue
+
+### Requirements
+
+There are a few extra dependencies while generating the dependency tree graph, please refer to [pygraphviz](https://github.com/pygraphviz/pygraphviz/blob/master/INSTALL.txt) documentation to install both `graphviz` and `pygraphviz`
+
+### CLI usage
+
+`python generate_rules.py --graph OUTPUT_PATH`

+ 142 - 0
.gitlab/ci/dependencies/dependencies.yml

@@ -0,0 +1,142 @@
+.all_targets: &all_targets
+  - esp32
+  - esp32s2
+  - esp32s3
+  - esp32c3
+
+.target_test: &target_test
+  - example_test
+  - custom_test
+  - unit_test
+  - component_ut
+
+"build:{0}-{1}":
+  matrix:
+    - *target_test
+    - *all_targets
+  labels:
+    - build
+  patterns:
+    - build
+
+"build:example_test-esp32":
+  labels:
+    - build
+    - weekend_test  # only have esp32 jobs
+    - iperf_stress_test  # only have esp32 jobs
+  patterns:
+    - build
+    - example_test
+
+"build:{0}":
+  matrix:
+    - [windows, docker]
+  labels:
+    - build
+    - "{0}"
+  patterns:
+    - build
+    - "{0}"
+
+"build:macos":
+  labels:
+    - build
+    - macos
+    - macos_test  # for backward compatibility
+  patterns:
+    - build
+    - macos
+
+"build:docs":
+  labels:
+    - build
+    - docs
+    - build_docs  # for backward compatibility
+  patterns:
+    - docs
+  deploy:
+    - preview
+    - production
+
+"build":
+  labels:
+    - build
+  patterns:
+    - build
+
+"test:{0}-{1}":
+  matrix:
+    - *target_test
+    - *all_targets
+  labels:
+    - "{0}"
+    - "{0}_{1}"
+  patterns:
+    - "{0}"
+    - build
+    - "build-{0}"
+  included_in:
+    - "build:{0}-{1}"
+    - test:target_test
+    - test:any_test
+
+"test:component_ut-{0}":
+  matrix:
+    - *all_targets
+  labels:
+    - component_ut
+    - "component_ut_{0}"
+    - unit_test
+    - "unit_test_{0}"
+  patterns:
+    - component_ut
+    - build
+    - "build-component_ut-{0}"
+  included_in:
+    - "build:component_ut-{0}"
+    - test:target_test
+    - test:any_test
+
+# due to the lack of runners, c3 tests will only be triggered by label
+"test:unit_test-esp32c3":
+  labels:
+    - unit_test_esp32c3
+  patterns:
+    - unit_test
+    - build
+    - "build-unit_test-esp32c3"
+  included_in:
+    - "build:unit_test-esp32c3"
+
+"test:integration_test":
+  labels:
+    - "integration_test"
+  patterns:
+    - "integration_test"
+  included_in:
+    - "build:integration_test"
+    - test:target_test
+    - test:any_test
+
+"test:host_test":
+  labels:
+    - host_test
+  patterns:
+    - host_test
+  included_in:
+    - test:any_test
+
+"labels:{0}":
+  matrix:
+    - [weekend_test, iperf_stress_test, nvs_coverage]
+  labels:
+    - "{0}"
+  included_in:
+    - test:any_test
+
+"labels:fuzzer_test-weekend_test":
+  labels:
+    - fuzzer_test
+    - weekend_test
+  included_in:
+    - test:any_test

+ 303 - 0
.gitlab/ci/dependencies/generate_rules.py

@@ -0,0 +1,303 @@
+#!/usr/bin/env python
+#
+# Copyright 2021 Espressif Systems (Shanghai) CO LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import inspect
+import os
+import sys
+from collections import defaultdict
+from itertools import product
+
+try:
+    import pygraphviz as pgv
+except ImportError:  # used when pre-commit, skip generating image
+    pass
+
+import yaml
+
+IDF_PATH = os.path.abspath(os.getenv('IDF_PATH', os.path.join(os.path.dirname(__file__), '..', '..', '..')))
+
+
+def _list(str_or_list):
+    if isinstance(str_or_list, str):
+        return [str_or_list]
+    elif isinstance(str_or_list, list):
+        return str_or_list
+    else:
+        raise ValueError('Wrong type: {}. Only supports str or list.'.format(type(str_or_list)))
+
+
+def _format_nested_dict(_dict, f_tuple):
+    res = {}
+    for k, v in _dict.items():
+        k = k.split('__')[0]
+        if isinstance(v, dict):
+            v = _format_nested_dict(v, f_tuple)
+        elif isinstance(v, list):
+            v = _format_nested_list(v, f_tuple)
+        elif isinstance(v, str):
+            v = v.format(*f_tuple)
+        res[k.format(*f_tuple)] = v
+    return res
+
+
+def _format_nested_list(_list, f_tuple):
+    res = []
+    for item in _list:
+        if isinstance(item, list):
+            item = _format_nested_list(item, f_tuple)
+        elif isinstance(item, dict):
+            item = _format_nested_dict(item, f_tuple)
+        elif isinstance(item, str):
+            item = item.format(*f_tuple)
+        res.append(item)
+    return res
+
+
+class RulesWriter:
+    AUTO_GENERATE_MARKER = inspect.cleandoc(r'''
+    ##################
+    # Auto Generated #
+    ##################
+    ''')
+
+    LABEL_TEMPLATE = inspect.cleandoc(r'''
+    .if-label-{0}: &if-label-{0}
+      if: '$BOT_LABEL_{1}'
+    ''')
+    TITLE_TEMPLATE = inspect.cleandoc(r'''
+    .if-title-{0}: &if-title-{0}
+      if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*{0}(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*{0}(?:[, ]+\w+)*/i'
+    ''')
+
+    RULE_NORM = '    - <<: *if-protected'
+    RULE_PROD = '    - <<: *if-protected-no_label'
+    RULE_LABEL_TEMPLATE = '    - <<: *if-label-{0}'
+    RULE_TITLE_TEMPLATE = '    - <<: *if-title-{0}'
+    RULE_PATTERN_TEMPLATE = '    - <<: *if-dev-push\n' \
+                            '      changes: *patterns-{0}'
+    RULES_TEMPLATE = inspect.cleandoc(r"""
+    .rules:{0}:
+      rules:
+    {1}
+    """)
+
+    KEYWORDS = ['labels', 'patterns']
+
+    def __init__(self, rules_yml, depend_yml):  # type: (str, str) -> None
+        self.rules_yml = rules_yml
+        self.rules_cfg = yaml.load(open(rules_yml), Loader=yaml.FullLoader)
+
+        self.full_cfg = yaml.load(open(depend_yml), Loader=yaml.FullLoader)
+        self.cfg = {k: v for k, v in self.full_cfg.items() if not k.startswith('.')}
+        self.cfg = self.expand_matrices()
+        self.rules = self.expand_rules()
+
+        self.graph = None
+
+    def expand_matrices(self):  # type: () -> dict
+        """
+        Expand the matrix into different rules
+        """
+        res = {}
+        for k, v in self.cfg.items():
+            res.update(self._expand_matrix(k, v))
+
+        for k, v in self.cfg.items():
+            deploy = v.get('deploy')
+            if deploy:
+                for item in _list(deploy):
+                    res['{}-{}'.format(k, item)] = v
+        return res
+
+    @staticmethod
+    def _expand_matrix(name, cfg):  # type: (str, dict) -> dict
+        """
+        Expand matrix into multi keys
+        :param cfg: single rule dict
+        :return:
+        """
+        default = {name: cfg}
+        if not cfg:
+            return default
+        matrices = cfg.pop('matrix', None)
+        if not matrices:
+            return default
+
+        res = {}
+        for comb in product(*_list(matrices)):
+            res.update(_format_nested_dict(default, comb))
+        return res
+
+    def expand_rules(self):  # type: () -> dict[str, dict[str, list]]
+        res = defaultdict(lambda: defaultdict(set))  # type: dict[str, dict[str, set]]
+        for k, v in self.cfg.items():
+            for vk, vv in v.items():
+                if vk in self.KEYWORDS:
+                    res[k][vk] = set(_list(vv))
+                else:
+                    res[k][vk] = vv
+            for key in self.KEYWORDS:  # provide empty set for missing field
+                if key not in res[k]:
+                    res[k][key] = set()
+
+        for k, v in self.cfg.items():
+            if not v:
+                continue
+            if 'included_in' in v:
+                for item in _list(v['included_in']):
+                    if 'labels' in v:
+                        res[item]['labels'].update(_list(v['labels']))
+                    if 'patterns' in v:
+                        for _pat in _list(v['patterns']):
+                            # Patterns must be pre-defined
+                            if '.patterns-{}'.format(_pat) not in self.rules_cfg:
+                                print('WARNING: pattern {} not exists'.format(_pat))
+                                continue
+                            res[item]['patterns'].add(_pat)
+
+        sorted_res = defaultdict(lambda: defaultdict(list))  # type: dict[str, dict[str, list]]
+        for k, v in res.items():
+            for vk, vv in v.items():
+                sorted_res[k][vk] = sorted(vv)
+        return sorted_res
+
+    def new_labels_titles_str(self):  # type: () -> str
+        _labels = set([])
+        for k, v in self.cfg.items():
+            if not v:
+                continue  # shouldn't be possible
+            labels = v.get('labels')
+            if not labels:
+                continue
+            _labels.update(_list(labels))
+        labels = sorted(_labels)
+
+        res = ''
+        res += '\n\n'.join([self._format_label(_label) for _label in labels])
+        res += '\n\n'
+        res += '\n\n'.join([self._format_title(_label) for _label in labels])
+        return res
+
+    @classmethod
+    def _format_label(cls, label):  # type: (str) -> str
+        return cls.LABEL_TEMPLATE.format(label, cls.bot_label_str(label))
+
+    @staticmethod
+    def bot_label_str(label):  # type: (str) -> str
+        return label.upper().replace('-', '_')
+
+    @classmethod
+    def _format_title(cls, title):  # type: (str) -> str
+        return cls.TITLE_TEMPLATE.format(title)
+
+    def new_rules_str(self):  # type: () -> str
+        res = []
+        for k, v in sorted(self.rules.items()):
+            res.append(self.RULES_TEMPLATE.format(k, self._format_rule(k, v)))
+        return '\n\n'.join(res)
+
+    def _format_rule(self, name, cfg):  # type: (str, dict) -> str
+        _rules = []
+        if name.endswith('-production'):
+            _rules.append(self.RULE_PROD)
+        else:
+            if not name.endswith('-preview'):
+                _rules.append(self.RULE_NORM)
+            for label in cfg['labels']:
+                _rules.append(self.RULE_LABEL_TEMPLATE.format(label))
+                _rules.append(self.RULE_TITLE_TEMPLATE.format(label))
+            for pattern in cfg['patterns']:
+                if '.patterns-{}'.format(pattern) in self.rules_cfg:
+                    _rules.append(self.RULE_PATTERN_TEMPLATE.format(pattern))
+                else:
+                    print('WARNING: pattern {} not exists'.format(pattern))
+        return '\n'.join(_rules)
+
+    def update_rules_yml(self):  # type: () -> bool
+        with open(self.rules_yml) as fr:
+            file_str = fr.read()
+
+        auto_generate_str = '\n{}\n\n{}\n'.format(self.new_labels_titles_str(), self.new_rules_str())
+        rest, marker, old = file_str.partition(self.AUTO_GENERATE_MARKER)
+        if old == auto_generate_str:
+            return False
+        else:
+            print(self.rules_yml, 'has been modified. Please check')
+            with open(self.rules_yml, 'w') as fw:
+                fw.write(rest + marker + auto_generate_str)
+            return True
+
+
+LABEL_COLOR = 'green'
+PATTERN_COLOR = 'cyan'
+RULE_COLOR = 'blue'
+
+
+def build_graph(rules_dict):  # type: (dict[str, dict[str, list]]) -> pgv.AGraph
+    graph = pgv.AGraph(directed=True, rankdir='LR', concentrate=True)
+
+    for k, v in rules_dict.items():
+        if not v:
+            continue
+        included_in = v.get('included_in')
+        if included_in:
+            for item in _list(included_in):
+                graph.add_node(k, color=RULE_COLOR)
+                graph.add_node(item, color=RULE_COLOR)
+                graph.add_edge(k, item, color=RULE_COLOR)
+        labels = v.get('labels')
+        if labels:
+            for _label in labels:
+                graph.add_node('label:{}'.format(_label), color=LABEL_COLOR)
+                graph.add_edge('label:{}'.format(_label), k, color=LABEL_COLOR)
+        patterns = v.get('patterns')
+        if patterns:
+            for _pat in patterns:
+                graph.add_node('pattern:{}'.format(_pat), color=PATTERN_COLOR)
+                graph.add_edge('pattern:{}'.format(_pat), k, color=PATTERN_COLOR)
+
+    return graph
+
+
+def output_graph(graph, output_path='output.png'):  # type: (pgv.AGraph, str) -> None
+    graph.layout('dot')
+    if output_path.endswith('.png'):
+        img_path = output_path
+    else:
+        img_path = os.path.join(output_path, 'output.png')
+    graph.draw(img_path)
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('rules_yml', nargs='?', default=os.path.join(IDF_PATH, '.gitlab', 'ci', 'rules.yml'),
+                        help='rules.yml file path')
+    parser.add_argument('dependencies_yml', nargs='?', default=os.path.join(IDF_PATH, '.gitlab', 'ci', 'dependencies',
+                                                                            'dependencies.yml'),
+                        help='dependencies.yml file path')
+    parser.add_argument('--graph',
+                        help='Specify PNG image output path. Use this argument to generate dependency graph')
+    args = parser.parse_args()
+
+    writer = RulesWriter(args.rules_yml, args.dependencies_yml)
+    file_modified = writer.update_rules_yml()
+
+    if args.graph:
+        dep_tree_graph = build_graph(writer.rules)
+        output_graph(dep_tree_graph)
+
+    sys.exit(file_modified)

+ 73 - 0
.gitlab/ci/deploy.yml

@@ -0,0 +1,73 @@
+.deploy_job_template:
+  extends: .before_script_no_sync_submodule
+  stage: deploy
+  image: $CI_DOCKER_REGISTRY/esp32-ci-env$BOT_DOCKER_IMAGE_TAG
+  tags:
+    - deploy
+  dependencies: []
+
+push_to_github:
+  extends:
+    - .deploy_job_template
+    - .rules:protected-no_label
+  script:
+    - add_github_ssh_keys
+    - git remote remove github &>/dev/null || true
+    - git remote add github git@github.com:espressif/esp-idf.git
+    - tools/ci/push_to_github.sh
+
+deploy_test_result:
+  extends:
+    - .deploy_job_template
+    - .rules:ref:master-schedule-always
+  image: $CI_DOCKER_REGISTRY/bot-env
+  tags:
+    - deploy_test
+  artifacts:
+    when: always
+    paths:
+      - ${CI_PROJECT_DIR}/test-management/*.log
+      # save all test logs as artifacts, make it easier to track errors
+      - ${CI_PROJECT_DIR}/TEST_LOGS
+      - $CI_PROJECT_DIR/$CI_COMMIT_SHA
+    expire_in: 1 mos
+  variables:
+    UNIT_TEST_CASE_FILE: "${CI_PROJECT_DIR}/components/idf_test/unit_test/TestCaseAll.yml"
+    BOT_ACCOUNT_CONFIG_FILE: "${CI_PROJECT_DIR}/test-management/Config/Account.local.yml"
+    TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
+    AUTO_TEST_SCRIPT_PATH: "${CI_PROJECT_DIR}/auto_test_script"
+  script:
+    - add_gitlab_ssh_keys
+    - export GIT_SHA=$(echo ${CI_COMMIT_SHA} | cut -c 1-8)
+    - export REV_COUNT=$(git rev-list --count HEAD)
+    - export SUMMARY="IDF CI test result for $GIT_SHA (r${REV_COUNT})"
+    # artifacts of job update_test_cases creates test-management folder
+    # we need to remove it so we can clone test-management folder again
+    - rm -rf test-management
+    - retry_failed git clone $TEST_MANAGEMENT_REPO
+    - python3 $CHECKOUT_REF_SCRIPT test-management test-management
+    - cd test-management
+    - echo $BOT_JIRA_ACCOUNT > ${BOT_ACCOUNT_CONFIG_FILE}
+    # update test results
+    - python3 ImportTestResult.py -r "$GIT_SHA (r${REV_COUNT})" -j $JIRA_TEST_MANAGEMENT_PROJECT -s "$SUMMARY" -l CI -p ${CI_PROJECT_DIR}/TEST_LOGS ${CI_PROJECT_DIR}/${CI_COMMIT_SHA} --pipeline_url ${CI_PIPELINE_URL}
+
+check_submodule_sync:
+  extends:
+    - .deploy_job_template
+    - .rules:protected
+  tags:
+    - github_sync
+  retry: 2
+  variables:
+    GIT_STRATEGY: clone
+    SUBMODULES_TO_FETCH: "none"
+    PUBLIC_IDF_URL: "https://github.com/espressif/esp-idf.git"
+  script:
+    - git submodule deinit --force .
+    # setting the default remote URL to the public one, to resolve relative location URLs
+    - git config remote.origin.url ${PUBLIC_IDF_URL}
+    # check if all submodules are correctly synced to public repository
+    - git submodule init
+    - git config --get-regexp '^submodule\..*\.url$' || true
+    - git submodule update --recursive
+    - echo "IDF was cloned from ${PUBLIC_IDF_URL} completely"

+ 151 - 0
.gitlab/ci/docs.yml

@@ -0,0 +1,151 @@
+# stage: pre_check
+check_readme_links:
+  extends:
+    - .pre_check_job_template
+    - .rules:build:docs
+  tags: ["build", "amd64", "internet"]
+  allow_failure: true
+  script:
+    - python ${IDF_PATH}/tools/ci/check_readme_links.py
+
+check_docs_lang_sync:
+  extends:
+    - .pre_check_job_template
+    - .rules:build:docs
+  script:
+    - cd docs
+    - ./check_lang_folder_sync.sh
+
+.build_docs_template:
+  image: $ESP_IDF_DOC_ENV_IMAGE
+  tags:
+    - build_docs
+  dependencies: []
+  script:
+    - cd docs
+    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 pip install -r requirements.txt
+    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 ./build_docs.py -bs $DOC_BUILDERS -l $DOCLANG -t $DOCTGT build
+  parallel:
+    matrix:
+      - DOCLANG: ["en", "zh_CN"]
+        DOCTGT: ["esp32", "esp32s2"]
+
+check_docs_gh_links:
+  extends:
+    - .pre_check_job_template
+    - .build_docs_template
+    - .rules:build:docs
+  script:
+    - cd docs
+    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 pip install -r requirements.txt
+    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 ./build_docs.py gh-linkcheck
+
+# stage: build_doc
+# Add this stage to let the build_docs job run in parallel with build
+.build_docs_build_stage_template:
+  extends:
+    - .build_docs_template
+    - .rules:build:docs
+  stage: build_doc
+  needs:
+    - job: check_docs_lang_sync
+      artifacts: false
+    - job: check_docs_gh_links
+      artifacts: false
+
+build_docs_html:
+  extends:
+    - .build_docs_build_stage_template
+  artifacts:
+    when: always
+    paths:
+      - docs/_build/*/*/*.txt
+      - docs/_build/*/*/html/*
+    expire_in: 4 days
+  variables:
+    DOC_BUILDERS: "html"
+
+build_docs_pdf:
+  extends:
+    - .build_docs_build_stage_template
+  artifacts:
+    when: always
+    paths:
+      - docs/_build/*/*/latex/*
+    expire_in: 4 days
+  variables:
+    DOC_BUILDERS: "latex"
+
+.deploy_docs_template:
+  extends:
+    - .before_script_no_sync_submodule
+  image: $ESP_IDF_DOC_ENV_IMAGE
+  stage: test_deploy
+  tags:
+    - deploy
+    - shiny
+  variables:
+    DOCS_BUILD_DIR: "${IDF_PATH}/docs/_build/"
+    PYTHONUNBUFFERED: 1
+  dependencies: []
+  script:
+    - add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
+    - export GIT_VER=$(git describe --always)
+    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 ${IDF_PATH}/tools/ci/deploy_docs.py
+
+# stage: test_deploy
+deploy_docs_preview:
+  extends:
+    - .deploy_docs_template
+    - .rules:build:docs-preview
+  dependencies:  # set dependencies to null to avoid missing artifacts issue
+  needs:
+    - build_docs_html
+    - build_docs_pdf
+  variables:
+    TYPE: "preview"
+    # older branches use DOCS_DEPLOY_KEY, DOCS_SERVER, DOCS_SERVER_USER, DOCS_PATH for preview server so we keep these names for 'preview'
+    DOCS_DEPLOY_PRIVATEKEY: "$DOCS_DEPLOY_KEY"
+    DOCS_DEPLOY_SERVER: "$DOCS_SERVER"
+    DOCS_DEPLOY_SERVER_USER: "$DOCS_SERVER_USER"
+    DOCS_DEPLOY_PATH: "$DOCS_PATH"
+    DOCS_DEPLOY_URL_BASE: "https://$CI_DOCKER_REGISTRY/docs/esp-idf"
+
+# stage: post_deploy
+deploy_docs_production:
+  # The DOCS_PROD_* variables used by this job are "Protected" so these branches must all be marked "Protected" in Gitlab settings
+  extends:
+    - .deploy_docs_template
+    - .rules:build:docs-production
+  stage: post_deploy
+  dependencies:  # set dependencies to null to avoid missing artifacts issue
+  needs: # ensure runs after push_to_github succeeded
+    - build_docs_html
+    - build_docs_pdf
+    - job: push_to_github
+      artifacts: false
+  variables:
+    TYPE: "preview"
+    DOCS_DEPLOY_PRIVATEKEY: "$DOCS_PROD_DEPLOY_KEY"
+    DOCS_DEPLOY_SERVER: "$DOCS_PROD_SERVER"
+    DOCS_DEPLOY_SERVER_USER: "$DOCS_PROD_SERVER_USER"
+    DOCS_DEPLOY_PATH: "$DOCS_PROD_PATH"
+    DOCS_DEPLOY_URL_BASE: "https://docs.espressif.com/projects/esp-idf"
+
+check_doc_links:
+  extends:
+    - .build_docs_template
+    - .rules:protected
+  stage: post_deploy
+  tags: ["build", "amd64", "internet"]
+  artifacts:
+    when: always
+    paths:
+      - docs/_build/*/*/*.txt
+      - docs/_build/*/*/linkcheck/*.txt
+    expire_in: 1 week
+  allow_failure: true
+  script:
+    - cd docs
+    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 pip install -r requirements.txt
+    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 ./build_docs.py -l $DOCLANG -t $DOCTGT linkcheck

+ 23 - 22
tools/ci/config/host-test.yml → .gitlab/ci/host-test.yml

@@ -1,30 +1,11 @@
 .host_test_template:
-  extends: .rules:labels:host_test
+  extends: .rules:test:host_test
   stage: host_test
   image: $CI_DOCKER_REGISTRY/esp32-ci-env$BOT_DOCKER_IMAGE_TAG
   tags:
     - host_test
   dependencies: []
-
-.host_fuzzer_test_template:
-  extends:
-    - .host_test_template
-    - .rules:labels:fuzzer_test-weekend_test-only
-  image: $CI_DOCKER_REGISTRY/afl-fuzzer-test
-  artifacts:
-    when: always
-    paths:
-      - ${FUZZER_TEST_DIR}/out/crashes
-      - ${FUZZER_TEST_DIR}/fuzz_output.log
-    expire_in: 1 week
-  script:
-    - export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 && export AFL_SKIP_CPUFREQ=1
-    - cd ${FUZZER_TEST_DIR}
-    # run AFL fuzzer for one hour
-    - ( ( make ${FUZZER_PARAMS} fuzz | tee fuzz_output.log | grep -v '\(Fuzzing test case\|Entering queue cycle\)' ) || pkill sleep ) &
-    - ( sleep 3600 || mkdir -p out/crashes/env_failed ) && pkill afl-fuz
-    # check no crashes found
-    - test -z "$(ls out/crashes/)" || exit 1
+  needs: []  # run host_test jobs immediately
 
 test_nvs_on_host:
   extends: .host_test_template
@@ -35,7 +16,7 @@ test_nvs_on_host:
 test_nvs_coverage:
   extends:
     - .host_test_template
-    - .rules:labels:nvs_coverage-only
+    - .rules:labels:nvs_coverage
   artifacts:
     paths:
       - components/nvs_flash/test_nvs_host/coverage_report
@@ -79,6 +60,26 @@ test_ldgen_on_host:
   variables:
     LC_ALL: C.UTF-8
 
+.host_fuzzer_test_template:
+  extends:
+    - .host_test_template
+    - .rules:labels:fuzzer_test-weekend_test
+  image: $CI_DOCKER_REGISTRY/afl-fuzzer-test
+  artifacts:
+    when: always
+    paths:
+      - ${FUZZER_TEST_DIR}/out/crashes
+      - ${FUZZER_TEST_DIR}/fuzz_output.log
+    expire_in: 1 week
+  script:
+    - export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 && export AFL_SKIP_CPUFREQ=1
+    - cd ${FUZZER_TEST_DIR}
+    # run AFL fuzzer for one hour
+    - ( ( make ${FUZZER_PARAMS} fuzz | tee fuzz_output.log | grep -v '\(Fuzzing test case\|Entering queue cycle\)' ) || pkill sleep ) &
+    - ( sleep 3600 || mkdir -p out/crashes/env_failed ) && pkill afl-fuz
+    # check no crashes found
+    - test -z "$(ls out/crashes/)" || exit 1
+
 test_mdns_fuzzer_on_host:
   extends: .host_fuzzer_test_template
   variables:

+ 49 - 80
tools/ci/config/pre_check.yml → .gitlab/ci/pre_check.yml

@@ -8,16 +8,10 @@
 .pre_check_job_template:
   extends:
     - .pre_check_base_template
-    - .before_script_lesser_nofilter
-
-.pre_check_job_template_with_filter:
-  extends:
-    - .pre_check_base_template
-    - .before_script_lesser
+    - .before_script_no_sync_submodule
 
 .check_pre_commit_template:
   extends: .pre_check_job_template
-  stage: pre_check
   image: "$CI_DOCKER_REGISTRY/esp-idf-pre-commit:1"
   before_script:
     - source tools/ci/utils.sh
@@ -37,14 +31,6 @@ check_pre_commit_MR:
   script:
     - python ${CI_PROJECT_DIR}/tools/ci/ci_get_mr_info.py files ${CI_COMMIT_BRANCH} | xargs pre-commit run --files
 
-check_docs_lang_sync:
-  extends: .pre_check_job_template
-  variables:
-    SUBMODULES_TO_FETCH: "none"
-  script:
-    - cd docs
-    - ./check_lang_folder_sync.sh
-
 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.
@@ -56,20 +42,20 @@ check_version:
     - tools/ci/check_idf_version.sh
 
 check_examples_cmake_make:
-  extends:
-    - .pre_check_job_template_with_filter
-    - .rules:dev
+  extends: .pre_check_job_template
   script:
-    - python ${IDF_PATH}/tools/ci/check_examples_cmake_make.py
+  - python ${IDF_PATH}/tools/ci/check_examples_cmake_make.py
 
 check_rom_api_header:
-  extends: .pre_check_job_template_with_filter
+  extends: .pre_check_job_template
   script:
     - tools/ci/check_examples_rom_header.sh
     - tools/ci/check_rom_apis.sh
 
 check_python_style:
-  extends: .pre_check_base_template
+  extends:
+    - .pre_check_base_template
+    - .rules:patterns:python-files
   artifacts:
     when: on_failure
     paths:
@@ -78,8 +64,8 @@ check_python_style:
   script:
     - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh python -m flake8 --config=$IDF_PATH/.flake8 --output-file=flake8_output.txt --tee --benchmark $IDF_PATH
 
-check_kconfigs:
-  extends: .pre_check_job_template_with_filter
+test_check_kconfigs:
+  extends: .pre_check_job_template
   artifacts:
     when: on_failure
     paths:
@@ -92,7 +78,6 @@ check_kconfigs:
     expire_in: 1 week
   script:
     - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh ${IDF_PATH}/tools/ci/test_check_kconfigs.py
-    - ${IDF_PATH}/tools/ci/check_kconfigs.py
 
 check_wifi_lib_md5:
   extends: .pre_check_base_template
@@ -107,25 +92,19 @@ check_wifi_lib_md5:
 check_public_headers:
   extends:
     - .pre_check_base_template
-    - .rules:labels:build
+    - .rules:build
   tags:
     - build
   script:
     - python tools/ci/check_public_headers.py --jobs 4 --prefix xtensa-esp32-elf-
 
-.scan_build_tests:
-  stage: pre_check
+scan_tests:
+  extends:
+    - .pre_check_base_template
+    - .rules:test:target_test
   image: $CI_DOCKER_REGISTRY/ubuntu-test-env$BOT_DOCKER_IMAGE_TAG
   tags:
     - assign_test
-  variables:
-    CI_SCAN_TESTS_PY: ${CI_PROJECT_DIR}/tools/ci/python_packages/ttfw_idf/CIScanTests.py
-    TEST_CONFIG_FILE: ${CI_PROJECT_DIR}/tools/ci/config/target-test.yml
-
-scan_tests:
-  extends:
-    - .scan_build_tests
-    - .rules:build_tests:target_test-weekend_test
   artifacts:
     paths:
       - $EXAMPLE_TEST_OUTPUT_DIR
@@ -137,59 +116,49 @@ scan_tests:
     TEST_APPS_TEST_DIR: ${CI_PROJECT_DIR}/tools/test_apps
     TEST_APPS_OUTPUT_DIR: ${CI_PROJECT_DIR}/tools/test_apps/test_configs
     COMPONENT_UT_OUTPUT_DIR: ${CI_PROJECT_DIR}/component_ut/test_configs
-    PYTHON_VER: 3
+    CI_SCAN_TESTS_PY: ${CI_PROJECT_DIR}/tools/ci/python_packages/ttfw_idf/CIScanTests.py
   script:
     - set_component_ut_vars
-    - python $CI_SCAN_TESTS_PY example_test $EXAMPLE_TEST_DIR -b make --exclude examples/build_system/idf_as_lib -c $TEST_CONFIG_FILE -o $EXAMPLE_TEST_OUTPUT_DIR
-    - python $CI_SCAN_TESTS_PY example_test $EXAMPLE_TEST_DIR -b cmake --exclude examples/build_system/idf_as_lib -c $TEST_CONFIG_FILE -o $EXAMPLE_TEST_OUTPUT_DIR
-    - python $CI_SCAN_TESTS_PY test_apps $TEST_APPS_TEST_DIR -c $TEST_CONFIG_FILE -o $TEST_APPS_OUTPUT_DIR
-    - python $CI_SCAN_TESTS_PY component_ut $COMPONENT_UT_DIRS --exclude $COMPONENT_UT_EXCLUDES -c $TEST_CONFIG_FILE -o $COMPONENT_UT_OUTPUT_DIR
+    - run_cmd python $CI_SCAN_TESTS_PY example_test $EXAMPLE_TEST_DIR -b make --exclude examples/build_system/idf_as_lib -c $CI_TARGET_TEST_CONFIG_FILE -o $EXAMPLE_TEST_OUTPUT_DIR
+    - run_cmd python $CI_SCAN_TESTS_PY example_test $EXAMPLE_TEST_DIR -b cmake --exclude examples/build_system/idf_as_lib -c $CI_TARGET_TEST_CONFIG_FILE -o $EXAMPLE_TEST_OUTPUT_DIR
+    - run_cmd python $CI_SCAN_TESTS_PY test_apps $TEST_APPS_TEST_DIR -c $CI_TARGET_TEST_CONFIG_FILE -o $TEST_APPS_OUTPUT_DIR
+    - run_cmd python $CI_SCAN_TESTS_PY component_ut $COMPONENT_UT_DIRS --exclude $COMPONENT_UT_EXCLUDES -c $CI_TARGET_TEST_CONFIG_FILE -o $COMPONENT_UT_OUTPUT_DIR
 
-check_readme_links:
-  extends: .pre_check_job_template
-  tags: [ "amd64", "deploy", "internet" ]
-  allow_failure: true
-  variables:
-    PYTHON_VER: 3
+# For release tag pipelines only, make sure the tag was created with 'git tag -a' so it will update
+# the version returned by 'git describe'
+check_version_tag:
+  extends:
+    - .pre_check_job_template
+    - .rules:tag:release
   script:
-    - python ${IDF_PATH}/tools/ci/check_readme_links.py
+    - (git cat-file -t $CI_COMMIT_REF_NAME | grep tag) || (echo "ESP-IDF versions must be annotated tags." && exit 1)
 
-.clang_tidy_check_template:
-  extends: .pre_check_base_template
-  image: ${CI_DOCKER_REGISTRY}/clang-static-analysis
-  artifacts:
-    reports:
-      junit: $IDF_PATH/output.xml
-    when: always
-    paths:
-      - $IDF_PATH/examples/get-started/hello_world/tidybuild/report/*
-    expire_in: 1 day
+check_ut_cmake_make:
+  extends: .pre_check_job_template
+  tags:
+    - build
   script:
-    - retry_failed git clone $IDF_ANALYSIS_UTILS static_analysis_utils && cd static_analysis_utils
-    # Setup parameters of triggered/regular job
-    - export TRIGGERED_RELATIVE=${BOT_LABEL_STATIC_ANALYSIS-} && export TRIGGERED_ABSOLUTE=${BOT_LABEL_STATIC_ANALYSIS_ALL-} && export TARGET_BRANCH=${BOT_CUSTOMIZED_REVISION-}
-    - ./analyze.sh $IDF_PATH/examples/get-started/hello_world/ $IDF_PATH/tools/ci/static-analysis-rules.yml $IDF_PATH/output.xml
+    - tools/ci/check_ut_cmake_make.sh
 
-clang_tidy_check:
-  extends: .clang_tidy_check_template
-  variables:
-    BOT_NEEDS_TRIGGER_BY_NAME: 1
-    BOT_LABEL_STATIC_ANALYSIS: 1
+check_artifacts_expire_time:
+  extends: .pre_check_job_template
+  script:
+    # check if we have set expire time for all artifacts
+    - python tools/ci/check_artifacts_expire_time.py
 
-clang_tidy_check_regular:
-  extends: .clang_tidy_check_template
+check_commit_msg:
+  extends: .pre_check_job_template
+  script:
+    - git status
+    - git log -n10 --oneline
+    # commit start with "WIP: " need to be squashed before merge
+    - 'git log --pretty=%s master.. -- | grep "^WIP: " && exit 1 || exit 0'
 
-clang_tidy_check_all:
-  extends: .clang_tidy_check_template
+check_tools_file_patterns:
+  extends: .pre_check_job_template
+  image: $CI_DOCKER_REGISTRY/ubuntu-test-env$BOT_DOCKER_IMAGE_TAG
   variables:
-    BOT_NEEDS_TRIGGER_BY_NAME: 1
-    BOT_LABEL_STATIC_ANALYSIS_ALL: 1
-
-# For release tag pipelines only, make sure the tag was created with 'git tag -a' so it will update
-# the version returned by 'git describe'
-check_version_tag:
-  extends:
-    - .pre_check_job_template
-    - .rules:tag:release-no_label
+    PYTHON_VER: 3.7.7
   script:
-    - (git cat-file -t $CI_COMMIT_REF_NAME | grep tag) || (echo "ESP-IDF versions must be annotated tags." && exit 1)
+    - python tools/ci/check_tools_files_patterns.py
+  allow_failure: true

+ 1131 - 0
.gitlab/ci/rules.yml

@@ -0,0 +1,1131 @@
+############
+# Patterns #
+############
+.patterns-docs: &patterns-docs
+  - ".gitlab/ci/docs.yml"
+  - "docs/**/*"
+  - "components/**/*.h"
+  - "components/**/Kconfig"
+  - "components/**/CMakeList.txt"
+  - "components/**/sdkconfig*"
+  - "tools/kconfig_new/**/*"
+  - "CONTRIBUTING.rst"
+
+.patterns-c-files: &patterns-c-files
+  - "**/*.{c,C}"
+  - "**/*.{h,H}"
+  - "components/**/Kconfig"
+  - "components/**/CMakeList.txt"
+
+.patterns-python-files: &patterns-python-files
+  - "**/*.py"
+
+.patterns-static-code-analysis: &patterns-static-code-analysis
+  - ".gitlab/ci/static-code-analysis.yml"
+  - "tools/ci/static-analysis-rules.yml"
+  - "**/*.{c,C}"
+  - "**/*.{h,H}"
+  - "**/*.py"
+
+.patterns-example_test: &patterns-example_test
+  - "tools/ci/python_packages/gitlab_api.py"
+  - "tools/ci/python_packages/idf_http_server_test/**/*"
+  - "tools/ci/python_packages/idf_iperf_test_util/**/*"
+  - "tools/ci/python_packages/tiny_test_fw/**/*"
+  - "tools/ci/python_packages/ttfw_idf/**/*"
+
+  - "tools/ci/find_apps_build_apps.sh"
+  - "tools/build_apps.py"
+  - "tools/find_apps.py"
+  - "tools/find_build_apps/**/*"
+
+  - "tools/esp_prov/**/*"
+  - "examples/**/*"
+
+.patterns-build-example_test: &patterns-build-example_test
+  - "tools/ci/build_example_dirs.txt"
+  - "tools/ci/get_supported_examples.sh"
+
+.patterns-build: &patterns-build
+  - "tools/cmake/**/*"
+  - "tools/kconfig_new/**/*"
+  - "tools/tools.json"
+
+.patterns-custom_test: &patterns-custom_test
+  - "components/espcoredump/**/*"
+
+  - "tools/ci/python_packages/gitlab_api.py"
+  - "tools/ci/python_packages/tiny_test_fw/**/*"
+  - "tools/ci/python_packages/ttfw_idf/**/*"
+
+  - "tools/ci/find_apps_build_apps.sh"
+  - "tools/build_apps.py"
+  - "tools/find_apps.py"
+  - "tools/find_build_apps/**/*"
+
+  - "tools/test_apps/**/*"
+
+.patterns-unit_test: &patterns-unit_test
+  - "tools/ci/python_packages/gitlab_api.py"
+  - "tools/ci/python_packages/tiny_test_fw/**/*"
+  - "tools/ci/python_packages/ttfw_idf/**/*"
+
+  - "tools/ci/find_apps_build_apps.sh"
+  - "tools/build_apps.py"
+  - "tools/find_apps.py"
+  - "tools/find_build_apps/**/*"
+
+  - "tools/unit-test-app/**/*"
+
+.patterns-component_ut: &patterns-component_ut
+  - "tools/ci/python_packages/gitlab_api.py"
+  - "tools/ci/python_packages/tiny_test_fw/**/*"
+  - "tools/ci/python_packages/ttfw_idf/**/*"
+
+  - "tools/ci/find_apps_build_apps.sh"
+  - "tools/build_apps.py"
+  - "tools/find_apps.py"
+  - "tools/find_build_apps/**/*"
+
+  - "components/**/test_apps/**/*"
+
+.patterns-integration_test: &patterns-integration_test
+  - "tools/ci/python_packages/tiny_test_fw/**/*"
+
+.patterns-host_test: &patterns-host_test
+  - ".gitlab/ci/host-test.yml"
+  - "tools/ci/test_autocomplete.py"
+  - "tools/ci/test_build_system.sh"
+  - "tools/ci/test_build_system_cmake.sh"
+  - "tools/ci/test_check_kconfigs.py"
+  - "tools/ci/test_configure_ci_environment.sh"
+
+  - "tools/mass_mfg/**/*"
+  - "components/nvs_flash/test_nvs_host/**/*"
+
+  - "tools/esp_app_trace/**/*"
+  - "tools/ldgen/**/*"
+
+  - "tools/gdb_panic_server.py"
+  - "tools/idf_monitor.py"
+  - "tools/test_idf_monitor/**/*"
+
+  - "tools/idf.py"
+  - "tools/idf_py_actions/**/*"
+  - "tools/test_idf_py/**/*"
+
+  - "tools/idf_size.py"
+  - "tools/test_idf_size/**/*"
+
+  - "tools/tools_schema.json"
+  - "tools/idf_tools.py"
+  - "tools/test_idf_tools/**/*"
+
+  - "tools/mkdfu.py"
+  - "tools/test_mkdfu/**/*"
+
+  - "tools/kconfig_new/**/*"
+
+.patterns-windows: &patterns-windows
+  - "tools/windows/**/*"
+
+.patterns-docker: &patterns-docker
+  - "tools/docker/**/*"
+
+##############
+# if anchors #
+##############
+.if-ref-master: &if-ref-master
+  if: '$CI_COMMIT_REF_NAME == "master"'
+
+.if-tag-release: &if-tag-release
+  if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/'
+
+.if-protected: &if-protected
+  if: '($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_BRANCH =~ /^release\/v/ || $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/)'
+
+.if-protected-no_label: &if-protected-no_label
+  if: '($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_BRANCH =~ /^release\/v/ || $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/) && $BOT_TRIGGER_WITH_LABEL == null'
+
+.if-dev-push: &if-dev-push
+  if: '$CI_COMMIT_REF_NAME != "master" && $CI_COMMIT_BRANCH !~ /^release\/v/ && $CI_COMMIT_TAG !~ /^v\d+\.\d+(\.\d+)?($|-)/ && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event")'
+
+.if-schedule: &if-schedule
+  if: '$CI_PIPELINE_SOURCE == "schedule"'
+
+.if-trigger: &if-trigger
+  if: '$CI_PIPELINE_SOURCE == "trigger"'
+
+#########
+# Rules #
+#########
+.rules:protected:
+  rules:
+    - <<: *if-protected
+
+.rules:protected-no_label:
+  rules:
+    - <<: *if-protected-no_label
+
+.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-schedule-always:
+  rules:
+    - <<: *if-ref-master
+      when: always
+    - <<: *if-schedule
+      when: always
+
+.rules:patterns:clang_tidy:
+  rules:
+    - <<: *if-protected
+    - <<: *if-dev-push
+      changes: *patterns-c-files
+
+.rules:patterns:python-files:
+  rules:
+    - <<: *if-protected
+    - <<: *if-dev-push
+      changes: *patterns-python-files
+
+.rules:patterns:static-code-analysis-preview:
+  rules:
+    - <<: *if-dev-push
+      changes: *patterns-static-code-analysis
+
+# DO NOT place comments or maintain any code from this line
+#
+# Use dependencies.yml and generate_rules.py under .gitlab/ci/dependencies dir
+# to generate labels and rules
+# Could also use pre-commit hook to finish this if detected changes on
+# these two files
+
+##################
+# Auto Generated #
+##################
+.if-label-build: &if-label-build
+  if: '$BOT_LABEL_BUILD'
+
+.if-label-build_docs: &if-label-build_docs
+  if: '$BOT_LABEL_BUILD_DOCS'
+
+.if-label-component_ut: &if-label-component_ut
+  if: '$BOT_LABEL_COMPONENT_UT'
+
+.if-label-component_ut_esp32: &if-label-component_ut_esp32
+  if: '$BOT_LABEL_COMPONENT_UT_ESP32'
+
+.if-label-component_ut_esp32c3: &if-label-component_ut_esp32c3
+  if: '$BOT_LABEL_COMPONENT_UT_ESP32C3'
+
+.if-label-component_ut_esp32s2: &if-label-component_ut_esp32s2
+  if: '$BOT_LABEL_COMPONENT_UT_ESP32S2'
+
+.if-label-component_ut_esp32s3: &if-label-component_ut_esp32s3
+  if: '$BOT_LABEL_COMPONENT_UT_ESP32S3'
+
+.if-label-custom_test: &if-label-custom_test
+  if: '$BOT_LABEL_CUSTOM_TEST'
+
+.if-label-custom_test_esp32: &if-label-custom_test_esp32
+  if: '$BOT_LABEL_CUSTOM_TEST_ESP32'
+
+.if-label-custom_test_esp32c3: &if-label-custom_test_esp32c3
+  if: '$BOT_LABEL_CUSTOM_TEST_ESP32C3'
+
+.if-label-custom_test_esp32s2: &if-label-custom_test_esp32s2
+  if: '$BOT_LABEL_CUSTOM_TEST_ESP32S2'
+
+.if-label-custom_test_esp32s3: &if-label-custom_test_esp32s3
+  if: '$BOT_LABEL_CUSTOM_TEST_ESP32S3'
+
+.if-label-docker: &if-label-docker
+  if: '$BOT_LABEL_DOCKER'
+
+.if-label-docs: &if-label-docs
+  if: '$BOT_LABEL_DOCS'
+
+.if-label-example_test: &if-label-example_test
+  if: '$BOT_LABEL_EXAMPLE_TEST'
+
+.if-label-example_test_esp32: &if-label-example_test_esp32
+  if: '$BOT_LABEL_EXAMPLE_TEST_ESP32'
+
+.if-label-example_test_esp32c3: &if-label-example_test_esp32c3
+  if: '$BOT_LABEL_EXAMPLE_TEST_ESP32C3'
+
+.if-label-example_test_esp32s2: &if-label-example_test_esp32s2
+  if: '$BOT_LABEL_EXAMPLE_TEST_ESP32S2'
+
+.if-label-example_test_esp32s3: &if-label-example_test_esp32s3
+  if: '$BOT_LABEL_EXAMPLE_TEST_ESP32S3'
+
+.if-label-fuzzer_test: &if-label-fuzzer_test
+  if: '$BOT_LABEL_FUZZER_TEST'
+
+.if-label-host_test: &if-label-host_test
+  if: '$BOT_LABEL_HOST_TEST'
+
+.if-label-integration_test: &if-label-integration_test
+  if: '$BOT_LABEL_INTEGRATION_TEST'
+
+.if-label-iperf_stress_test: &if-label-iperf_stress_test
+  if: '$BOT_LABEL_IPERF_STRESS_TEST'
+
+.if-label-macos: &if-label-macos
+  if: '$BOT_LABEL_MACOS'
+
+.if-label-macos_test: &if-label-macos_test
+  if: '$BOT_LABEL_MACOS_TEST'
+
+.if-label-nvs_coverage: &if-label-nvs_coverage
+  if: '$BOT_LABEL_NVS_COVERAGE'
+
+.if-label-unit_test: &if-label-unit_test
+  if: '$BOT_LABEL_UNIT_TEST'
+
+.if-label-unit_test_esp32: &if-label-unit_test_esp32
+  if: '$BOT_LABEL_UNIT_TEST_ESP32'
+
+.if-label-unit_test_esp32c3: &if-label-unit_test_esp32c3
+  if: '$BOT_LABEL_UNIT_TEST_ESP32C3'
+
+.if-label-unit_test_esp32s2: &if-label-unit_test_esp32s2
+  if: '$BOT_LABEL_UNIT_TEST_ESP32S2'
+
+.if-label-unit_test_esp32s3: &if-label-unit_test_esp32s3
+  if: '$BOT_LABEL_UNIT_TEST_ESP32S3'
+
+.if-label-weekend_test: &if-label-weekend_test
+  if: '$BOT_LABEL_WEEKEND_TEST'
+
+.if-label-windows: &if-label-windows
+  if: '$BOT_LABEL_WINDOWS'
+
+.if-title-build: &if-title-build
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*build(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*build(?:[, ]+\w+)*/i'
+
+.if-title-build_docs: &if-title-build_docs
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*build_docs(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*build_docs(?:[, ]+\w+)*/i'
+
+.if-title-component_ut: &if-title-component_ut
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*component_ut(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*component_ut(?:[, ]+\w+)*/i'
+
+.if-title-component_ut_esp32: &if-title-component_ut_esp32
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*component_ut_esp32(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*component_ut_esp32(?:[, ]+\w+)*/i'
+
+.if-title-component_ut_esp32c3: &if-title-component_ut_esp32c3
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*component_ut_esp32c3(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*component_ut_esp32c3(?:[, ]+\w+)*/i'
+
+.if-title-component_ut_esp32s2: &if-title-component_ut_esp32s2
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*component_ut_esp32s2(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*component_ut_esp32s2(?:[, ]+\w+)*/i'
+
+.if-title-component_ut_esp32s3: &if-title-component_ut_esp32s3
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*component_ut_esp32s3(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*component_ut_esp32s3(?:[, ]+\w+)*/i'
+
+.if-title-custom_test: &if-title-custom_test
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*custom_test(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*custom_test(?:[, ]+\w+)*/i'
+
+.if-title-custom_test_esp32: &if-title-custom_test_esp32
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*custom_test_esp32(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*custom_test_esp32(?:[, ]+\w+)*/i'
+
+.if-title-custom_test_esp32c3: &if-title-custom_test_esp32c3
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*custom_test_esp32c3(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*custom_test_esp32c3(?:[, ]+\w+)*/i'
+
+.if-title-custom_test_esp32s2: &if-title-custom_test_esp32s2
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*custom_test_esp32s2(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*custom_test_esp32s2(?:[, ]+\w+)*/i'
+
+.if-title-custom_test_esp32s3: &if-title-custom_test_esp32s3
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*custom_test_esp32s3(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*custom_test_esp32s3(?:[, ]+\w+)*/i'
+
+.if-title-docker: &if-title-docker
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*docker(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*docker(?:[, ]+\w+)*/i'
+
+.if-title-docs: &if-title-docs
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*docs(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*docs(?:[, ]+\w+)*/i'
+
+.if-title-example_test: &if-title-example_test
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*example_test(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*example_test(?:[, ]+\w+)*/i'
+
+.if-title-example_test_esp32: &if-title-example_test_esp32
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*example_test_esp32(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*example_test_esp32(?:[, ]+\w+)*/i'
+
+.if-title-example_test_esp32c3: &if-title-example_test_esp32c3
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*example_test_esp32c3(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*example_test_esp32c3(?:[, ]+\w+)*/i'
+
+.if-title-example_test_esp32s2: &if-title-example_test_esp32s2
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*example_test_esp32s2(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*example_test_esp32s2(?:[, ]+\w+)*/i'
+
+.if-title-example_test_esp32s3: &if-title-example_test_esp32s3
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*example_test_esp32s3(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*example_test_esp32s3(?:[, ]+\w+)*/i'
+
+.if-title-fuzzer_test: &if-title-fuzzer_test
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*fuzzer_test(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*fuzzer_test(?:[, ]+\w+)*/i'
+
+.if-title-host_test: &if-title-host_test
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*host_test(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*host_test(?:[, ]+\w+)*/i'
+
+.if-title-integration_test: &if-title-integration_test
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*integration_test(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*integration_test(?:[, ]+\w+)*/i'
+
+.if-title-iperf_stress_test: &if-title-iperf_stress_test
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*iperf_stress_test(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*iperf_stress_test(?:[, ]+\w+)*/i'
+
+.if-title-macos: &if-title-macos
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*macos(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*macos(?:[, ]+\w+)*/i'
+
+.if-title-macos_test: &if-title-macos_test
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*macos_test(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*macos_test(?:[, ]+\w+)*/i'
+
+.if-title-nvs_coverage: &if-title-nvs_coverage
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*nvs_coverage(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*nvs_coverage(?:[, ]+\w+)*/i'
+
+.if-title-unit_test: &if-title-unit_test
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*unit_test(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*unit_test(?:[, ]+\w+)*/i'
+
+.if-title-unit_test_esp32: &if-title-unit_test_esp32
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*unit_test_esp32(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*unit_test_esp32(?:[, ]+\w+)*/i'
+
+.if-title-unit_test_esp32c3: &if-title-unit_test_esp32c3
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*unit_test_esp32c3(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*unit_test_esp32c3(?:[, ]+\w+)*/i'
+
+.if-title-unit_test_esp32s2: &if-title-unit_test_esp32s2
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*unit_test_esp32s2(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*unit_test_esp32s2(?:[, ]+\w+)*/i'
+
+.if-title-unit_test_esp32s3: &if-title-unit_test_esp32s3
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*unit_test_esp32s3(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*unit_test_esp32s3(?:[, ]+\w+)*/i'
+
+.if-title-weekend_test: &if-title-weekend_test
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*weekend_test(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*weekend_test(?:[, ]+\w+)*/i'
+
+.if-title-windows: &if-title-windows
+  if: '$CI_MERGE_REQUEST_LABELS =~ /^(?:\w+,)*windows(?:,\w+)*$/i || $CI_COMMIT_DESCRIPTION =~ /test labels?: (?:\w+[, ]+)*windows(?:[, ]+\w+)*/i'
+
+.rules:build:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-dev-push
+      changes: *patterns-build
+
+.rules:build:component_ut-esp32:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32
+    - <<: *if-title-component_ut_esp32
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32
+    - <<: *if-title-unit_test_esp32
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+
+.rules:build:component_ut-esp32c3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32c3
+    - <<: *if-title-component_ut_esp32c3
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32c3
+    - <<: *if-title-unit_test_esp32c3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+
+.rules:build:component_ut-esp32s2:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32s2
+    - <<: *if-title-component_ut_esp32s2
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32s2
+    - <<: *if-title-unit_test_esp32s2
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+
+.rules:build:component_ut-esp32s3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32s3
+    - <<: *if-title-component_ut_esp32s3
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32s3
+    - <<: *if-title-unit_test_esp32s3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+
+.rules:build:custom_test-esp32:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32
+    - <<: *if-title-custom_test_esp32
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+
+.rules:build:custom_test-esp32c3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32c3
+    - <<: *if-title-custom_test_esp32c3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+
+.rules:build:custom_test-esp32s2:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32s2
+    - <<: *if-title-custom_test_esp32s2
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+
+.rules:build:custom_test-esp32s3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32s3
+    - <<: *if-title-custom_test_esp32s3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+
+.rules:build:docker:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-docker
+    - <<: *if-title-docker
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-docker
+
+.rules:build:docs:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-build_docs
+    - <<: *if-title-build_docs
+    - <<: *if-label-docs
+    - <<: *if-title-docs
+    - <<: *if-dev-push
+      changes: *patterns-docs
+
+.rules:build:docs-preview:
+  rules:
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-build_docs
+    - <<: *if-title-build_docs
+    - <<: *if-label-docs
+    - <<: *if-title-docs
+    - <<: *if-dev-push
+      changes: *patterns-docs
+
+.rules:build:docs-production:
+  rules:
+    - <<: *if-protected-no_label
+
+.rules:build:example_test-esp32:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32
+    - <<: *if-title-example_test_esp32
+    - <<: *if-label-iperf_stress_test
+    - <<: *if-title-iperf_stress_test
+    - <<: *if-label-weekend_test
+    - <<: *if-title-weekend_test
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+
+.rules:build:example_test-esp32c3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32c3
+    - <<: *if-title-example_test_esp32c3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+
+.rules:build:example_test-esp32s2:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32s2
+    - <<: *if-title-example_test_esp32s2
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+
+.rules:build:example_test-esp32s3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32s3
+    - <<: *if-title-example_test_esp32s3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+
+.rules:build:integration_test:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-integration_test
+    - <<: *if-title-integration_test
+    - <<: *if-dev-push
+      changes: *patterns-integration_test
+
+.rules:build:macos:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-macos
+    - <<: *if-title-macos
+    - <<: *if-label-macos_test
+    - <<: *if-title-macos_test
+    - <<: *if-dev-push
+      changes: *patterns-build
+
+.rules:build:unit_test-esp32:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32
+    - <<: *if-title-unit_test_esp32
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-unit_test
+
+.rules:build:unit_test-esp32c3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-unit_test_esp32c3
+    - <<: *if-title-unit_test_esp32c3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-unit_test
+
+.rules:build:unit_test-esp32s2:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32s2
+    - <<: *if-title-unit_test_esp32s2
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-unit_test
+
+.rules:build:unit_test-esp32s3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32s3
+    - <<: *if-title-unit_test_esp32s3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-unit_test
+
+.rules:build:windows:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-build
+    - <<: *if-title-build
+    - <<: *if-label-windows
+    - <<: *if-title-windows
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-windows
+
+.rules:labels:fuzzer_test-weekend_test:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-fuzzer_test
+    - <<: *if-title-fuzzer_test
+    - <<: *if-label-weekend_test
+    - <<: *if-title-weekend_test
+
+.rules:labels:iperf_stress_test:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-iperf_stress_test
+    - <<: *if-title-iperf_stress_test
+
+.rules:labels:nvs_coverage:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-nvs_coverage
+    - <<: *if-title-nvs_coverage
+
+.rules:labels:weekend_test:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-weekend_test
+    - <<: *if-title-weekend_test
+
+.rules:test:any_test:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32
+    - <<: *if-title-component_ut_esp32
+    - <<: *if-label-component_ut_esp32c3
+    - <<: *if-title-component_ut_esp32c3
+    - <<: *if-label-component_ut_esp32s2
+    - <<: *if-title-component_ut_esp32s2
+    - <<: *if-label-component_ut_esp32s3
+    - <<: *if-title-component_ut_esp32s3
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32
+    - <<: *if-title-custom_test_esp32
+    - <<: *if-label-custom_test_esp32c3
+    - <<: *if-title-custom_test_esp32c3
+    - <<: *if-label-custom_test_esp32s2
+    - <<: *if-title-custom_test_esp32s2
+    - <<: *if-label-custom_test_esp32s3
+    - <<: *if-title-custom_test_esp32s3
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32
+    - <<: *if-title-example_test_esp32
+    - <<: *if-label-example_test_esp32c3
+    - <<: *if-title-example_test_esp32c3
+    - <<: *if-label-example_test_esp32s2
+    - <<: *if-title-example_test_esp32s2
+    - <<: *if-label-example_test_esp32s3
+    - <<: *if-title-example_test_esp32s3
+    - <<: *if-label-fuzzer_test
+    - <<: *if-title-fuzzer_test
+    - <<: *if-label-host_test
+    - <<: *if-title-host_test
+    - <<: *if-label-integration_test
+    - <<: *if-title-integration_test
+    - <<: *if-label-iperf_stress_test
+    - <<: *if-title-iperf_stress_test
+    - <<: *if-label-nvs_coverage
+    - <<: *if-title-nvs_coverage
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32
+    - <<: *if-title-unit_test_esp32
+    - <<: *if-label-unit_test_esp32c3
+    - <<: *if-title-unit_test_esp32c3
+    - <<: *if-label-unit_test_esp32s2
+    - <<: *if-title-unit_test_esp32s2
+    - <<: *if-label-unit_test_esp32s3
+    - <<: *if-title-unit_test_esp32s3
+    - <<: *if-label-weekend_test
+    - <<: *if-title-weekend_test
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+    - <<: *if-dev-push
+      changes: *patterns-host_test
+    - <<: *if-dev-push
+      changes: *patterns-integration_test
+    - <<: *if-dev-push
+      changes: *patterns-unit_test
+
+.rules:test:component_ut-esp32:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32
+    - <<: *if-title-component_ut_esp32
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32
+    - <<: *if-title-unit_test_esp32
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+
+.rules:test:component_ut-esp32c3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32c3
+    - <<: *if-title-component_ut_esp32c3
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32c3
+    - <<: *if-title-unit_test_esp32c3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+
+.rules:test:component_ut-esp32s2:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32s2
+    - <<: *if-title-component_ut_esp32s2
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32s2
+    - <<: *if-title-unit_test_esp32s2
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+
+.rules:test:component_ut-esp32s3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32s3
+    - <<: *if-title-component_ut_esp32s3
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32s3
+    - <<: *if-title-unit_test_esp32s3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+
+.rules:test:custom_test-esp32:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32
+    - <<: *if-title-custom_test_esp32
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+
+.rules:test:custom_test-esp32c3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32c3
+    - <<: *if-title-custom_test_esp32c3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+
+.rules:test:custom_test-esp32s2:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32s2
+    - <<: *if-title-custom_test_esp32s2
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+
+.rules:test:custom_test-esp32s3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32s3
+    - <<: *if-title-custom_test_esp32s3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+
+.rules:test:example_test-esp32:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32
+    - <<: *if-title-example_test_esp32
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+
+.rules:test:example_test-esp32c3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32c3
+    - <<: *if-title-example_test_esp32c3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+
+.rules:test:example_test-esp32s2:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32s2
+    - <<: *if-title-example_test_esp32s2
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+
+.rules:test:example_test-esp32s3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32s3
+    - <<: *if-title-example_test_esp32s3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+
+.rules:test:host_test:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-host_test
+    - <<: *if-title-host_test
+    - <<: *if-dev-push
+      changes: *patterns-host_test
+
+.rules:test:integration_test:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-integration_test
+    - <<: *if-title-integration_test
+    - <<: *if-dev-push
+      changes: *patterns-integration_test
+
+.rules:test:target_test:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-component_ut
+    - <<: *if-title-component_ut
+    - <<: *if-label-component_ut_esp32
+    - <<: *if-title-component_ut_esp32
+    - <<: *if-label-component_ut_esp32c3
+    - <<: *if-title-component_ut_esp32c3
+    - <<: *if-label-component_ut_esp32s2
+    - <<: *if-title-component_ut_esp32s2
+    - <<: *if-label-component_ut_esp32s3
+    - <<: *if-title-component_ut_esp32s3
+    - <<: *if-label-custom_test
+    - <<: *if-title-custom_test
+    - <<: *if-label-custom_test_esp32
+    - <<: *if-title-custom_test_esp32
+    - <<: *if-label-custom_test_esp32c3
+    - <<: *if-title-custom_test_esp32c3
+    - <<: *if-label-custom_test_esp32s2
+    - <<: *if-title-custom_test_esp32s2
+    - <<: *if-label-custom_test_esp32s3
+    - <<: *if-title-custom_test_esp32s3
+    - <<: *if-label-example_test
+    - <<: *if-title-example_test
+    - <<: *if-label-example_test_esp32
+    - <<: *if-title-example_test_esp32
+    - <<: *if-label-example_test_esp32c3
+    - <<: *if-title-example_test_esp32c3
+    - <<: *if-label-example_test_esp32s2
+    - <<: *if-title-example_test_esp32s2
+    - <<: *if-label-example_test_esp32s3
+    - <<: *if-title-example_test_esp32s3
+    - <<: *if-label-integration_test
+    - <<: *if-title-integration_test
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32
+    - <<: *if-title-unit_test_esp32
+    - <<: *if-label-unit_test_esp32c3
+    - <<: *if-title-unit_test_esp32c3
+    - <<: *if-label-unit_test_esp32s2
+    - <<: *if-title-unit_test_esp32s2
+    - <<: *if-label-unit_test_esp32s3
+    - <<: *if-title-unit_test_esp32s3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-build-example_test
+    - <<: *if-dev-push
+      changes: *patterns-component_ut
+    - <<: *if-dev-push
+      changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-example_test
+    - <<: *if-dev-push
+      changes: *patterns-integration_test
+    - <<: *if-dev-push
+      changes: *patterns-unit_test
+
+.rules:test:unit_test-esp32:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32
+    - <<: *if-title-unit_test_esp32
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-unit_test
+
+.rules:test:unit_test-esp32c3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-unit_test_esp32c3
+    - <<: *if-title-unit_test_esp32c3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-unit_test
+
+.rules:test:unit_test-esp32s2:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32s2
+    - <<: *if-title-unit_test_esp32s2
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-unit_test
+
+.rules:test:unit_test-esp32s3:
+  rules:
+    - <<: *if-protected
+    - <<: *if-label-unit_test
+    - <<: *if-title-unit_test
+    - <<: *if-label-unit_test_esp32s3
+    - <<: *if-title-unit_test_esp32s3
+    - <<: *if-dev-push
+      changes: *patterns-build
+    - <<: *if-dev-push
+      changes: *patterns-unit_test

+ 131 - 0
.gitlab/ci/static-code-analysis.yml

@@ -0,0 +1,131 @@
+# pre_check stage
+clang_tidy_check:
+  extends:
+    - .pre_check_base_template
+    - .rules:patterns:clang_tidy
+  image: ${CI_DOCKER_REGISTRY}/clang-static-analysis
+  artifacts:
+    reports:
+      junit: $IDF_PATH/output.xml
+    when: always
+    paths:
+      - $IDF_PATH/examples/get-started/hello_world/tidybuild/report/*
+    expire_in: 1 day
+  script:
+    - retry_failed git clone $IDF_ANALYSIS_UTILS static_analysis_utils && cd static_analysis_utils
+    # Setup parameters of triggered/regular job
+    - export TARGET_BRANCH=${BOT_CUSTOMIZED_REVISION-}
+    - ./analyze.sh $IDF_PATH/examples/get-started/hello_world/ $IDF_PATH/tools/ci/static-analysis-rules.yml $IDF_PATH/output.xml
+
+# build stage
+# Sonarqube related jobs put here for this reason:
+# Here we have two jobs. code_quality_check and code_quality_report.
+#
+# code_quality_check will analyze the code changes between your MR and
+# code repo stored in sonarqube server. The analysis result is only shown in
+# the comments under this MR and won't be transferred to the server.
+#
+# code_quality_report will analyze and transfer both of the newly added code
+# and the analysis result to the server.
+#
+# Put in the front to ensure that the newly merged code can be stored in
+# sonarqube server ASAP, in order to avoid reporting unrelated code issues
+.sonar_scan_template:
+  stage: build
+  image:
+    name: $CI_DOCKER_REGISTRY/sonarqube-scanner:2
+  before_script:
+    - source tools/ci/utils.sh
+    - export PYTHONPATH="$CI_PROJECT_DIR/tools:$CI_PROJECT_DIR/tools/ci/python_packages:$PYTHONPATH"
+    - fetch_submodules
+    # Exclude the submodules, all paths ends with /**
+    - export SUBMODULES=$(get_all_submodules)
+    # get all exclude paths specified in tools/ci/sonar_exclude_list.txt | ignore lines start with # | xargs | replace all <space> to <comma>
+    - export CUSTOM_EXCLUDES=$(cat $CI_PROJECT_DIR/tools/ci/sonar_exclude_list.txt | grep -v '^#' | xargs | sed -e 's/ /,/g')
+    # Exclude the report dir
+    - export EXCLUSIONS="$SUBMODULES,$REPORT_DIR/**,docs/_static/**,**/*.png,**/*.jpg"
+    - python $NORMALIZE_CLANGTIDY_PY $CI_PROJECT_DIR/$REPORT_DIR/warnings.txt $CI_PROJECT_DIR/$REPORT_DIR/clang_tidy_report.txt $CI_PROJECT_DIR
+  variables:
+    GIT_DEPTH: 0
+    NORMALIZE_CLANGTIDY_PY: $CI_PROJECT_DIR/tools/ci/normalize_clangtidy_path.py
+    REPORT_DIR: examples/get-started/hello_world/tidybuild/report
+  tags:
+    - host_test
+  dependencies:  # Here is not a hard dependency relationship, could be skipped when only python files changed. so we do not use "needs" here.
+    - clang_tidy_check
+
+code_quality_check:
+  extends:
+    - .sonar_scan_template
+    - .rules:patterns:static-code-analysis-preview
+  allow_failure: true
+  script:
+    - export CI_MERGE_REQUEST_COMMITS=$(python ${CI_PROJECT_DIR}/tools/ci/ci_get_mr_info.py commits ${CI_COMMIT_REF_NAME} | tr '\n' ',')
+    # test if this branch have merge request, if not, exit 0
+    - test -n "$CI_MERGE_REQUEST_IID" || exit 0
+    - test -n "$CI_MERGE_REQUEST_COMMITS" || exit 0
+    - sonar-scanner
+      -Dsonar.analysis.mode=preview
+      -Dsonar.branch.name=$CI_COMMIT_REF_NAME
+      -Dsonar.cxx.clangtidy.reportPath=$REPORT_DIR/clang_tidy_report.txt
+      -Dsonar.cxx.includeDirectories=components,/usr/include
+      -Dsonar.exclusions=$EXCLUSIONS
+      -Dsonar.gitlab.ci_merge_request_iid=$CI_MERGE_REQUEST_IID
+      -Dsonar.gitlab.commit_sha=$CI_MERGE_REQUEST_COMMITS
+      -Dsonar.gitlab.failure_notification_mode=exit-code
+      -Dsonar.gitlab.merge_request_discussion=true
+      -Dsonar.gitlab.project_id=$CI_PROJECT_ID
+      -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME
+      -Dsonar.host.url=$SONAR_HOST_URL
+      -Dsonar.login=$SONAR_LOGIN
+      -Dsonar.projectBaseDir=$CI_PROJECT_DIR
+      -Dsonar.projectKey=esp-idf
+      -Dsonar.python.pylint_config=.pylintrc
+      -Dsonar.sourceEncoding=UTF-8
+      -Dsonar.sources=$CI_PROJECT_DIR
+
+code_quality_report:
+  extends:
+    - .sonar_scan_template
+    - .rules:protected
+  script:
+    - sonar-scanner
+      -Dsonar.branch.name=$CI_COMMIT_REF_NAME
+      -Dsonar.cxx.clangtidy.reportPath=$REPORT_DIR/clang_tidy_report.txt
+      -Dsonar.cxx.includeDirectories=components,/usr/include
+      -Dsonar.exclusions=$EXCLUSIONS
+      -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA
+      -Dsonar.gitlab.failure_notification_mode=exit-code
+      -Dsonar.gitlab.project_id=$CI_PROJECT_ID
+      -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME
+      -Dsonar.host.url=$SONAR_HOST_URL
+      -Dsonar.login=$SONAR_LOGIN
+      -Dsonar.projectBaseDir=$CI_PROJECT_DIR
+      -Dsonar.projectKey=esp-idf
+      -Dsonar.python.pylint_config=.pylintrc
+      -Dsonar.sourceEncoding=UTF-8
+      -Dsonar.sources=$CI_PROJECT_DIR
+
+# deploy stage
+clang_tidy_deploy:
+  extends:
+    - .deploy_job_template
+    - .rules:patterns:clang_tidy
+  needs:
+    - clang_tidy_check
+  tags:
+    - deploy
+    - shiny
+  script:
+    - add_doc_server_ssh_keys $DOCS_DEPLOY_KEY $DOCS_SERVER $DOCS_SERVER_USER
+    - export GIT_VER=$(git describe --always)
+    - cd $IDF_PATH/examples/get-started/hello_world/tidybuild
+    - mv report $GIT_VER
+    - tar czvf $GIT_VER.tar.gz $GIT_VER
+    - export STATIC_REPORT_PATH="web/static_analysis/esp-idf/"
+    - ssh $DOCS_SERVER -x "mkdir -p $STATIC_REPORT_PATH/clang-tidy"
+    - scp $GIT_VER.tar.gz $DOCS_SERVER:$STATIC_REPORT_PATH/clang-tidy
+    - ssh $DOCS_SERVER -x "cd $STATIC_REPORT_PATH/clang-tidy && tar xzvf $GIT_VER.tar.gz && rm -f latest && ln -s $GIT_VER latest"
+    # add link to view the report
+    - echo "[static analysis][clang tidy] $CI_DOCKER_REGISTRY/static_analysis/esp-idf/clang-tidy/${GIT_VER}/index.html"
+    - test ! -e ${GIT_VER}/FAILED_RULES || { echo 'Failed static analysis rules!'; cat ${GIT_VER}/FAILED_RULES; exit 1; }

+ 166 - 162
tools/ci/config/target-test.yml → .gitlab/ci/target-test.yml

@@ -35,117 +35,22 @@
     - 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
-    - .rules:tests:example_test-schedule
+  extends: .target_test_job_template
   variables:
     TEST_CASE_PATH: "$CI_PROJECT_DIR/examples"
     CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/examples/test_configs"
 
 .example_debug_template:
-  extends: .example_test_template
-  variables:
-    SUBMODULES_TO_FETCH: "all"
-
-.test_app_template:
-  extends:
-    - .target_test_job_template
-    - .rules:tests:custom_test-schedule
-  variables:
-    TEST_CASE_PATH: "$CI_PROJECT_DIR/tools/test_apps"
-    CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/tools/test_apps/test_configs"
-
-.component_ut_template:
-  extends:
-    - .target_test_job_template
-    - .rules:tests:unit_test
-  variables:
-    CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/component_ut/test_configs"
-  script:
-    - *define_config_file_name
-    # first test if config file exists, if not exist, exit 0
-    - test -e $CONFIG_FILE || exit 0
-    - set_component_ut_vars
-    # clone test env configs
-    - retry_failed git clone $TEST_ENV_CONFIG_REPO
-    - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
-    # git clone the known failure cases repo, run test
-    - retry_failed git clone $KNOWN_FAILURE_CASES_REPO known_failure_cases
-    # run test
-    - cd tools/ci/python_packages/tiny_test_fw/bin
-    - run_cmd python Runner.py $COMPONENT_UT_DIRS -c $CONFIG_FILE -e $ENV_FILE  --known_failure_cases_file $CI_PROJECT_DIR/known_failure_cases/known_failure_cases.txt
-
-.component_ut_32_template:
-  extends:
-    - .component_ut_template
-    - .rules:tests:unit_test_32
-
-.component_ut_s2_template: # unused yet
-  extends:
-    - .component_ut_template
-    - .rules:tests:unit_test_s2
-
-.unit_test_template:
-  extends:
-    - .target_test_job_template
-    - .rules:tests:unit_test
-  variables:
-    TEST_CASE_PATH: "$CI_PROJECT_DIR/tools/unit-test-app"
-    CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/components/idf_test/unit_test/CIConfigs"
-
-.unit_test_32_template:
-  extends:
-    - .unit_test_template
-    - .rules:tests:unit_test_32
-
-.unit_test_s2_template:
   extends:
-    - .unit_test_template
-    - .rules:tests:unit_test_s2
-
-.unit_test_c3_template:
-  extends:
-    - .unit_test_template
-    - .rules:tests:unit_test_c3 # due to the lack of runners, c3 tests will only be triggered by label
-
-.integration_test_template:
-  extends:
-    - .target_test_job_template
-    - .rules:tests:integration_test
-  needs:
-    - assign_test
-    - build_ssc_esp32
-  variables:
-    LOCAL_ENV_CONFIG_PATH: "$CI_PROJECT_DIR/ci-test-runner-configs/$CI_RUNNER_DESCRIPTION/ESP32_IDF"
-    LOG_PATH: "${CI_PROJECT_DIR}/${CI_COMMIT_SHA}"
-    TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/auto_test_script/TestCaseFiles"
-    MODULE_UPDATE_FILE: "$CI_PROJECT_DIR/components/idf_test/ModuleDefinition.yml"
-    CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/components/idf_test/integration_test/CIConfigs"
-    KNOWN_ISSUE_FILE: "${CI_PROJECT_DIR}/components/idf_test/integration_test/KnownIssues"
-    CI_RUNNER_SCRIPT: "${CI_PROJECT_DIR}/auto_test_script/bin/CIRunner.py"
-    PYTHONPATH: ${CI_PROJECT_DIR}/auto_test_script/packages
-    # auto_test_script only supports python 3.7.x
-    PYTHON_VER: 3.7.7
-  script:
-    - *define_config_file_name
-    # first test if config file exists, if not exist, exit 0
-    - test -e $CONFIG_FILE || exit 0
-    # clone local test env configs
-    - retry_failed git clone $TEST_ENV_CONFIG_REPO
-    - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
-    # clone test bench
-    # can not retry if downing git lfs files failed, so using empty_branch first.
-    - retry_failed git clone ${CI_AUTO_TEST_SCRIPT_REPO_URL} -b empty_branch
-    - retry_failed git -C auto_test_script checkout -f ${CI_AUTO_TEST_SCRIPT_REPO_BRANCH}
-    - python $CHECKOUT_REF_SCRIPT auto_test_script auto_test_script --customized_only
-    - cat ${KNOWN_ISSUE_FILE} >> ${TEST_CASE_FILE_PATH}/KnownIssues
-    # run test
-    - python ${CI_RUNNER_SCRIPT} -l "$LOG_PATH/$JOB_FULL_NAME" -c $CONFIG_FILE -e $LOCAL_ENV_CONFIG_PATH -t $TEST_CASE_FILE_PATH
+    - .example_test_template
+    - .rules:test:example_test-esp32
+variables:
+    SUBMODULES_TO_FETCH: "all"
 
 test_weekend_mqtt:
   extends:
     - .example_test_template
-    - .rules:labels:weekend_test-only
+    - .rules:labels:weekend_test
   tags:
     - ESP32
     - Example_WIFI
@@ -157,7 +62,7 @@ test_weekend_mqtt:
 test_weekend_network:
   extends:
     - .example_test_template
-    - .rules:labels:weekend_test-only
+    - .rules:labels:weekend_test
   image: $CI_DOCKER_REGISTRY/rpi-net-suite$BOT_DOCKER_IMAGE_TAG
   tags:
     - ESP32
@@ -167,92 +72,97 @@ test_weekend_network:
     TEST_CASE_PATH: "$CI_PROJECT_DIR/components/lwip/weekend_test"
     CONFIG_FILE_PATH: "$CI_PROJECT_DIR/components/lwip/weekend_test"
 
+.example_test_esp32_template:
+  extends:
+    - .example_test_template
+    - .rules:test:example_test-esp32
+
 example_test_001A:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   parallel: 4
   tags:
     - ESP32
     - Example_WIFI
 
 example_test_001B:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_EthKitV1
 
 example_test_001C:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   parallel: 3
   tags:
     - ESP32
     - Example_GENERIC
 
 example_test_001D:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_8Mflash_Ethernet
 
 example_test_002:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   image: $CI_DOCKER_REGISTRY/ubuntu-test-env$BOT_DOCKER_IMAGE_TAG
   tags:
     - ESP32
     - Example_ShieldBox_Basic
 
 .example_test_003:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_SDIO
 
 example_test_004A:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_TWAI1
 
 example_test_004B:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_TWAI2
 
 example_test_005:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_WIFI_BT
 
 example_test_006:
   extends:
-    - .example_test_template
-    - .rules:labels:iperf_stress_test-only
+    - .example_test_esp32_template
+    - .rules:labels:iperf_stress_test
   image: $CI_DOCKER_REGISTRY/ubuntu-test-env$BOT_DOCKER_IMAGE_TAG
   tags:
     - ESP32
     - Example_ShieldBox
 
 example_test_007:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_I2C_CCS811_SENSOR
 
 example_test_008A:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_Flash_Encryption
 
 example_test_008B:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_Flash_Encryption_OTA
 
 example_test_009:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - test_jtag_arm
@@ -264,7 +174,7 @@ example_test_009:
     PYTHON_VER: 3
 
 example_test_010:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_ExtFlash
@@ -282,25 +192,25 @@ example_test_011:
     SETUP_TOOLS: "1"
 
 example_test_012:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_RMT_IR_PROTOCOLS
 
 example_test_013:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - UT_T1_SDMODE
 
 example_test_014:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - 8Mpsram
 
 example_test_015:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_PPP
@@ -310,7 +220,7 @@ example_test_015:
       - $LOG_PATH
 
 example_test_016:
-  extends: .example_test_template
+  extends: .example_test_esp32_template
   tags:
     - ESP32
     - Example_Modbus_TCP
@@ -319,8 +229,24 @@ example_test_016:
       - $CI_PROJECT_DIR/examples/*/*/*.log
       - $LOG_PATH
 
+.test_app_template:
+  extends: .target_test_job_template
+  variables:
+    TEST_CASE_PATH: "$CI_PROJECT_DIR/tools/test_apps"
+    CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/tools/test_apps/test_configs"
+
+.test_app_esp32_template:
+  extends:
+    - .test_app_template
+    - .rules:test:custom_test-esp32
+
+.test_app_esp32s2_template:
+  extends:
+    - .test_app_template
+    - .rules:test:custom_test-esp32s2
+
 test_app_test_001:
-  extends: .test_app_template
+  extends: .test_app_esp32_template
   tags:
     - ESP32
     - test_jtag_arm
@@ -331,25 +257,25 @@ test_app_test_001:
     SETUP_TOOLS: "1"
 
 test_app_test_002:
-  extends: .test_app_template
+  extends: .test_app_esp32_template
   tags:
     - ESP32
     - Example_WIFI
 
 test_app_test_003:
-  extends: .test_app_template
+  extends: .test_app_esp32_template
   tags:
     - ESP32
     - Example_PPP
 
 test_app_test_004:
-  extends: .test_app_template
+  extends: .test_app_esp32s2_template
   tags:
     - ESP32S2
     - Example_GENERIC
 
 test_app_test_esp32_generic:
-  extends: .test_app_template
+  extends: .test_app_esp32_template
   parallel: 4
   tags:
     - ESP32
@@ -357,14 +283,58 @@ test_app_test_esp32_generic:
   variables:
     SETUP_TOOLS: "1"
 
+.component_ut_template:
+  extends: .target_test_job_template
+  variables:
+    CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/component_ut/test_configs"
+  script:
+    - *define_config_file_name
+    # first test if config file exists, if not exist, exit 0
+    - test -e $CONFIG_FILE || exit 0
+    - set_component_ut_vars
+    # clone test env configs
+    - retry_failed git clone $TEST_ENV_CONFIG_REPO
+    - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
+    # git clone the known failure cases repo, run test
+    - retry_failed git clone $KNOWN_FAILURE_CASES_REPO known_failure_cases
+    # run test
+    - cd tools/ci/python_packages/tiny_test_fw/bin
+    - run_cmd python Runner.py $COMPONENT_UT_DIRS -c $CONFIG_FILE -e $ENV_FILE --known_failure_cases_file $CI_PROJECT_DIR/known_failure_cases/known_failure_cases.txt
+
+.component_ut_esp32_template:
+  extends:
+    - .component_ut_template
+    - .rules:test:component_ut-esp32
+
 component_ut_test_001:
-  extends: .component_ut_32_template
+  extends: .component_ut_esp32_template
   tags:
     - ESP32
     - COMPONENT_UT_GENERIC
 
+.unit_test_template:
+  extends: .target_test_job_template
+  variables:
+    TEST_CASE_PATH: "$CI_PROJECT_DIR/tools/unit-test-app"
+    CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/components/idf_test/unit_test/CIConfigs"
+
+.unit_test_esp32_template:
+  extends:
+    - .unit_test_template
+    - .rules:test:unit_test-esp32
+
+.unit_test_esp32s2_template:
+  extends:
+    - .unit_test_template
+    - .rules:test:unit_test-esp32s2
+
+.unit_test_esp32c3_template:
+  extends:
+    - .unit_test_template
+    - .rules:test:unit_test-esp32c3
+
 UT_001:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   parallel: 48
   tags:
     - ESP32_IDF
@@ -373,7 +343,7 @@ UT_001:
 # Max. allowed value of 'parallel' is 50.
 
 UT_002:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   parallel: 13
   tags:
     - ESP32_IDF
@@ -381,74 +351,74 @@ UT_002:
     - psram
 
 UT_003:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   parallel: 2
   tags:
     - ESP32_IDF
     - UT_T1_SDMODE
 
 UT_004:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_SPIMODE
 
 UT_005:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_SDMODE
     - psram
 
 UT_006:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_SPIMODE
     - psram
 
 UT_008:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_GPIO
     - psram
 
 UT_012:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_LEDC
     - psram
 
 UT_014:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T2_RS485
     - psram
 
 UT_015:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_RMT
 
 UT_016:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_RMT
     - psram
 
 UT_017:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - EMMC
 
 UT_018:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   parallel: 2
   tags:
     - ESP32_IDF
@@ -456,14 +426,14 @@ UT_018:
     - 8Mpsram
 
 UT_020:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - Example_SPI_Multi_device
     - psram
 
 UT_021:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   parallel: 2
   tags:
     - ESP32_IDF
@@ -471,62 +441,62 @@ UT_021:
     - UT_T1_FlashEncryption
 
 UT_022:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T2_I2C
     - psram
 
 UT_023:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   parallel: 2
   tags:
     - ESP32_IDF
     - UT_T1_MCPWM
 
 UT_024:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_MCPWM
     - psram
 
 UT_028:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T2_1
     - psram
 
 UT_031:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_FlashEncryption
 
 UT_033:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T2_Ethernet
     - psram
 
 UT_034:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   parallel: 3
   tags:
     - ESP32_IDF
     - UT_T1_ESP_FLASH
 
 UT_035:
-  extends: .unit_test_s2_template
+  extends: .unit_test_esp32s2_template
   parallel: 50
   tags:
     - ESP32S2_IDF
     - UT_T1_1
 
 UT_036:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_PSRAMV0
@@ -534,65 +504,99 @@ UT_036:
 
 # ToDo: re-enable this job when ESP32-S2 LEDC runner installed
 # UT_037:
-#   extends: .unit_test_s2_template
+#   extends: .unit_test_esp32s2_template
 #   tags:
 #     - ESP32S2_IDF
 #     - UT_T1_LEDC
 
 UT_038:
-  extends: .unit_test_s2_template
+  extends: .unit_test_esp32s2_template
   parallel: 3
   tags:
     - ESP32S2_IDF
     - UT_T1_ESP_FLASH
 
 UT_041:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_no32kXTAL
     - psram
 
 UT_043:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_32kXTAL
     - psram
 
 UT_044:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_SDIO
 
 UT_045:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_SDIO
     - psram
 
 UT_046:
-  extends: .unit_test_32_template
+  extends: .unit_test_esp32_template
   tags:
     - ESP32_IDF
     - UT_T1_GPIO
 
 UT_C3:
-  extends: .unit_test_c3_template
+  extends: .unit_test_esp32c3_template
   parallel: 28
   tags:
     - ESP32C3_IDF
     - UT_T1_1
 
 UT_C3_FLASH:
-  extends: .unit_test_c3_template
+  extends: .unit_test_esp32c3_template
   parallel: 3
   tags:
     - ESP32C3_IDF
     - UT_T1_ESP_FLASH
 
+.integration_test_template:
+  extends:
+    - .target_test_job_template
+    - .rules:test:integration_test
+  needs:
+    - assign_test
+    - build_ssc_esp32
+  variables:
+    LOCAL_ENV_CONFIG_PATH: "$CI_PROJECT_DIR/ci-test-runner-configs/$CI_RUNNER_DESCRIPTION/ESP32_IDF"
+    LOG_PATH: "${CI_PROJECT_DIR}/${CI_COMMIT_SHA}"
+    TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/auto_test_script/TestCaseFiles"
+    MODULE_UPDATE_FILE: "$CI_PROJECT_DIR/components/idf_test/ModuleDefinition.yml"
+    CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/components/idf_test/integration_test/CIConfigs"
+    KNOWN_ISSUE_FILE: "${CI_PROJECT_DIR}/components/idf_test/integration_test/KnownIssues"
+    CI_RUNNER_SCRIPT: "${CI_PROJECT_DIR}/auto_test_script/bin/CIRunner.py"
+    PYTHONPATH: ${CI_PROJECT_DIR}/auto_test_script/packages
+    # auto_test_script only supports python 3.7.x
+    PYTHON_VER: 3.7.7
+  script:
+    - *define_config_file_name
+    # first test if config file exists, if not exist, exit 0
+    - test -e $CONFIG_FILE || exit 0
+    # clone local test env configs
+    - retry_failed git clone $TEST_ENV_CONFIG_REPO
+    - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
+    # clone test bench
+    # can not retry if downing git lfs files failed, so using empty_branch first.
+    - retry_failed git clone ${CI_AUTO_TEST_SCRIPT_REPO_URL} -b empty_branch
+    - retry_failed git -C auto_test_script checkout -f ${CI_AUTO_TEST_SCRIPT_REPO_BRANCH}
+    - python $CHECKOUT_REF_SCRIPT auto_test_script auto_test_script --customized_only
+    - cat ${KNOWN_ISSUE_FILE} >> ${TEST_CASE_FILE_PATH}/KnownIssues
+    # run test
+    - python ${CI_RUNNER_SCRIPT} -l "$LOG_PATH/$JOB_FULL_NAME" -c $CONFIG_FILE -e $LOCAL_ENV_CONFIG_PATH -t $TEST_CASE_FILE_PATH
+
 nvs_compatible_test:
   extends: .integration_test_template
   artifacts:

+ 22 - 6
.pre-commit-config.yaml

@@ -13,12 +13,12 @@ repos:
         # 3 - any directory named 'testdata'
         # 4 - IDF monitor test data
         exclude: &whitespace_excludes |
-            (?x)^(
-                .+\.(md|rst|map|bin)|
-                .+test.*\/.*expected.*|
-                .+\/testdata\/.+|
-                .+test_idf_monitor\/tests\/.+
-            )$
+          (?x)^(
+            .+\.(md|rst|map|bin)|
+            .+test.*\/.*expected.*|
+            .+\/testdata\/.+|
+            .+test_idf_monitor\/tests\/.+
+          )$
       - id: end-of-file-fixer
         exclude: *whitespace_excludes
       - id: check-executables-have-shebangs
@@ -75,3 +75,19 @@ repos:
         language: python
         files: '\.gitlab/CODEOWNERS'
         pass_filenames: false
+      - id: check-rules-yml
+        name: Check rules.yml all rules have at lease one job applied, all rules needed exist
+        entry: tools/ci/check_rules_yml.py
+        language: python
+        files: '\.gitlab/ci/.+\.yml|\.gitlab-ci.yml'
+        pass_filenames: false
+        additional_dependencies:
+          - PyYAML == 5.3.1
+      - id: check-generated-rules
+        name: Check rules are generated (based on .gitlab/ci/dependencies/dependencies.yml)
+        entry: .gitlab/ci/dependencies/generate_rules.py
+        language: python
+        files: '\.gitlab/ci/dependencies/.+'
+        pass_filenames: false
+        additional_dependencies:
+          - PyYAML == 5.3.1

+ 3 - 0
.pylintrc

@@ -147,6 +147,9 @@ disable=print-statement,
         too-few-public-methods,
         too-many-locals,
         bad-super-call,  # since we still haven't drop python2 support
+        too-many-nested-blocks,
+        too-many-branches,
+        too-many-statements,
 
 # Enable the message, report, category or checker with the given id(s). You can
 # either give multiple identifier separated by comma (,) or put this option

+ 0 - 63
tools/ci/apply_bot_filter.py

@@ -1,63 +0,0 @@
-#!/usr/bin/env python
-
-# internal use only
-# called by CI jobs to determine if it need to be executed
-
-import json
-import os
-import re
-import sys
-
-RE_FILTER_PATTERN = re.compile(r'^r"(.+)?"$')
-
-RE_TYPE = type(re.compile('', 0))
-
-
-def parse_filter(filter_name):
-    filter_raw = os.getenv(filter_name)
-    filters = []
-    if filter_raw:
-        filter_data = json.loads(filter_raw)
-        for _filter in filter_data:
-            match = RE_FILTER_PATTERN.search(_filter)
-            if match:
-                filters.append(re.compile(match.group(1)))
-            else:
-                filters.append(_filter)
-    return filters
-
-
-def process_filter(execute_by_default, filter_name, ci_name):
-    execute = execute_by_default
-
-    # bot message is case insensitive (processed with lower case). so we also convert ci_name to lower case.
-    ci_name = ci_name.lower()
-
-    filter_list = parse_filter(filter_name)
-
-    for _filter in filter_list:
-        if isinstance(_filter, RE_TYPE):
-            match = _filter.search(ci_name) is not None
-        else:
-            match = _filter == ci_name
-
-        if match:
-            execute = True
-            break
-        else:
-            execute = False
-    return execute
-
-
-if __name__ == '__main__':
-    execute_by_default = True
-    if os.getenv('BOT_NEEDS_TRIGGER_BY_NAME', '0') == '1':
-        execute_by_default = False
-
-    need_to_execute = process_filter(True, 'BOT_STAGE_FILTER', os.getenv('CI_JOB_STAGE')) and process_filter(execute_by_default,
-                                                                                                             'BOT_JOB_FILTER', os.getenv('CI_JOB_NAME'))
-    if need_to_execute:
-        sys.exit(0)
-    else:
-        print("Skip this job as it doesn't fit @bot's filter")
-        sys.exit(-1)

+ 113 - 0
tools/ci/check_rules_yml.py

@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+#
+# Copyright 2021 Espressif Systems (Shanghai) CO LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Check if all rules in rules.yml used or not in CI yaml files.
+"""
+
+import argparse
+import os
+import sys
+from copy import deepcopy
+
+import yaml
+from idf_ci_utils import IDF_PATH
+
+ROOT_YML_FP = os.path.join(IDF_PATH, '.gitlab-ci.yml')
+
+
+def load_yaml(file_path):
+    return yaml.load(open(file_path), Loader=yaml.FullLoader)
+
+
+class YMLConfig:
+    def __init__(self, root_yml_file_path):
+        self._config = None
+        self._all_extends = None
+
+        self.root_yml = load_yaml(root_yml_file_path)
+        assert self.root_yml
+
+    @staticmethod
+    def _list(str_or_list):
+        if isinstance(str_or_list, str):
+            return [str_or_list]
+        if isinstance(str_or_list, list):
+            return str_or_list
+        raise ValueError('Wrong type: {}. Only supports str or list.'.format(type(str_or_list)))
+
+    @property
+    def config(self):
+        if self._config:
+            return self._config
+
+        all_config = dict()
+        for item in self.root_yml['include']:
+            if not item.endswith('rules.yml'):
+                all_config.update(load_yaml(os.path.join(IDF_PATH, item)))
+        self._config = all_config
+        return self._config
+
+    @property
+    def all_extends(self):
+        if self._all_extends:
+            return self._all_extends
+
+        res = set([])
+        for v in self.config.values():
+            if 'extends' in v:
+                for item in self._list(v['extends']):
+                    if item.startswith('.rules:'):
+                        res.add(item)
+        self._all_extends = res
+        return self._all_extends
+
+    def exists(self, key):
+        if key in self.all_extends:
+            return True
+        return False
+
+
+def validate(rules_yml):
+    yml_config = YMLConfig(ROOT_YML_FP)
+    res = 0
+    needed_rules = deepcopy(yml_config.all_extends)
+    with open(rules_yml) as fr:
+        for index, line in enumerate(fr):
+            if line.startswith('.rules:'):
+                key = line.strip().rsplit(':', 1)[0]
+                if not yml_config.exists(key):
+                    print('{}:{}:WARNING:rule "{}" unused'.format(rules_yml, index, key))
+                else:
+                    needed_rules.remove(key)
+
+    if needed_rules:
+        for item in needed_rules:
+            print('ERROR: missing rule: "{}"'.format(item))
+        res = 1
+
+    if res == 0:
+        print('Pass')
+    return res
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('rules_yml', nargs='?', default=os.path.join(IDF_PATH, '.gitlab', 'ci', 'rules.yml'),
+                        help='rules.yml file path')
+    args = parser.parse_args()
+
+    sys.exit(validate(args.rules_yml))

+ 94 - 0
tools/ci/check_tools_files_patterns.py

@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+#
+# Copyright 2021 Espressif Systems (Shanghai) CO LTD
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import fnmatch
+import glob
+import os
+import sys
+
+import yaml
+from idf_ci_utils import IDF_PATH, get_git_files, magic_check, magic_check_bytes, translate
+
+# Monkey patch starts
+# glob.glob will ignore all files starts with ``.``
+# don't ignore them here
+# need to keep the same argument as glob._ishidden
+
+
+def _ishidden(path):  # pylint: disable=W0613
+    return False
+
+
+fnmatch.translate = translate
+
+glob.magic_check = magic_check
+glob.magic_check_bytes = magic_check_bytes
+glob._ishidden = _ishidden  # pylint: disable=W0212
+# ends here
+
+
+def check(pattern_yml, exclude_list):
+    rules_dict = yaml.load(open(pattern_yml), Loader=yaml.FullLoader)
+    rules_patterns_set = set()
+    for k, v in rules_dict.items():
+        if k.startswith('.pattern') and isinstance(v, list):
+            rules_patterns_set.update(v)
+    rules_files_set = set()
+    for pat in rules_patterns_set:
+        rules_files_set.update(glob.glob(os.path.join(IDF_PATH, pat), recursive=True))
+
+    exclude_patterns_set = set()
+    exclude_patterns_set.update([path.split('#')[0].strip() for path in open(exclude_list).readlines() if path])
+    exclude_files_set = set()
+    for pat in exclude_patterns_set:
+        exclude_files_set.update(glob.glob(os.path.join(IDF_PATH, pat), recursive=True))
+
+    missing_files = set()
+    git_files = get_git_files(os.path.join(IDF_PATH, 'tools'), full_path=True)
+    for f in git_files:
+        if f in rules_files_set or f in exclude_files_set:
+            continue
+        missing_files.add(os.path.relpath(f, IDF_PATH))
+
+    return missing_files, rules_patterns_set.intersection(exclude_patterns_set)
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description='check if all tools files are in rules patterns or exclude list')
+    parser.add_argument('-c', '--pattern-yml',
+                        default=os.path.join(IDF_PATH, '.gitlab', 'ci', 'rules.yml'),
+                        help='yml file path included file patterns')
+    parser.add_argument('-e', '--exclude-list',
+                        default=os.path.join(IDF_PATH, 'tools', 'ci', 'exclude_check_tools_files.txt'),
+                        help='exclude list path')
+
+    args = parser.parse_args()
+
+    res = 0
+    not_included_files, dup_patterns = check(args.pattern_yml, args.exclude_list)
+    if not_included_files:
+        print('Missing Files: (please add to tools/ci/exclude_check_tools_files.txt')
+        for file in not_included_files:
+            print(file)
+        res = 1
+    if dup_patterns:
+        print('Duplicated Patterns: (please check .gitlab/ci/rules.yml and tools/ci/exclude_check_tools_files.txt')
+        for pattern in dup_patterns:
+            print(pattern)
+        res = 1
+
+    sys.exit(res)

+ 0 - 135
tools/ci/config/README.md

@@ -1,135 +0,0 @@
-# Rules for `rules.yml`
-
-- [Rules for `rules.yml`](#rules-for-rulesyml)
-  - [How to Develop With `rules.yml`?](#how-to-develop-with-rulesyml)
-    - [How to Add a New `Job`?](#how-to-add-a-new-job)
-    - [How to Add a New `Rules` Template?](#how-to-add-a-new-rules-template)
-    - [How to Add a New `if` Anchor?](#how-to-add-a-new-if-anchor)
-  - [Naming Rules](#naming-rules)
-    - [Common Naming Rules](#common-naming-rules)
-    - [`if` Anchors Naming Rules](#if-anchors-naming-rules)
-      - [Common Phrases/Abbreviations](#common-phrasesabbreviations)
-    - [`rules` Template Naming Rules](#rules-template-naming-rules)
-  - [Reusable Shell Script `tools/ci/utils.sh`](#reusable-shell-script-toolsciutilssh)
-    - [Functions](#functions)
-      - [CI Job Related](#ci-job-related)
-      - [Shell Script Related](#shell-script-related)
-
-## How to Develop With `rules.yml`?
-
-### How to Add a New `Job`?
-
-check if there's a suitable `.rules:<rules-you-need>` template
-
-1. if there is, put this in the job `extends`. All done, now you can close this window. (`extends` could be array or string)
-2. if there isn't
-   1. check [How to Add a New `Rules` Template?](#how-to-add-a-new-rules-template), create a suitable one
-   2. follow step 1
-
-### How to Add a New `Rules` Template?
-
-check if there's a suitable `.if-<if-anchor-you-need>` anchor
-
- 1. if there is, create a rule following [`rules` Template Naming Rules](#rules-template-naming-rules).For detail information, please refer to [GitLab Documentation `rules-if`](https://docs.gitlab.com/ee/ci/yaml/README.html#rulesif). Here's an example.
-
-```yaml
-.rules:dev:
-rules:
-  - <<: *if-trigger
-  - <<: *if-dev-push
-```
-
-2. if there isn't
-
-   1. check [How to Add a New `if` Anchor?](#how-to-add-a-new-if-anchor), create a suitable one
-   2. follow step 1
-
-### How to Add a New `if` Anchor?
-
-Create an `if` anchor following [`if` Anchors Naming Rules](#if-anchors-naming-rules). For detail information about how to write the condition clause, please refer to [GitLab Documentation `only/except (advanced)](https://docs.gitlab.com/ee/ci/yaml/README.html#onlyexcept-advanced). Here's an example.
-
-```yaml
-.if-schedule: &if-schedule:
-  if: '$CI_PIPELINE_SOURCE == "schedule"'
-```
-
-## Naming Rules
-
-### Common Naming Rules
-
-if a phrase has multi words, use `_` to concat them.
-
-> e.g. `regular_test`
-
-if a name have multi phrase, use `-` to concat them.
-
-> e.g. `regular_test-example_test`
-
-### `if` Anchors Naming Rules
-
-- if it's a label: `.if-label-<label_name>`
-- if it's a ref: `.if-ref-<ref_name>`
-- if it's a branch: `.if-branch-<branch_name>`
-- if it's a tag: `.if-tag-<tag_name>`
-- if it's a operating system: `.if-os-mac`
-- if it's multi-type combination: `.if-ref-<release_name>-branch-<branch_name>`
-
-#### Common Phrases/Abbreviations
-
-- `no_label`
-
-   `$BOT_TRIGGER_WITH_LABEL == null`
-
-- `protected`
-
-  `($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_BRANCH =~ /^release\/v/ || $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/)`
-
--  `target_test`
-
-   `example_test` or `custom_test` or `unit_test-all_targets`
-
-### `rules` Template Naming Rules
-
-- if it's os related: `.rules:os:<os_name>`
-- if it's tag related: `.rules:tag:<tag_1>-<tag_2>`
-- if it's label related: `.rules:labels:<label_1>-<label_2>`
-
-    By default, all `.rules:labels` should include both `if-label-regular_test` and `if-protected-no-label` implicitly, unless they have special postfixes:
-
-    - slim: `regular_test` not included
-    - only: only have the phrases listed
-
-- if it's target test related: `.rules:tests:<test_type_1>-<test_type_2>`
-
-    By default, all `.rules:tests` should include `if-protected-no_label` implicitly, unless they have special postfixes (same as above)
-
-- if it needs to build at first, then do some target test: `.rules:build_tests:<test_type_1>-<test_type_2>`
-
-    By default, all `.rules:build_tests` should include `if-protected-no-label`, `if-label-build`, `if-label-regular-test` implictly, unless they have special postfixes (same as above)
-
-- if a job supports all targets, use `-all_targets` as postfix
-
-## Reusable Shell Script `tools/ci/utils.sh`
-
-It is used to put all the reusable shell script as small functions. If you want to set `before_script: []` for you job, now you can set `extends: .before_script_slim` instead. it will only run `source tools/ci/utils.sh`
-
-If you're developing CI shell scripts, you can use these functions without source. They're already included in all `before_script`
-
-To run these commands in shell script locally, place `source tools/ci/utils.sh` at the very beginning.
-
-### Functions
-
-#### CI Job Related
-- `apply_bot_filter`
-- `add_gitlab_ssh_keys`
-- `add_github_ssh_keys`
-- `add_doc_server_ssh_keys`
-- `fetch_submodules`
-- `get_all_submodules`
-
-#### Shell Script Related
-- `error`: log in red color
-- `warning`: log in orange color
-- `info`: log in green color
-- `run_cmd`: run the command with duration seconds info
-- `retry_failed`: run the command with duration seconds info, retry when failed

+ 0 - 143
tools/ci/config/deploy.yml

@@ -1,143 +0,0 @@
-.deploy_job_template:
-  stage: deploy
-  image: $CI_DOCKER_REGISTRY/esp32-ci-env$BOT_DOCKER_IMAGE_TAG
-  tags:
-    - deploy
-
-.clang_tidy_deploy_template:
-  extends: .deploy_job_template
-  tags:
-    - deploy
-    - shiny
-  script:
-    - add_doc_server_ssh_keys $DOCS_DEPLOY_KEY $DOCS_SERVER $DOCS_SERVER_USER
-    - export GIT_VER=$(git describe --always)
-    - cd $IDF_PATH/examples/get-started/hello_world/tidybuild
-    - mv report $GIT_VER
-    - tar czvf $GIT_VER.tar.gz $GIT_VER
-    - export STATIC_REPORT_PATH="web/static_analysis/esp-idf/"
-    - ssh $DOCS_SERVER -x "mkdir -p $STATIC_REPORT_PATH/clang-tidy"
-    - scp $GIT_VER.tar.gz $DOCS_SERVER:$STATIC_REPORT_PATH/clang-tidy
-    - ssh $DOCS_SERVER -x "cd $STATIC_REPORT_PATH/clang-tidy && tar xzvf $GIT_VER.tar.gz && rm -f latest && ln -s $GIT_VER latest"
-    # add link to view the report
-    - echo "[static analysis][clang tidy] $CI_DOCKER_REGISTRY/static_analysis/esp-idf/clang-tidy/${GIT_VER}/index.html"
-    - test ! -e ${GIT_VER}/FAILED_RULES || { echo 'Failed static analysis rules!'; cat ${GIT_VER}/FAILED_RULES; exit 1; }
-
-clang_tidy_deploy:
-  extends: .clang_tidy_deploy_template
-  # Override default stage to happen before the post_check
-  stage: test_deploy
-  needs:
-    - clang_tidy_check
-    - clang_tidy_check_all
-  variables:
-    BOT_NEEDS_TRIGGER_BY_NAME: 1
-
-clang_tidy_deploy_regular:
-  extends:
-    - .clang_tidy_deploy_template
-    - .rules:labels:static_analysis-only
-  needs:
-    - clang_tidy_check_regular
-
-push_to_github:
-  extends:
-    - .deploy_job_template
-    - .before_script_lesser
-    - .rules:protected-no_label
-  dependencies: []
-  script:
-    - add_github_ssh_keys
-    - git remote remove github &>/dev/null || true
-    - git remote add github git@github.com:espressif/esp-idf.git
-    - tools/ci/push_to_github.sh
-
-.deploy_docs_template:
-  extends:
-    - .deploy_job_template
-    - .before_script_lesser
-  image: $ESP_IDF_DOC_ENV_IMAGE
-  tags:
-    - deploy
-    - shiny
-  variables:
-    DOCS_BUILD_DIR: "${IDF_PATH}/docs/_build/"
-    PYTHONUNBUFFERED: 1
-  script:
-    - add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
-    - export GIT_VER=$(git describe --always)
-    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 ${IDF_PATH}/tools/ci/deploy_docs.py
-
-# deploys docs to CI_DOCKER_REGISTRY webserver, for internal review
-deploy_docs_preview:
-  extends:
-    - .deploy_docs_template
-    - .rules:labels:build_docs-preview
-  # Override default stage to happen before the post_check
-  stage: test_deploy
-  needs:
-    - build_docs_html
-    - build_docs_pdf
-  variables:
-    TYPE: "preview"
-    # older branches use DOCS_DEPLOY_KEY, DOCS_SERVER, DOCS_SERVER_USER, DOCS_PATH for preview server so we keep these names for 'preview'
-    DOCS_DEPLOY_PRIVATEKEY: "$DOCS_DEPLOY_KEY"
-    DOCS_DEPLOY_SERVER: "$DOCS_SERVER"
-    DOCS_DEPLOY_SERVER_USER: "$DOCS_SERVER_USER"
-    DOCS_DEPLOY_PATH: "$DOCS_PATH"
-    DOCS_DEPLOY_URL_BASE: "https://$CI_DOCKER_REGISTRY/docs/esp-idf"
-
-# deploy docs to production webserver
-deploy_docs_production:
-  # The DOCS_PROD_* variables used by this job are "Protected" so these branches must all be marked "Protected" in Gitlab settings
-  extends:
-    - .deploy_docs_template
-    - .rules:protected-no_label
-  stage: post_deploy
-  needs: # ensure runs after push_to_github succeeded
-    - build_docs_html
-    - build_docs_pdf
-    - push_to_github
-  variables:
-    TYPE: "preview"
-    DOCS_DEPLOY_PRIVATEKEY: "$DOCS_PROD_DEPLOY_KEY"
-    DOCS_DEPLOY_SERVER: "$DOCS_PROD_SERVER"
-    DOCS_DEPLOY_SERVER_USER: "$DOCS_PROD_SERVER_USER"
-    DOCS_DEPLOY_PATH: "$DOCS_PROD_PATH"
-    DOCS_DEPLOY_URL_BASE: "https://docs.espressif.com/projects/esp-idf"
-
-deploy_test_result:
-  extends:
-    - .deploy_job_template
-    - .before_script_slim
-    - .rules:ref:master-schedule-always
-  image: $CI_DOCKER_REGISTRY/bot-env
-  tags:
-    - deploy_test
-  artifacts:
-    when: always
-    paths:
-      - ${CI_PROJECT_DIR}/test-management/*.log
-      # save all test logs as artifacts, make it easier to track errors
-      - ${CI_PROJECT_DIR}/TEST_LOGS
-      - $CI_PROJECT_DIR/$CI_COMMIT_SHA
-    expire_in: 1 mos
-  variables:
-    UNIT_TEST_CASE_FILE: "${CI_PROJECT_DIR}/components/idf_test/unit_test/TestCaseAll.yml"
-    BOT_ACCOUNT_CONFIG_FILE: "${CI_PROJECT_DIR}/test-management/Config/Account.local.yml"
-    TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
-    AUTO_TEST_SCRIPT_PATH: "${CI_PROJECT_DIR}/auto_test_script"
-  script:
-    - add_gitlab_ssh_keys
-    - export GIT_SHA=$(echo ${CI_COMMIT_SHA} | cut -c 1-8)
-    - export REV_COUNT=$(git rev-list --count HEAD)
-    - export SUMMARY="IDF CI test result for $GIT_SHA (r${REV_COUNT})"
-    # artifacts of job update_test_cases creates test-management folder
-    # we need to remove it so we can clone test-management folder again
-    - rm -rf test-management
-    - retry_failed git clone $TEST_MANAGEMENT_REPO
-    - python3 $CHECKOUT_REF_SCRIPT test-management test-management
-    - cd test-management
-    - echo $BOT_JIRA_ACCOUNT > ${BOT_ACCOUNT_CONFIG_FILE}
-    # update test results
-    - python3 ImportTestResult.py -r "$GIT_SHA (r${REV_COUNT})" -j $JIRA_TEST_MANAGEMENT_PROJECT -s "$SUMMARY" -l CI -p ${CI_PROJECT_DIR}/TEST_LOGS ${CI_PROJECT_DIR}/${CI_COMMIT_SHA} --pipeline_url ${CI_PIPELINE_URL}

+ 0 - 73
tools/ci/config/post_check.yml

@@ -1,73 +0,0 @@
-# copy from .gitlab-ci.yml as anchor is not global
-.show_submodule_urls: &show_submodule_urls |
-  git config --get-regexp '^submodule\..*\.url$' || true
-
-.post_check_base_template:
-  stage: post_check
-  image: $CI_DOCKER_REGISTRY/esp32-ci-env$BOT_DOCKER_IMAGE_TAG
-  tags:
-    - host_test
-  dependencies: []
-
-.post_check_job_template:
-  extends:
-    - .post_check_base_template
-    - .before_script_lesser_nofilter
-
-.post_check_job_template_with_filter:
-  extends:
-    - .post_check_base_template
-    - .before_script_lesser
-
-check_submodule_sync:
-  extends:
-    - .before_script_slim
-    - .post_check_job_template
-  tags:
-    - github_sync
-  retry: 2
-  variables:
-    GIT_STRATEGY: clone
-    SUBMODULES_TO_FETCH: "none"
-    PUBLIC_IDF_URL: "https://github.com/espressif/esp-idf.git"
-  script:
-    - git submodule deinit --force .
-    # setting the default remote URL to the public one, to resolve relative location URLs
-    - git config remote.origin.url ${PUBLIC_IDF_URL}
-    # check if all submodules are correctly synced to public repository
-    - git submodule init
-    - *show_submodule_urls
-    - git submodule update --recursive
-    - echo "IDF was cloned from ${PUBLIC_IDF_URL} completely"
-
-check_ut_cmake_make:
-  extends:
-    - .post_check_job_template_with_filter
-    - .rules:dev
-  tags:
-    - build
-  script:
-    - tools/ci/check_ut_cmake_make.sh
-
-check_artifacts_expire_time:
-  extends: .post_check_job_template
-  script:
-    # check if we have set expire time for all artifacts
-    - python tools/ci/check_artifacts_expire_time.py
-
-check_pipeline_triggered_by_label:
-  extends:
-    - .post_check_job_template
-    - .rules:dev
-  script:
-    # If the pipeline is triggered with label, the pipeline will only succeeded if "regular_test" label is added.
-    # We want to make sure some jobs are always executed to detect regression.
-    - test "$BOT_LABEL_REGULAR_TEST" = "true" || { echo "CI can only pass if 'regular_test' label is included"; exit -1; }
-
-check_commit_msg:
-  extends: .post_check_job_template
-  script:
-    - git status
-    - git log -n10 --oneline
-    # commit start with "WIP: " need to be squashed before merge
-    - 'git log --pretty=%s master.. -- | grep "^WIP: " && exit 1 || exit 0'

+ 0 - 45
tools/ci/config/post_deploy.yml

@@ -1,45 +0,0 @@
-.check_doc_links_template:
-  extends: .rules:protected-no_label
-  stage: post_deploy
-  image: $ESP_IDF_DOC_ENV_IMAGE
-  tags: [ "build", "amd64", "internet" ]
-  needs: # ensure runs after push_to_github succeeded
-    - build_docs_html
-    - build_docs_pdf
-    - push_to_github
-  artifacts:
-    when: always
-    paths:
-      - docs/_build/*/*/*.txt
-      - docs/_build/*/*/linkcheck/*.txt
-    expire_in: 1 week
-  allow_failure: true
-  dependencies: []
-  script:
-    - cd docs
-    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 pip install -r requirements.txt
-    - ${IDF_PATH}/tools/ci/multirun_with_pyenv.sh -p 3.6.10 ./build_docs.py -l $DOCLANG -t $DOCTGT linkcheck
-
-check_doc_links_en_esp32:
-  extends: .check_doc_links_template
-  variables:
-    DOCLANG: "en"
-    DOCTGT: "esp32"
-
-check_doc_links_en_esp32s2:
-  extends: .check_doc_links_template
-  variables:
-    DOCLANG: "en"
-    DOCTGT: "esp32s2"
-
-check_doc_links_zh_CN_esp32:
-  extends: .check_doc_links_template
-  variables:
-    DOCLANG: "zh_CN"
-    DOCTGT: "esp32"
-
-check_doc_links_zh_CN_esp32s2:
-  extends: .check_doc_links_template
-  variables:
-    DOCLANG: "zh_CN"
-    DOCTGT: "esp32s2"

+ 0 - 273
tools/ci/config/rules.yml

@@ -1,273 +0,0 @@
-# if anchors
-.if-ref-master: &if-ref-master
-  if: '$CI_COMMIT_REF_NAME == "master"'
-
-.if-tag-release-no_label: &if-tag-release-no_label
-  if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/ && $BOT_TRIGGER_WITH_LABEL == null'
-
-.if-protected: &if-protected
-  if: '($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_BRANCH =~ /^release\/v/ || $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/)'
-
-.if-protected-no_label: &if-protected-no_label
-  if: '($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_BRANCH =~ /^release\/v/ || $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/) && $BOT_TRIGGER_WITH_LABEL == null'
-
-.if-dev-push: &if-dev-push
-  if: '$CI_COMMIT_REF_NAME != "master" && $CI_COMMIT_BRANCH !~ /^release\/v/ && $CI_COMMIT_TAG !~ /^v\d+\.\d+(\.\d+)?($|-)/ && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event")'
-
-.if-schedule: &if-schedule
-  if: '$CI_PIPELINE_SOURCE == "schedule"'
-
-.if-trigger: &if-trigger
-  if: '$CI_PIPELINE_SOURCE == "trigger"'
-
-.if-label-regular_test: &if-label-regular_test
-  if: '$BOT_LABEL_REGULAR_TEST'
-
-.if-label-build: &if-label-build
-  if: '$BOT_LABEL_BUILD'
-
-.if-label-build_docs: &if-label-build_docs
-  if: '$BOT_LABEL_BUILD_DOCS'
-
-.if-label-integration_test: &if-label-integration_test
-  if: '$BOT_LABEL_INTEGRATION_TEST'
-
-.if-label-unit_test: &if-label-unit_test
-  if: '$BOT_LABEL_UNIT_TEST'
-
-.if-label-unit_test-32: &if-label-unit_test-32
-  if: '$BOT_LABEL_UNIT_TEST_32'
-
-.if-label-unit_test-s2: &if-label-unit_test-s2
-  if: '$BOT_LABEL_UNIT_TEST_S2'
-
-.if-label-unit_test-c3: &if-label-unit_test-c3
-  if: '$BOT_LABEL_UNIT_TEST_C3'
-
-.if-label-unit_test-all_labels: &if-label-unit_test-all_labels
-  if: '$BOT_LABEL_UNIT_TEST || $BOT_LABEL_UNIT_TEST_32 || $BOT_LABEL_UNIT_TEST_S2 || $BOT_LABEL_UNIT_TEST_C3'
-
-.if-label-weekend_test: &if-label-weekend_test
-  if: '$BOT_LABEL_WEEKEND_TEST'
-
-.if-label-example_test: &if-label-example_test
-  if: '$BOT_LABEL_EXAMPLE_TEST'
-
-.if-label-custom_test: &if-label-custom_test
-  if: '$BOT_LABEL_CUSTOM_TEST'
-
-.if-label-host_test: &if-label-host_test
-  if: '$BOT_LABEL_HOST_TEST'
-
-.if-label-fuzzer_test: &if-label-fuzzer_test
-  if: '$BOT_LABEL_FUZZER_TEST'
-
-.if-label-nvs_coverage: &if-label-nvs_coverage
-  if: '$BOT_LABEL_NVS_COVERAGE'
-
-.if-label-static_analysis: &if-label-static_analysis
-  if: '$BOT_LABEL_STATIC_ANALYSIS || $BOT_LABEL_STATIC_ANALYSIS_ALL'
-
-.if-label-iperf_stress_test: &if-label-iperf_stress_test
-  if: '$BOT_LABEL_IPERF_STRESS_TEST'
-
-.if-os-mac: &if-os-mac
-  if: '$BOT_LABEL_MACOS_TEST'
-
-# Rules templates
-.rules:protected:
-  rules:
-    - <<: *if-protected
-
-.rules:protected-no_label:
-  rules:
-    - <<: *if-protected-no_label
-
-.rules:protected-schedule:
-  rules:
-    - <<: *if-protected
-    - <<: *if-schedule
-
-.rules:trigger:
-  rules:
-    - <<: *if-trigger
-
-.rules:dev:
-  rules:
-    - <<: *if-trigger
-    - <<: *if-dev-push
-
-.rules:os:mac_os:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-os-mac
-
-.rules:tag:release-no_label:
-  rules:
-    - <<: *if-tag-release-no_label
-
-.rules:ref:master-schedule:
-  rules:
-    - <<: *if-ref-master
-    - <<: *if-schedule
-
-.rules:ref:master-schedule-always:
-  rules:
-    - <<: *if-ref-master
-      when: always
-    - <<: *if-schedule
-      when: always
-
-.rules:labels:static_analysis-only:
-  rules:
-    - <<: *if-label-static_analysis
-
-.rules:labels:build:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-regular_test
-    - <<: *if-label-build
-
-.rules:labels:build_docs:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-build
-    - <<: *if-label-regular_test
-    - <<: *if-label-build_docs
-
-.rules:labels:build_docs-slim:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-build_docs
-
-.rules:labels:build_docs-preview:
-  rules:
-    - <<: *if-label-build_docs
-
-.rules:labels:weekend_test-only:
-  rules:
-    - <<: *if-label-weekend_test
-
-.rules:labels:iperf_stress_test-only:
-  rules:
-    - <<: *if-label-iperf_stress_test
-
-.rules:labels:fuzzer_test-weekend_test-only:
-  rules:
-    - <<: *if-label-fuzzer_test
-    - <<: *if-label-weekend_test
-
-.rules:labels:nvs_coverage-only:
-  rules:
-    - <<: *if-label-nvs_coverage
-
-.rules:labels:host_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-regular_test
-    - <<: *if-label-host_test
-
-.rules:tests:example_test-schedule:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-example_test
-    - <<: *if-schedule
-
-.rules:tests:custom_test-schedule:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-custom_test
-    - <<: *if-schedule
-
-.rules:tests:unit_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-unit_test-all_labels
-
-.rules:tests:unit_test_32:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-unit_test
-    - <<: *if-label-unit_test-32
-
-.rules:tests:unit_test_s2:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-unit_test
-    - <<: *if-label-unit_test-s2
-
-.rules:tests:unit_test_c3:
-  rules:
-    - <<: *if-label-unit_test
-    - <<: *if-label-unit_test-c3
-
-.rules:tests:integration_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-integration_test
-
-.rules:assign_test:target_test-integration_test-weekend_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-regular_test
-    - <<: *if-label-example_test
-    - <<: *if-label-custom_test
-    - <<: *if-label-unit_test-all_labels
-    - <<: *if-label-integration_test
-    - <<: *if-label-weekend_test
-
-.rules:build_tests:integration_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-build
-    - <<: *if-label-regular_test
-    - <<: *if-label-integration_test
-
-.rules:build_tests:weekend_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-build
-    - <<: *if-label-regular_test
-    - <<: *if-label-weekend_test
-
-.rules:build_tests:unit_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-build
-    - <<: *if-label-regular_test
-    - <<: *if-label-unit_test-all_labels
-
-.rules:build_tests:example_test-weekend_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-build
-    - <<: *if-label-regular_test
-    - <<: *if-label-example_test
-    - <<: *if-label-weekend_test
-
-.rules:build_tests:custom_test-weekend_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-build
-    - <<: *if-label-regular_test
-    - <<: *if-label-custom_test
-    - <<: *if-label-weekend_test
-
-.rules:build_tests:target_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-build
-    - <<: *if-label-regular_test
-    - <<: *if-label-example_test
-    - <<: *if-label-custom_test
-    - <<: *if-label-unit_test-all_labels
-
-.rules:build_tests:target_test-weekend_test:
-  rules:
-    - <<: *if-protected-no_label
-    - <<: *if-label-build
-    - <<: *if-label-regular_test
-    - <<: *if-label-example_test
-    - <<: *if-label-custom_test
-    - <<: *if-label-unit_test-all_labels
-    - <<: *if-label-weekend_test

+ 32 - 0
tools/ci/exclude_check_tools_files.txt

@@ -0,0 +1,32 @@
+tools/ble/**/*
+tools/catch/**/*
+tools/ci/build_template_app.sh
+tools/ci/check_*.{py,txt,sh}  # excluded because run in default pipeline pre-check stage
+tools/ci/checkout_project_ref.py
+tools/ci/ci_fetch_submodule.py
+tools/ci/ci_get_mr_info.py
+tools/ci/configure_ci_environment.sh
+tools/ci/deploy_docs.py
+tools/ci/envsubst.py
+tools/ci/*exclude*.txt
+tools/ci/executable-list.txt
+tools/ci/fix_empty_prototypes.sh
+tools/ci/get-full-sources.sh
+tools/ci/idf_ci_utils.py
+tools/ci/mirror-submodule-update.sh
+tools/ci/multirun_with_pyenv.sh
+tools/ci/normalize_clangtidy_path.py
+tools/ci/push_to_github.sh
+tools/ci/python_packages/wifi_tools.py
+tools/ci/setup_python.sh
+tools/ci/utils.sh
+tools/eclipse-code-style.xml
+tools/format-minimal.sh
+tools/format.sh
+tools/gen_esp_err_to_name.py
+tools/kconfig/**/*
+tools/set-submodules-to-github.sh
+tools/templates/sample_component/CMakeLists.txt
+tools/templates/sample_component/include/main.h
+tools/templates/sample_component/main.c
+tools/toolchain_versions.mk

+ 3 - 1
tools/ci/executable-list.txt

@@ -1,3 +1,4 @@
+.gitlab/ci/dependencies/generate_rules.py
 components/app_update/otatool.py
 components/efuse/efuse_table_gen.py
 components/efuse/test_efuse_host/efuse_tests.py
@@ -33,7 +34,6 @@ install.fish
 install.sh
 tools/build_apps.py
 tools/check_python_dependencies.py
-tools/ci/apply_bot_filter.py
 tools/ci/build_template_app.sh
 tools/ci/check_build_warnings.py
 tools/ci/check_callgraph.py
@@ -46,6 +46,8 @@ tools/ci/check_idf_version.sh
 tools/ci/check_kconfigs.py
 tools/ci/check_readme_links.py
 tools/ci/check_rom_apis.sh
+tools/ci/check_rules_yml.py
+tools/ci/check_tools_files_patterns.py
 tools/ci/check_ut_cmake_make.sh
 tools/ci/checkout_project_ref.py
 tools/ci/deploy_docs.py

+ 133 - 5
tools/ci/idf_ci_utils.py

@@ -15,13 +15,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
+import functools
 import logging
 import os
+import re
 import subprocess
 import sys
 
-IDF_PATH = os.getenv('IDF_PATH', os.path.join(os.path.dirname(__file__), '..', '..'))
+IDF_PATH = os.path.abspath(os.getenv('IDF_PATH', os.path.join(os.path.dirname(__file__), '..', '..')))
 
 
 def get_submodule_dirs(full_path=False):  # type: (bool) -> list
@@ -41,7 +42,7 @@ def get_submodule_dirs(full_path=False):  # type: (bool) -> list
                 dirs.append(os.path.join(IDF_PATH, path))
             else:
                 dirs.append(path)
-    except Exception as e:
+    except Exception as e:  # pylint: disable=W0703
         logging.warning(str(e))
 
     return dirs
@@ -67,5 +68,132 @@ def is_executable(full_path):  # type: (str) -> bool
     """
     if sys.platform == 'win32':
         return _check_git_filemode(full_path)
-    else:
-        return os.access(full_path, os.X_OK)
+    return os.access(full_path, os.X_OK)
+
+
+def get_git_files(path=IDF_PATH, full_path=False):  # type: (str, bool) -> list[str]
+    """
+    Get the result of git ls-files
+    :param path: path to run git ls-files
+    :param full_path: return full path if set to True
+    :return: list of file paths
+    """
+    try:
+        files = subprocess.check_output(['git', 'ls-files'], cwd=path).decode('utf8').strip().split('\n')
+    except Exception as e:  # pylint: disable=W0703
+        logging.warning(str(e))
+        files = []
+    return [os.path.join(path, f) for f in files] if full_path else files
+
+
+# this function is a commit from
+# https://github.com/python/cpython/pull/6299/commits/bfd63120c18bd055defb338c075550f975e3bec1
+# In order to solve python https://bugs.python.org/issue9584
+# glob pattern does not support brace expansion issue
+def _translate(pat):
+    """Translate a shell PATTERN to a regular expression.
+    There is no way to quote meta-characters.
+    """
+    i, n = 0, len(pat)
+    res = ''
+    while i < n:
+        c = pat[i]
+        i = i + 1
+        if c == '*':
+            res = res + '.*'
+        elif c == '?':
+            res = res + '.'
+        elif c == '[':
+            j = i
+            if j < n and pat[j] == '!':
+                j = j + 1
+            if j < n and pat[j] == ']':
+                j = j + 1
+            while j < n and pat[j] != ']':
+                j = j + 1
+            if j >= n:
+                res = res + '\\['
+            else:
+                stuff = pat[i:j]
+                if '--' not in stuff:
+                    stuff = stuff.replace('\\', r'\\')
+                else:
+                    chunks = []
+                    k = i + 2 if pat[i] == '!' else i + 1
+                    while True:
+                        k = pat.find('-', k, j)
+                        if k < 0:
+                            break
+                        chunks.append(pat[i:k])
+                        i = k + 1
+                        k = k + 3
+                    chunks.append(pat[i:j])
+                    # Escape backslashes and hyphens for set difference (--).
+                    # Hyphens that create ranges shouldn't be escaped.
+                    stuff = '-'.join(s.replace('\\', r'\\').replace('-', r'\-')
+                                     for s in chunks)
+                # Escape set operations (&&, ~~ and ||).
+                stuff = re.sub(r'([&~|])', r'\\\1', stuff)
+                i = j + 1
+                if stuff[0] == '!':
+                    stuff = '^' + stuff[1:]
+                elif stuff[0] in ('^', '['):
+                    stuff = '\\' + stuff
+                res = '%s[%s]' % (res, stuff)
+        elif c == '{':
+            # Handling of brace expression: '{PATTERN,PATTERN,...}'
+            j = 1
+            while j < n and pat[j] != '}':
+                j = j + 1
+            if j >= n:
+                res = res + '\\{'
+            else:
+                stuff = pat[i:j]
+                i = j + 1
+
+                # Find indices of ',' in pattern excluding r'\,'.
+                # E.g. for r'a\,a,b\b,c' it will be [4, 8]
+                indices = [m.end() for m in re.finditer(r'[^\\],', stuff)]
+
+                # Splitting pattern string based on ',' character.
+                # Also '\,' is translated to ','. E.g. for r'a\,a,b\b,c':
+                # * first_part = 'a,a'
+                # * last_part = 'c'
+                # * middle_part = ['b,b']
+                first_part = stuff[:indices[0] - 1].replace(r'\,', ',')
+                last_part = stuff[indices[-1]:].replace(r'\,', ',')
+                middle_parts = [
+                    stuff[st:en - 1].replace(r'\,', ',')
+                    for st, en in zip(indices, indices[1:])
+                ]
+
+                # creating the regex from splitted pattern. Each part is
+                # recursivelly evaluated.
+                expanded = functools.reduce(
+                    lambda a, b: '|'.join((a, b)),
+                    (_translate(elem) for elem in [first_part] + middle_parts + [last_part])
+                )
+                res = '%s(%s)' % (res, expanded)
+        else:
+            res = res + re.escape(c)
+    return res
+
+
+def translate(pat):
+    res = _translate(pat)
+    return r'(?s:%s)\Z' % res
+
+
+magic_check = re.compile('([*?[{])')
+magic_check_bytes = re.compile(b'([*?[{])')
+# cpython github PR 6299 ends here
+
+# Here's the code block we're going to use to monkey patch ``glob`` module and ``fnmatch`` modules
+# DO NOT monkey patch here, only patch where you really needs
+#
+# import glob
+# import fnmatch
+# from idf_ci_utils import magic_check, magic_check_bytes, translate
+# glob.magic_check = magic_check
+# glob.magic_check_bytes = magic_check_bytes
+# fnmatch.translate = translate

+ 6 - 1
tools/ci/normalize_clangtidy_path.py

@@ -1,12 +1,17 @@
 #!/usr/bin/env python
 import argparse
 import re
-from os.path import dirname, join, normpath, relpath
+from os.path import dirname, exists, join, normpath, relpath
 
 CLANG_TIDY_REGEX = re.compile(r'(.+|[a-zA-Z]:\\\\.+):([0-9]+):([0-9]+): ([^:]+): (.+)')
 
 
 def normalize_clang_tidy_path(file_path, output_path, base_dir):
+    if not exists(file_path):
+        print('Skipping normalizing. This could only happen when skipping clang-tidy check '
+              'because of no c file modified. Please double check')
+        return
+
     with open(output_path, 'w') as fw:
         for line in open(file_path):
             result = CLANG_TIDY_REGEX.match(line)

+ 0 - 5
tools/ci/utils.sh

@@ -1,10 +1,5 @@
 # Modified from https://gitlab.com/gitlab-org/gitlab/-/blob/master/scripts/utils.sh
 
-# before each job, we need to check if this job is filtered by bot stage/job filter
-function apply_bot_filter() {
-  python "${IDF_PATH}"/tools/ci/apply_bot_filter.py || exit 0
-}
-
 function add_ssh_keys() {
   local key_string="${1}"
   mkdir -p ~/.ssh

+ 1 - 1
tools/unit-test-app/configs/fatfs_fast_seek

@@ -1,3 +1,3 @@
 TEST_COMPONENTS=fatfs
 CONFIG_FATFS_USE_FASTSEEK=y
-CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE=64
+CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE=64