From b56006b17e97f04f1fd4c1a7fde24ab6d4df2eb4 Mon Sep 17 00:00:00 2001 From: Liu Haoyu Date: Tue, 28 Jul 2026 11:50:08 +0800 Subject: [PATCH 1/7] [CI] Refactor CI trigger based on PR labels - Add new action 'should-skip-ci-by-labels' to determine CI execution - Update mthreads workflow to use the new label-based action Signed-off-by: Liu Haoyu --- .../should-skip-ci-by-labels/action.yml | 91 +++++++++++++++++++ .github/new-prs-labeler.yml | 48 +++++----- .../workflows/mthreads3.6-build-and-test.yml | 16 +++- 3 files changed, 127 insertions(+), 28 deletions(-) create mode 100644 .github/actions/should-skip-ci-by-labels/action.yml diff --git a/.github/actions/should-skip-ci-by-labels/action.yml b/.github/actions/should-skip-ci-by-labels/action.yml new file mode 100644 index 000000000..37db87557 --- /dev/null +++ b/.github/actions/should-skip-ci-by-labels/action.yml @@ -0,0 +1,91 @@ +# Copyright 2025- FlagOS Contributors +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +name: 'Should Skip CI By Labels' +description: 'Determine whether CI should run based on the labels applied to the PR' + +inputs: + backend: + description: 'Backend name matching the PR label (e.g. mthreads, nvidia)' + required: true + +outputs: + should_skip: + description: 'true if the PR only contains doc changes or lacks relevant labels, false if CI should run' + value: ${{ steps.check.outputs.should_skip }} + +runs: + using: 'composite' + steps: + - name: Check PR labels + id: check + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + BACKEND="${{ inputs.backend }}" + EVENT_NAME="${{ github.event_name }}" + + echo "🔍 Checking PR labels for backend: $BACKEND" + + # 1. Handle non-PR events (e.g., direct push to the main branch) + # Direct pushes do not have PR labels. For safety, default to NOT skipping (i.e., run CI). + if [ "$EVENT_NAME" != "pull_request" ] && [ "$EVENT_NAME" != "pull_request_target" ]; then + echo "⚠️ Event is '$EVENT_NAME' (not a PR). Defaulting to run CI." + echo "should_skip=false" >> $GITHUB_OUTPUT + exit 0 + fi + + # 2. Fetch all labels for the current PR + PR_NUMBER="${{ github.event.pull_request.number }}" + echo "Fetching labels for PR #$PR_NUMBER..." + + # Use the 'gh' command to fetch labels and parse them into a plain text list using 'jq' + LABELS=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name') + + echo "Current PR labels:" + echo "$LABELS" + + # 3. Evaluation logic with short-circuit evaluation + SHOULD_SKIP=false + + # Check 1: If 'DOC' label exists, it means ALL changed files are docs (and no CMakeLists.txt). + # We can safely skip CI and exit early. + if echo "$LABELS" | grep -qx "DOC"; then + echo "🛑 Found 'DOC' label. All changes are documentation. Will safely skip CI." + SHOULD_SKIP=true + # Check 2: If no 'DOC' label, check for the specific backend label (e.g., 'mthreads'). + # If found, we need to run CI. No need to check for 'CORE'. + elif echo "$LABELS" | grep -qx "$BACKEND"; then + echo "✅ Found backend label '$BACKEND'. Will run CI." + SHOULD_SKIP=false + # Check 3: If no backend label, check for the 'CORE' label (core code changes). + # If found, we need to run CI for all backends. + elif echo "$LABELS" | grep -qx "CORE"; then + echo "✅ Found 'CORE' label. Core files changed, will run CI." + SHOULD_SKIP=false + # Check 4: No relevant labels found, skip CI. + else + echo "🛑 No relevant labels (DOC, $BACKEND, or CORE) found. Will safely skip CI." + SHOULD_SKIP=true + fi + + # Output the final result + echo "should_skip=$SHOULD_SKIP" >> $GITHUB_OUTPUT \ No newline at end of file diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml index c47a82d7b..06aa9dca5 100644 --- a/.github/new-prs-labeler.yml +++ b/.github/new-prs-labeler.yml @@ -19,7 +19,7 @@ # SOFTWARE. # ========================================== -# 1. Core File Labels (CORE) +# Core File Labels (CORE) # Includes core framework code and specific # third-party core libraries. # ========================================== @@ -47,7 +47,7 @@ CORE: - '.github/workflows/code-format-check.yml' # ========================================== -# 2. CI/CD Labels +# CI/CD Labels # Applied if any file under .github/workflows # or .github/actions is modified. # ========================================== @@ -63,13 +63,13 @@ CI/CD: - 'packaging/**' # ========================================== -# 3. Documentation Labels (DOC) -# Applied if the PR contains modifications to -# any of the following file types. +# Doc Only Label +# Rule: Applied only if ALL changed files +# are documentation files, EXCLUDING CMakeLists.txt. # ========================================== DOC: - changed-files: - - any-glob-to-any-file: + - any-glob-to-all-files: - '**/*.md' - '**/*.txt' - '**/*.rst' @@ -79,9 +79,26 @@ DOC: - '**/*.gif' - '**/*.ico' - 'LICENSE' + - all-globs-to-all-files: + - '!**/CMakeLists.txt' + +# ==================================================================================== +# TLE Labels +# Rule: Applied if any file path contains 'tle', 'TLE', or 'Tle' +# NOTE: Only matches these exact case patterns, not 'tLe', 'tlE', 'TlE', 'tLE', etc. +# ==================================================================================== +TLE: + - changed-files: + - any-glob-to-any-file: + - '**/tle/**' + - '**/TLE/**' + - '**/Tle/**' + - '**/*tle*' + - '**/*TLE*' + - '**/*Tle*' # ========================================== -# 4. Git Configuration Labels (GIT) +# Git Configuration Labels (GIT) # Applied if Git-related configuration files are modified. # ========================================== GIT: @@ -96,7 +113,7 @@ GIT: - '.pre-commit-config.yaml' # ========================================== -# 5. Backend Labels +# Backend Labels # Rule: Applied if files under third_party/{backend} # or .github/workflows/{backend}* are modified. # ========================================== @@ -208,18 +225,3 @@ xpu: - any-glob-to-any-file: - 'third_party/xpu/**' - '.github/workflows/xpu*' - -# ==================================================================================== -# 6. TLE Labels -# Rule: Applied if any file path contains 'tle', 'TLE', or 'Tle' -# NOTE: Only matches these exact case patterns, not 'tLe', 'tlE', 'TlE', 'tLE', etc. -# ==================================================================================== -TLE: - - changed-files: - - any-glob-to-any-file: - - '**/tle/**' - - '**/TLE/**' - - '**/Tle/**' - - '**/*tle*' - - '**/*TLE*' - - '**/*Tle*' diff --git a/.github/workflows/mthreads3.6-build-and-test.yml b/.github/workflows/mthreads3.6-build-and-test.yml index 0fa00deb9..9664884b6 100644 --- a/.github/workflows/mthreads3.6-build-and-test.yml +++ b/.github/workflows/mthreads3.6-build-and-test.yml @@ -34,6 +34,12 @@ jobs: mthreads36x-unit-test: runs-on: mthreads3.6 if: ${{ github.repository == 'FlagTree/flagtree' || github.repository == 'flagos-ai/flagtree' }} + + # Add permissions to read PR information (required for fetching labels) + permissions: + contents: read + pull-requests: read + steps: - name: Setup environment shell: bash @@ -46,14 +52,14 @@ jobs: with: checkout_version: 'v6' - - name: Check if backend-relevant files changed - id: check_backend - uses: flagos-ai/FlagTree/.github/actions/check-backend-changed@main + - name: Check if CI should run based on PR labels + id: check_labels + uses: ./.github/actions/should-skip-ci-by-labels with: backend: mthreads - name: Build FlagTree - if: steps.check_backend.outputs.should_skip != 'true' + if: steps.check_labels.outputs.should_skip != 'true' shell: bash run: | set -x @@ -61,7 +67,7 @@ jobs: MAX_JOBS=32 python3 -m pip install . --no-build-isolation - name: Unit Test - if: steps.check_backend.outputs.should_skip != 'true' + if: steps.check_labels.outputs.should_skip != 'true' shell: bash run: | set -x From 21556648a40342bc8fb99d7258c0253cf3748c03 Mon Sep 17 00:00:00 2001 From: flagtree-bot Date: Tue, 28 Jul 2026 04:00:39 +0000 Subject: [PATCH 2/7] Apply code-format changes --- .github/actions/should-skip-ci-by-labels/action.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/should-skip-ci-by-labels/action.yml b/.github/actions/should-skip-ci-by-labels/action.yml index 37db87557..d886d1e24 100644 --- a/.github/actions/should-skip-ci-by-labels/action.yml +++ b/.github/actions/should-skip-ci-by-labels/action.yml @@ -42,7 +42,7 @@ runs: run: | BACKEND="${{ inputs.backend }}" EVENT_NAME="${{ github.event_name }}" - + echo "🔍 Checking PR labels for backend: $BACKEND" # 1. Handle non-PR events (e.g., direct push to the main branch) @@ -56,10 +56,10 @@ runs: # 2. Fetch all labels for the current PR PR_NUMBER="${{ github.event.pull_request.number }}" echo "Fetching labels for PR #$PR_NUMBER..." - + # Use the 'gh' command to fetch labels and parse them into a plain text list using 'jq' LABELS=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name') - + echo "Current PR labels:" echo "$LABELS" @@ -88,4 +88,4 @@ runs: fi # Output the final result - echo "should_skip=$SHOULD_SKIP" >> $GITHUB_OUTPUT \ No newline at end of file + echo "should_skip=$SHOULD_SKIP" >> $GITHUB_OUTPUT From 7798650d2f81a6a2d33fedd06e670c7548e8df95 Mon Sep 17 00:00:00 2001 From: Liu Haoyu Date: Tue, 28 Jul 2026 12:08:42 +0800 Subject: [PATCH 3/7] Use github-script to fetch PR labels instead of gh CLI --- .../should-skip-ci-by-labels/action.yml | 95 ++++++++++--------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/.github/actions/should-skip-ci-by-labels/action.yml b/.github/actions/should-skip-ci-by-labels/action.yml index d886d1e24..101f93ebc 100644 --- a/.github/actions/should-skip-ci-by-labels/action.yml +++ b/.github/actions/should-skip-ci-by-labels/action.yml @@ -25,6 +25,10 @@ inputs: backend: description: 'Backend name matching the PR label (e.g. mthreads, nvidia)' required: true + github-token: + description: 'GitHub token for API access' + required: true + default: ${{ github.token }} outputs: should_skip: @@ -36,56 +40,53 @@ runs: steps: - name: Check PR labels id: check - shell: bash - env: - GH_TOKEN: ${{ github.token }} - run: | - BACKEND="${{ inputs.backend }}" - EVENT_NAME="${{ github.event_name }}" - - echo "🔍 Checking PR labels for backend: $BACKEND" - - # 1. Handle non-PR events (e.g., direct push to the main branch) - # Direct pushes do not have PR labels. For safety, default to NOT skipping (i.e., run CI). - if [ "$EVENT_NAME" != "pull_request" ] && [ "$EVENT_NAME" != "pull_request_target" ]; then - echo "⚠️ Event is '$EVENT_NAME' (not a PR). Defaulting to run CI." - echo "should_skip=false" >> $GITHUB_OUTPUT - exit 0 - fi + uses: actions/github-script@v7 + with: + github-token: ${{ inputs.github-token }} + script: | + const backend = '${{ inputs.backend }}'; + const eventName = context.eventName; - # 2. Fetch all labels for the current PR - PR_NUMBER="${{ github.event.pull_request.number }}" - echo "Fetching labels for PR #$PR_NUMBER..." + console.log(`🔍 Checking PR labels for backend: ${backend}`); - # Use the 'gh' command to fetch labels and parse them into a plain text list using 'jq' - LABELS=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name') + // 1. Handle non-PR events (e.g., direct push to the main branch) + // Direct pushes do not have PR labels. For safety, default to NOT skipping (i.e., run CI). + if (eventName !== 'pull_request' && eventName !== 'pull_request_target') { + console.log(`⚠️ Event is '${eventName}' (not a PR). Defaulting to run CI.`); + core.setOutput('should_skip', 'false'); + return; + } - echo "Current PR labels:" - echo "$LABELS" + // 2. Fetch all labels for the current PR + const labels = context.payload.pull_request.labels.map(label => label.name); + console.log('Current PR labels:', labels.join(', ') || '(no labels)'); - # 3. Evaluation logic with short-circuit evaluation - SHOULD_SKIP=false + // 3. Evaluation logic with short-circuit evaluation + let shouldSkip = false; - # Check 1: If 'DOC' label exists, it means ALL changed files are docs (and no CMakeLists.txt). - # We can safely skip CI and exit early. - if echo "$LABELS" | grep -qx "DOC"; then - echo "🛑 Found 'DOC' label. All changes are documentation. Will safely skip CI." - SHOULD_SKIP=true - # Check 2: If no 'DOC' label, check for the specific backend label (e.g., 'mthreads'). - # If found, we need to run CI. No need to check for 'CORE'. - elif echo "$LABELS" | grep -qx "$BACKEND"; then - echo "✅ Found backend label '$BACKEND'. Will run CI." - SHOULD_SKIP=false - # Check 3: If no backend label, check for the 'CORE' label (core code changes). - # If found, we need to run CI for all backends. - elif echo "$LABELS" | grep -qx "CORE"; then - echo "✅ Found 'CORE' label. Core files changed, will run CI." - SHOULD_SKIP=false - # Check 4: No relevant labels found, skip CI. - else - echo "🛑 No relevant labels (DOC, $BACKEND, or CORE) found. Will safely skip CI." - SHOULD_SKIP=true - fi + // Check 1: If 'DOC' label exists, it means ALL changed files are docs (and no CMakeLists.txt). + // We can safely skip CI and exit early. + if (labels.includes('DOC')) { + console.log("🛑 Found 'DOC' label. All changes are documentation. Will safely skip CI."); + shouldSkip = true; + } + // Check 2: If no 'DOC' label, check for the specific backend label (e.g., 'mthreads'). + // If found, we need to run CI. No need to check for 'CORE'. + else if (labels.includes(backend)) { + console.log(`✅ Found backend label '${backend}'. Will run CI.`); + shouldSkip = false; + } + // Check 3: If no backend label, check for the 'CORE' label (core code changes). + // If found, we need to run CI for all backends. + else if (labels.includes('CORE')) { + console.log("✅ Found 'CORE' label. Core files changed, will run CI."); + shouldSkip = false; + } + // Check 4: No relevant labels found, skip CI. + else { + console.log(`🛑 No relevant labels (DOC, ${backend}, or CORE) found. Will safely skip CI.`); + shouldSkip = true; + } - # Output the final result - echo "should_skip=$SHOULD_SKIP" >> $GITHUB_OUTPUT + // Output the final result + core.setOutput('should_skip', shouldSkip ? 'true' : 'false'); From b70ad42f494010aa9858aafe804df682de131c8f Mon Sep 17 00:00:00 2001 From: flagtree-bot Date: Tue, 28 Jul 2026 04:12:22 +0000 Subject: [PATCH 4/7] Apply code-format changes --- .github/actions/should-skip-ci-by-labels/action.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/should-skip-ci-by-labels/action.yml b/.github/actions/should-skip-ci-by-labels/action.yml index 101f93ebc..b6ef1594f 100644 --- a/.github/actions/should-skip-ci-by-labels/action.yml +++ b/.github/actions/should-skip-ci-by-labels/action.yml @@ -69,19 +69,19 @@ runs: if (labels.includes('DOC')) { console.log("🛑 Found 'DOC' label. All changes are documentation. Will safely skip CI."); shouldSkip = true; - } + } // Check 2: If no 'DOC' label, check for the specific backend label (e.g., 'mthreads'). // If found, we need to run CI. No need to check for 'CORE'. else if (labels.includes(backend)) { console.log(`✅ Found backend label '${backend}'. Will run CI.`); shouldSkip = false; - } + } // Check 3: If no backend label, check for the 'CORE' label (core code changes). // If found, we need to run CI for all backends. else if (labels.includes('CORE')) { console.log("✅ Found 'CORE' label. Core files changed, will run CI."); shouldSkip = false; - } + } // Check 4: No relevant labels found, skip CI. else { console.log(`🛑 No relevant labels (DOC, ${backend}, or CORE) found. Will safely skip CI.`); From c6666d854dd150bd95c85a88c5b6cdd60c3b8c82 Mon Sep 17 00:00:00 2001 From: Liu Haoyu Date: Tue, 28 Jul 2026 13:12:34 +0800 Subject: [PATCH 5/7] Add retry logic to check PR labels --- .../should-skip-ci-by-labels/action.yml | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/.github/actions/should-skip-ci-by-labels/action.yml b/.github/actions/should-skip-ci-by-labels/action.yml index b6ef1594f..7e6724472 100644 --- a/.github/actions/should-skip-ci-by-labels/action.yml +++ b/.github/actions/should-skip-ci-by-labels/action.yml @@ -57,34 +57,50 @@ runs: return; } - // 2. Fetch all labels for the current PR - const labels = context.payload.pull_request.labels.map(label => label.name); - console.log('Current PR labels:', labels.join(', ') || '(no labels)'); - - // 3. Evaluation logic with short-circuit evaluation + const prNumber = context.payload.pull_request.number; + let labels = []; let shouldSkip = false; + const maxRetries = 10; // Maximum number of retries + const delayMs = 1000; // Delay between retries in milliseconds + + // 2. Fetch labels with retry mechanism to handle delayed labeling + for (let i = 0; i < maxRetries; i++) { + // Use GitHub API to fetch the latest PR data + const { data: prData } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); - // Check 1: If 'DOC' label exists, it means ALL changed files are docs (and no CMakeLists.txt). - // We can safely skip CI and exit early. + labels = prData.labels.map(label => label.name); + console.log(`[Attempt ${i + 1}/${maxRetries}] Current PR labels:`, labels.join(', ') || '(no labels)'); + + // If the PR has ANY labels, it means the labeler action has finished. + // We can break the loop and proceed to evaluation. + if (labels.length > 0) { + console.log('✅ Labeling completed. Proceeding to evaluation.'); + break; + } + + // If it's the last attempt, don't wait + if (i < maxRetries - 1) { + console.log(`⏳ No labels found yet. Waiting ${delayMs / 1000}s before retrying...`); + await new Promise(resolve => setTimeout(resolve, delayMs)); + } + } + + // 3. Evaluation logic with short-circuit evaluation if (labels.includes('DOC')) { console.log("🛑 Found 'DOC' label. All changes are documentation. Will safely skip CI."); shouldSkip = true; - } - // Check 2: If no 'DOC' label, check for the specific backend label (e.g., 'mthreads'). - // If found, we need to run CI. No need to check for 'CORE'. - else if (labels.includes(backend)) { + } else if (labels.includes(backend)) { console.log(`✅ Found backend label '${backend}'. Will run CI.`); shouldSkip = false; - } - // Check 3: If no backend label, check for the 'CORE' label (core code changes). - // If found, we need to run CI for all backends. - else if (labels.includes('CORE')) { + } else if (labels.includes('CORE')) { console.log("✅ Found 'CORE' label. Core files changed, will run CI."); shouldSkip = false; - } - // Check 4: No relevant labels found, skip CI. - else { - console.log(`🛑 No relevant labels (DOC, ${backend}, or CORE) found. Will safely skip CI.`); + } else { + console.log(`🛑 No relevant labels (DOC, ${backend}, or CORE) found after retries. Will safely skip CI.`); shouldSkip = true; } From f827f96f0eb3a005fd2ca10e5b6e07af68210c2b Mon Sep 17 00:00:00 2001 From: Liu Haoyu Date: Tue, 28 Jul 2026 13:29:24 +0800 Subject: [PATCH 6/7] Use github-script v6 --- .github/actions/should-skip-ci-by-labels/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/should-skip-ci-by-labels/action.yml b/.github/actions/should-skip-ci-by-labels/action.yml index 7e6724472..982cf769e 100644 --- a/.github/actions/should-skip-ci-by-labels/action.yml +++ b/.github/actions/should-skip-ci-by-labels/action.yml @@ -40,7 +40,7 @@ runs: steps: - name: Check PR labels id: check - uses: actions/github-script@v7 + uses: actions/github-script@v6 with: github-token: ${{ inputs.github-token }} script: | From ac9adbe0e7737537d4efb1420bb988fef96a219e Mon Sep 17 00:00:00 2001 From: Liu Haoyu Date: Tue, 28 Jul 2026 15:37:29 +0800 Subject: [PATCH 7/7] Use 'all' to enforce AND logic for DOC label matching --- .github/new-prs-labeler.yml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml index 06aa9dca5..95b00a6d9 100644 --- a/.github/new-prs-labeler.yml +++ b/.github/new-prs-labeler.yml @@ -68,19 +68,20 @@ CI/CD: # are documentation files, EXCLUDING CMakeLists.txt. # ========================================== DOC: - - changed-files: - - any-glob-to-all-files: - - '**/*.md' - - '**/*.txt' - - '**/*.rst' - - '**/*.png' - - '**/*.jpg' - - '**/*.jpeg' - - '**/*.gif' - - '**/*.ico' - - 'LICENSE' - - all-globs-to-all-files: - - '!**/CMakeLists.txt' + - all: + - changed-files: + - any-glob-to-all-files: + - '**/*.md' + - '**/*.txt' + - '**/*.rst' + - '**/*.png' + - '**/*.jpg' + - '**/*.jpeg' + - '**/*.gif' + - '**/*.ico' + - 'LICENSE' + - all-globs-to-all-files: + - '!**/CMakeLists.txt' # ==================================================================================== # TLE Labels