From 68bfe015e6ab9ec2ab8f1657066898b7880eaf63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 20 Aug 2026 14:42:28 +0000 Subject: [PATCH] ci: build newly added variants in the PR matrix (#11549) * ci: build newly added variants in the PR matrix A new board declares board_level = release, so it gets no CI build until after merge. Build the first env of each platformio.ini added by a PR, regardless of board_level. Only added files qualify; adding an env to an existing config does not. * ci: also detect added variants in merge_group runs merge_group uses the same --level pr subset as pull_request, so a newly added variant was skipped there. Derive the diff base from github.event.merge_group.base_sha for those runs. * ci: fail the matrix step when the variant diff errors Process substitution hides the exit status, so a failed diff silently yielded an empty list and dropped the new board from the matrix. Capture into a variable so 'set -e' aborts the step instead. --- .github/workflows/main_matrix.yml | 27 ++++++++++++++++++++++++++- bin/generate_ci_matrix.py | 22 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 91eb2690e..59cc44c8c 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -55,6 +55,9 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v7 + with: + # Needed to diff against the base branch for newly added variants. + fetch-depth: 0 - uses: actions/setup-python@v6 with: python-version: 3.x @@ -62,11 +65,33 @@ jobs: - run: pip install -U platformio - name: Generate matrix id: jsonStep + env: + BASE_REF: ${{ github.base_ref }} + MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }} run: | + # A new board is 'release' and gets no CI until after merge, so force-build the + # first env of each ADDED variant config. A new env in an existing one does not count. + DIFF_BASE="" + if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then + git fetch --no-tags --depth=1 origin "$BASE_REF" + DIFF_BASE=$(git merge-base FETCH_HEAD HEAD) + elif [[ "$GITHUB_EVENT_NAME" == "merge_group" ]]; then + DIFF_BASE="$MERGE_GROUP_BASE_SHA" + fi + ADDED_ARGS=() + if [[ -n "$DIFF_BASE" ]]; then + # Assign rather than pipe: a failing diff must abort the step under 'set -e', + # not silently yield an empty list and drop the new board from the matrix. + ADDED_CONFIGS=$(git diff --name-only --diff-filter=A \ + "$DIFF_BASE" HEAD -- 'variants/**/platformio.ini') + while IFS= read -r cfg; do + [[ -n "$cfg" ]] && ADDED_ARGS+=(--added-config "$cfg") + done <<<"$ADDED_CONFIGS" + fi # PRs and (for now) merge_group builds use the narrowed --level pr board # subset. Full-matrix builds run on push / schedule / workflow_dispatch. if [[ "$GITHUB_EVENT_NAME" == "pull_request" || "$GITHUB_EVENT_NAME" == "merge_group" ]]; then - TARGETS=$(./bin/generate_ci_matrix.py all --level pr) + TARGETS=$(./bin/generate_ci_matrix.py all --level pr "${ADDED_ARGS[@]}") else TARGETS=$(./bin/generate_ci_matrix.py all) fi diff --git a/bin/generate_ci_matrix.py b/bin/generate_ci_matrix.py index c3235c279..02155b59a 100755 --- a/bin/generate_ci_matrix.py +++ b/bin/generate_ci_matrix.py @@ -23,10 +23,29 @@ parser.add_argument( default=[], help="Board level to build for (omit for the 'pr' + 'release' matrix)", ) +parser.add_argument( + "--added-config", + action="append", + default=[], + metavar="PATH", + help="platformio.ini added by this PR; its first env is built regardless of board_level", +) args = parser.parse_args() outlist = [] +# A brand-new board is normally 'release', so it would get no CI until after merge. +# Build the first env of each newly added config so it is compiled at least once. +forced_envs = set() +for added_path in args.added_config: + try: + with open(added_path, encoding="utf-8") as added_file: + first_env = re.search(r"^[ \t]*\[env:([^\]]+)\]", added_file.read(), re.MULTILINE) + except OSError: + continue + if first_env: + forced_envs.add(first_env.group(1).strip()) + cfg = ProjectConfig.get_instance() pio_envs = cfg.envs() @@ -69,6 +88,9 @@ for env in all_envs: # Always include board_level = 'pr' if env["board_level"] == "pr": outlist.append(env["ci"]) + # Include the first env of a platformio.ini added by this PR + elif env["ci"]["board"] in forced_envs: + outlist.append(env["ci"]) # Include board_level = 'extra' when requested elif "extra" in args.level and env["board_level"] == "extra": outlist.append(env["ci"])