From 5c283ceead25c6a83d7cf403f858ad2c31a749c6 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Tue, 14 Oct 2025 14:02:20 +0000 Subject: [PATCH 1/8] Track maximize-kselftest-coverage branch (uses kernel-container-build maximize-kselftest-coverage) From 5b896bf0dabded24570c074ef9aa821ec8c36e6b Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Tue, 14 Oct 2025 14:03:35 +0000 Subject: [PATCH 2/8] Update workflow to use updated maximize-kselftest-coverage branch --- .github/workflows/kernel-build-and-test-x86_64.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index 7f1af38b9e4a..a120c1296355 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 with: repository: ctrliq/kernel-container-build - ref: test-stage-separation + ref: maximize-kselftest-coverage path: kernel-container-build token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} @@ -92,7 +92,7 @@ jobs: uses: actions/checkout@v4 with: repository: ctrliq/kernel-container-build - ref: test-stage-separation + ref: maximize-kselftest-coverage path: kernel-container-build token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} @@ -146,7 +146,7 @@ jobs: uses: actions/checkout@v4 with: repository: ctrliq/kernel-container-build - ref: test-stage-separation + ref: maximize-kselftest-coverage path: kernel-container-build token: ${{ secrets.PRIVATE_REPO_ACCESS_TOKEN }} From 74ba2590154b715af7f6f875eab7ce48afd8308c Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Wed, 15 Oct 2025 12:01:02 +0000 Subject: [PATCH 3/8] Improve kselftest report Signed-off-by: Shreeya Patel --- .../kernel-build-and-test-x86_64.yml | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index a120c1296355..1af3a1fe064e 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -212,6 +212,7 @@ jobs: name: kselftest-logs-x86_64 path: output-previous branch: ${{ github.ref_name }} + workflow_conclusion: success search_artifacts: true skip_unpack: false if_no_artifact_found: warn @@ -220,13 +221,44 @@ jobs: - name: Compare test results run: | if [ -f output-previous/kselftests-*.log ]; then - BEFORE=$(grep -a '^ok' output-previous/kselftests-*.log | wc -l) - AFTER=$(grep -a '^ok' output-current/kselftests-*.log | wc -l) - echo "Previous run: $BEFORE passing tests" - echo "Current run: $AFTER passing tests" - if [ "$AFTER" -lt "$BEFORE" ]; then - echo "::warning::Regression detected: $BEFORE -> $AFTER" + # Compare passing tests (ok) + BEFORE_PASS=$(grep -a '^ok' output-previous/kselftests-*.log | wc -l) + AFTER_PASS=$(grep -a '^ok' output-current/kselftests-*.log | wc -l) + + # Compare failing tests (not ok) + BEFORE_FAIL=$(grep -a '^not ok' output-previous/kselftests-*.log | wc -l) + AFTER_FAIL=$(grep -a '^not ok' output-current/kselftests-*.log | wc -l) + + echo "### Kselftest Comparison (Branch: ${{ github.ref_name }})" + echo "Passing tests: $BEFORE_PASS -> $AFTER_PASS" + echo "Failing tests: $BEFORE_FAIL -> $AFTER_FAIL" + + # Calculate differences + PASS_DIFF=$((AFTER_PASS - BEFORE_PASS)) + FAIL_DIFF=$((AFTER_FAIL - BEFORE_FAIL)) + + echo "Pass difference: $PASS_DIFF" + echo "Fail difference: $FAIL_DIFF" + + # Check for regression (more than 3 tests difference) + REGRESSION=0 + + if [ $PASS_DIFF -lt -3 ]; then + echo "::error::Regression detected: $PASS_DIFF passing tests (threshold: -3)" + REGRESSION=1 + fi + + if [ $FAIL_DIFF -gt 3 ]; then + echo "::error::Regression detected: +$FAIL_DIFF failing tests (threshold: +3)" + REGRESSION=1 + fi + + if [ $REGRESSION -eq 1 ]; then + echo "::error::Test regression exceeds acceptable threshold of 3 tests" + exit 1 + else + echo "::notice::Test results within acceptable range (threshold: ±3 tests)" fi else - echo "No previous results found, skipping comparison" + echo "::warning::No previous successful test results found for branch ${{ github.ref_name }}, skipping comparison" fi From e8927e18a93f3bc8704e986376f38b166c49cefe Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Thu, 16 Oct 2025 20:20:07 +0000 Subject: [PATCH 4/8] Improve kselftest comparison with smart base branch detection - Add checkout step with full history (fetch-depth: 0) to enable git operations - Implement intelligent base branch detection: * For PRs: use the base branch directly from github.base_ref * For direct pushes: find closest common ancestor using git merge-base * Iterate through all remote branches to find best match - Compare against detected base branch instead of same branch - Add outputs (comparison_status, comparison_message) for downstream jobs - Improve error handling and warning messages - Skip comparison gracefully if base branch cannot be determined This makes the comparison much more useful by comparing against the branch you're merging into, rather than the previous run of the same branch. Signed-off-by: Shreeya Patel --- .../kernel-build-and-test-x86_64.yml | 108 ++++++++++++++++-- 1 file changed, 97 insertions(+), 11 deletions(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index 1af3a1fe064e..8d79e0064f42 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -199,39 +199,117 @@ jobs: if: success() || failure() steps: + - name: Checkout kernel source + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Download current kselftest logs uses: actions/download-artifact@v4 with: name: kselftest-logs-x86_64 path: output-current - - name: Download previous kselftest logs + - name: Determine base branch for comparison + id: base_branch + run: | + BASE_BRANCH="" + + # For PRs, use the base branch directly + if [ -n "${{ github.base_ref }}" ]; then + BASE_BRANCH="${{ github.base_ref }}" + echo "Using PR base branch: $BASE_BRANCH" + else + # For direct pushes, find the common ancestor branch + echo "Not a PR, finding base branch via merge-base" + + # Get all remote branches and find the one with closest common ancestor + CURRENT_COMMIT=$(git rev-parse HEAD) + BEST_BRANCH="" + CLOSEST_DISTANCE=999999 + + for remote_branch in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin/ | grep -v 'HEAD\|PR-CHECKER'); do + branch_name=$(echo "$remote_branch" | sed 's|^origin/||') + + # Skip if it's the current branch + if [ "$branch_name" = "${{ github.ref_name }}" ]; then + continue + fi + + MERGE_BASE=$(git merge-base HEAD "$remote_branch" 2>/dev/null || echo "") + + # Check if this branch shares history and isn't the current commit + if [ -n "$MERGE_BASE" ] && [ "$MERGE_BASE" != "$CURRENT_COMMIT" ]; then + # Calculate distance (number of commits) from merge-base to current HEAD + DISTANCE=$(git rev-list --count ${MERGE_BASE}..HEAD 2>/dev/null || echo "999999") + + # Find the branch with the shortest distance (most recent common ancestor) + if [ "$DISTANCE" -lt "$CLOSEST_DISTANCE" ]; then + CLOSEST_DISTANCE=$DISTANCE + BEST_BRANCH=$branch_name + fi + fi + done + + if [ -n "$BEST_BRANCH" ]; then + BASE_BRANCH=$BEST_BRANCH + echo "Found common ancestor with $BASE_BRANCH (distance: $CLOSEST_DISTANCE commits)" + fi + fi + + if [ -z "$BASE_BRANCH" ]; then + echo "::warning::Could not determine base branch for comparison - no common ancestor found" + echo "::warning::Kselftest comparison will be skipped" + echo "base_branch=" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "base_branch=$BASE_BRANCH" >> $GITHUB_OUTPUT + echo "Base branch for comparison: $BASE_BRANCH" + + - name: Download baseline kselftest logs from base branch + if: steps.base_branch.outputs.base_branch != '' uses: dawidd6/action-download-artifact@v3 with: workflow: kernel-build-and-test-x86_64.yml name: kselftest-logs-x86_64 path: output-previous - branch: ${{ github.ref_name }} + branch: ${{ steps.base_branch.outputs.base_branch }} workflow_conclusion: success search_artifacts: true skip_unpack: false if_no_artifact_found: warn + # Only search the last 5 successful runs for better performance + run_number: ${{ github.run_number }} + search_depth: 5 continue-on-error: true + timeout-minutes: 3 - name: Compare test results + id: comparison run: | - if [ -f output-previous/kselftests-*.log ]; then + # Check if we have a base branch to compare against + if [ -z "${{ steps.base_branch.outputs.base_branch }}" ]; then + echo "::warning::No base branch found for comparison" + echo "::warning::Kselftest comparison will be skipped" + echo "comparison_status=skipped" >> $GITHUB_OUTPUT + echo "comparison_message=No base branch found - unable to determine merge target" >> $GITHUB_OUTPUT + exit 0 + fi + + # Check if baseline logs exist + if ls output-previous/kselftests-*.log 1> /dev/null 2>&1; then # Compare passing tests (ok) - BEFORE_PASS=$(grep -a '^ok' output-previous/kselftests-*.log | wc -l) - AFTER_PASS=$(grep -a '^ok' output-current/kselftests-*.log | wc -l) + BEFORE_PASS=$(grep -a '^ok' output-previous/kselftests-*.log | wc -l || echo "0") + AFTER_PASS=$(grep -a '^ok' output-current/kselftests-*.log | wc -l || echo "0") # Compare failing tests (not ok) - BEFORE_FAIL=$(grep -a '^not ok' output-previous/kselftests-*.log | wc -l) - AFTER_FAIL=$(grep -a '^not ok' output-current/kselftests-*.log | wc -l) + BEFORE_FAIL=$(grep -a '^not ok' output-previous/kselftests-*.log | wc -l || echo "0") + AFTER_FAIL=$(grep -a '^not ok' output-current/kselftests-*.log | wc -l || echo "0") - echo "### Kselftest Comparison (Branch: ${{ github.ref_name }})" - echo "Passing tests: $BEFORE_PASS -> $AFTER_PASS" - echo "Failing tests: $BEFORE_FAIL -> $AFTER_FAIL" + echo "### Kselftest Comparison" + echo "Baseline (from ${{ steps.base_branch.outputs.base_branch }}): $BEFORE_PASS passing, $BEFORE_FAIL failing" + echo "Current (${{ github.ref_name }}): $AFTER_PASS passing, $AFTER_FAIL failing" # Calculate differences PASS_DIFF=$((AFTER_PASS - BEFORE_PASS)) @@ -255,10 +333,18 @@ jobs: if [ $REGRESSION -eq 1 ]; then echo "::error::Test regression exceeds acceptable threshold of 3 tests" + echo "comparison_status=failed" >> $GITHUB_OUTPUT + echo "comparison_message=Regression detected: Pass diff: $PASS_DIFF, Fail diff: $FAIL_DIFF (threshold: ±3)" >> $GITHUB_OUTPUT exit 1 else echo "::notice::Test results within acceptable range (threshold: ±3 tests)" + echo "comparison_status=passed" >> $GITHUB_OUTPUT + echo "comparison_message=Baseline: $BEFORE_PASS passing, $BEFORE_FAIL failing | Current: $AFTER_PASS passing, $AFTER_FAIL failing" >> $GITHUB_OUTPUT fi else - echo "::warning::No previous successful test results found for branch ${{ github.ref_name }}, skipping comparison" + echo "::warning::No baseline test results found for branch ${{ steps.base_branch.outputs.base_branch }}" + echo "::notice::Cannot compare against base branch - artifacts may not exist or have expired (7-day retention)" + echo "::notice::Skipping comparison - PR will still be created with warning" + echo "comparison_status=skipped" >> $GITHUB_OUTPUT + echo "comparison_message=No baseline results available from ${{ steps.base_branch.outputs.base_branch }}" >> $GITHUB_OUTPUT fi From b3ac2403550bc410e4a4f411445cc648281ac1df Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Thu, 16 Oct 2025 20:38:06 +0000 Subject: [PATCH 5/8] Add automated PR creation after successful CI runs - Create/update PRs automatically when all test stages pass - Include comprehensive test results and build statistics in PR body - Intelligently determine target branch using merge-base - Support both single and multiple commit scenarios - Use helper script to generate well-formatted PR descriptions The PR creation job: - Only runs if build, boot, and kselftest stages succeed - Downloads all artifacts (build logs, boot logs, test logs) - Extracts statistics (test pass/fail counts, build timers) - Determines base branch (same logic as compare-results) - Includes comparison results in PR body - Creates new PR or updates existing one - Adds labels: automated, tested, ready-for-review Files added: - .github/scripts/create-pr-body.sh: Generates PR body with test results - .github/workflows/kernel-build-and-test-x86_64.yml: Adds create-pr job --- .github/scripts/create-pr-body.sh | 49 +++++ .../kernel-build-and-test-x86_64.yml | 187 ++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100755 .github/scripts/create-pr-body.sh diff --git a/.github/scripts/create-pr-body.sh b/.github/scripts/create-pr-body.sh new file mode 100755 index 000000000000..521207eca3ec --- /dev/null +++ b/.github/scripts/create-pr-body.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Script to create PR body +# Arguments: build_time total_time passed failed run_id comparison_section + +BUILD_TIME="$1" +TOTAL_TIME="$2" +PASSED="$3" +FAILED="$4" +RUN_ID="$5" +COMPARISON_SECTION="$6" +REPO="$7" + +cat << EOF +## Summary +This PR has been automatically created after successful completion of all CI stages. + +## Commit Message(s) +\`\`\` +EOF + +cat /tmp/commit_message.txt + +cat << EOF +\`\`\` + +## Test Results + +### ✅ Build Stage +- Status: Passed +- Build Time: ${BUILD_TIME} +- Total Time: ${TOTAL_TIME} +- [View build logs](https://github.com/${REPO}/actions/runs/${RUN_ID}) + +### ✅ Boot Verification +- Status: Passed +- [View boot logs](https://github.com/${REPO}/actions/runs/${RUN_ID}) + +### ✅ Kernel Selftests +- **Passed:** ${PASSED} +- **Failed:** ${FAILED} +- [View kselftest logs](https://github.com/${REPO}/actions/runs/${RUN_ID}) + +${COMPARISON_SECTION} + +--- +🤖 This PR was automatically generated by GitHub Actions +Run ID: ${RUN_ID} +EOF diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index 8d79e0064f42..a43411ae6925 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -348,3 +348,190 @@ jobs: echo "comparison_status=skipped" >> $GITHUB_OUTPUT echo "comparison_message=No baseline results available from ${{ steps.base_branch.outputs.base_branch }}" >> $GITHUB_OUTPUT fi + create-pr: + name: Create Pull Request + runs-on: kernel-build + needs: [build, boot, test-kselftest, compare-results] + if: success() || failure() + + steps: + - name: Check if all stages passed + run: | + if [ "${{ needs.build.result }}" != "success" ] || \ + [ "${{ needs.boot.result }}" != "success" ] || \ + [ "${{ needs.test-kselftest.result }}" != "success" ]; then + echo "One or more test stages failed, skipping PR creation" + exit 1 + fi + echo "All test stages passed, proceeding with PR creation" + + - name: Checkout kernel source + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Download kernel compilation logs + uses: actions/download-artifact@v4 + with: + name: kernel-compilation-logs-x86_64 + path: artifacts/build + + - name: Download boot logs + uses: actions/download-artifact@v4 + with: + name: boot-logs-x86_64 + path: artifacts/boot + + - name: Download kselftest logs + uses: actions/download-artifact@v4 + with: + name: kselftest-logs-x86_64 + path: artifacts/test + + - name: Extract test statistics + id: stats + run: | + PASSED=$(grep -a '^ok' artifacts/test/kselftests-*.log | wc -l || echo "0") + FAILED=$(grep -a '^not ok' artifacts/test/kselftests-*.log | wc -l || echo "0") + echo "passed=$PASSED" >> $GITHUB_OUTPUT + echo "failed=$FAILED" >> $GITHUB_OUTPUT + + - name: Extract build timers + id: build_info + run: | + BUILD_TIME=$(grep -oP '\[TIMER\]\{BUILD\}:\s*\K[0-9]+' artifacts/build/kernel-build.log | head -1 || echo "N/A") + TOTAL_TIME=$(grep -oP '\[TIMER\]\{TOTAL\}\s*\K[0-9]+' artifacts/build/kernel-build.log | head -1 || echo "N/A") + echo "build_time=${BUILD_TIME}s" >> $GITHUB_OUTPUT + echo "total_time=${TOTAL_TIME}s" >> $GITHUB_OUTPUT + + - name: Get commit information + id: commit_msg + run: | + # Count commits since origin/main (or appropriate base branch) + BASE_BRANCH="main" + if ! git rev-parse origin/$BASE_BRANCH >/dev/null 2>&1; then + # Try other common base branch names + for branch in master lts-9.2 lts-9; do + if git rev-parse origin/$branch >/dev/null 2>&1; then + BASE_BRANCH=$branch + break + fi + done + fi + + COMMIT_COUNT=$(git rev-list --count origin/$BASE_BRANCH..HEAD 2>/dev/null || echo "1") + + if [ "$COMMIT_COUNT" -eq "1" ]; then + # Single commit: use commit subject + git log -1 --pretty=%s > /tmp/commit_subject.txt + COMMIT_SUBJECT=$(cat /tmp/commit_subject.txt) + echo "commit_subject=$COMMIT_SUBJECT" >> $GITHUB_OUTPUT + + # Save full commit message to file + git log -1 --pretty=%B > /tmp/commit_message.txt + else + # Multiple commits: create summary + echo "commit_subject=Multiple patches tested ($COMMIT_COUNT commits)" >> $GITHUB_OUTPUT + + # Get all commit messages and save to file + git log origin/$BASE_BRANCH..HEAD --pretty=format:"### %s%n%n%b%n---" > /tmp/commit_message.txt + fi + + - name: Create Pull Request + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + BASE_BRANCH="" + + # For PRs, use the base branch directly + if [ -n "${{ github.base_ref }}" ]; then + BASE_BRANCH="${{ github.base_ref }}" + echo "Using PR base branch: $BASE_BRANCH" + else + # For direct pushes, find the common ancestor branch + echo "Not a PR, finding base branch via merge-base" + + # Get all remote branches and find the one with closest common ancestor + CURRENT_COMMIT=$(git rev-parse HEAD) + BEST_BRANCH="" + CLOSEST_DISTANCE=999999 + + for remote_branch in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin/ | grep -v 'HEAD\|PR-CHECKER'); do + branch_name=$(echo "$remote_branch" | sed 's|^origin/||') + + # Skip if it's the current branch + if [ "$branch_name" = "${{ github.ref_name }}" ]; then + continue + fi + + MERGE_BASE=$(git merge-base HEAD "$remote_branch" 2>/dev/null || echo "") + + # Check if this branch shares history and isn't the current commit + if [ -n "$MERGE_BASE" ] && [ "$MERGE_BASE" != "$CURRENT_COMMIT" ]; then + # Calculate distance (number of commits) from merge-base to current HEAD + DISTANCE=$(git rev-list --count ${MERGE_BASE}..HEAD 2>/dev/null || echo "999999") + + # Find the branch with the shortest distance (most recent common ancestor) + if [ "$DISTANCE" -lt "$CLOSEST_DISTANCE" ]; then + CLOSEST_DISTANCE=$DISTANCE + BEST_BRANCH=$branch_name + fi + fi + done + + if [ -n "$BEST_BRANCH" ]; then + BASE_BRANCH=$BEST_BRANCH + echo "Found common ancestor with $BASE_BRANCH (distance: $CLOSEST_DISTANCE commits)" + fi + fi + + if [ -z "$BASE_BRANCH" ]; then + echo "ERROR: Could not determine base branch for PR" + exit 1 + fi + + echo "Creating PR from ${{ github.ref_name }} to $BASE_BRANCH" + + # Determine comparison status message + COMPARISON_RESULT="${{ needs.compare-results.result }}" + if [ "$COMPARISON_RESULT" = "success" ]; then + COMPARISON_SECTION="### ✅ Test Comparison + - Status: Passed - Within acceptable threshold (±3 tests) + - Compared against: $BASE_BRANCH" + else + COMPARISON_SECTION="### ⚠️ Test Comparison + - Status: Skipped + - Reason: No baseline test results available from $BASE_BRANCH + - **Note:** Manual review recommended to ensure no regressions" + fi + + # Create PR body using script + chmod +x .github/scripts/create-pr-body.sh + .github/scripts/create-pr-body.sh \ + "${{ steps.build_info.outputs.build_time }}" \ + "${{ steps.build_info.outputs.total_time }}" \ + "${{ steps.stats.outputs.passed }}" \ + "${{ steps.stats.outputs.failed }}" \ + "${{ github.run_id }}" \ + "$COMPARISON_SECTION" \ + "${{ github.repository }}" \ + > pr_body.md + + # Check if PR already exists + EXISTING_PR=$(gh pr list --head "${{ github.ref_name }}" --base "$BASE_BRANCH" --json number --jq '.[0].number' || echo "") + + if [ -n "$EXISTING_PR" ]; then + echo "PR #$EXISTING_PR already exists, updating it" + gh pr edit "$EXISTING_PR" \ + --title "[${{ github.ref_name }}] ${{ steps.commit_msg.outputs.commit_subject }}" \ + --body-file pr_body.md + else + echo "Creating new PR from ${{ github.ref_name }} to $BASE_BRANCH" + gh pr create \ + --base "$BASE_BRANCH" \ + --head "${{ github.ref_name }}" \ + --title "[${{ github.ref_name }}] ${{ steps.commit_msg.outputs.commit_subject }}" \ + --body-file pr_body.md \ + --label "automated,tested,ready-for-review" + fi From a31e328d7ac47099fe64d503dda58ddcc3678d19 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Thu, 16 Oct 2025 20:43:48 +0000 Subject: [PATCH 6/8] Kselftest and Create PR optimizations Signed-off-by: Shreeya Patel --- .../kernel-build-and-test-x86_64.yml | 57 ++++--------------- 1 file changed, 10 insertions(+), 47 deletions(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index a43411ae6925..35e4bc0f77bb 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -197,6 +197,8 @@ jobs: runs-on: kernel-build needs: test-kselftest if: success() || failure() + outputs: + base_branch: ${{ steps.base_branch.outputs.base_branch }} steps: - name: Checkout kernel source @@ -223,12 +225,14 @@ jobs: # For direct pushes, find the common ancestor branch echo "Not a PR, finding base branch via merge-base" - # Get all remote branches and find the one with closest common ancestor + # Get all remote branches sorted by commit date (most recent first) + # This ensures we check actively developed branches first CURRENT_COMMIT=$(git rev-parse HEAD) BEST_BRANCH="" CLOSEST_DISTANCE=999999 - for remote_branch in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin/ | grep -v 'HEAD\|PR-CHECKER'); do + # Only check the 30 most recently updated branches for performance + for remote_branch in $(git for-each-ref --sort=-committerdate --format='%(refname:short)' refs/remotes/origin/ | grep -v 'HEAD\|PR-CHECKER' | head -30); do branch_name=$(echo "$remote_branch" | sed 's|^origin/||') # Skip if it's the current branch @@ -368,7 +372,7 @@ jobs: - name: Checkout kernel source uses: actions/checkout@v4 with: - fetch-depth: 0 + fetch-depth: 50 # Shallow clone: enough for commit messages and logs token: ${{ secrets.GITHUB_TOKEN }} - name: Download kernel compilation logs @@ -442,52 +446,11 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - BASE_BRANCH="" - - # For PRs, use the base branch directly - if [ -n "${{ github.base_ref }}" ]; then - BASE_BRANCH="${{ github.base_ref }}" - echo "Using PR base branch: $BASE_BRANCH" - else - # For direct pushes, find the common ancestor branch - echo "Not a PR, finding base branch via merge-base" - - # Get all remote branches and find the one with closest common ancestor - CURRENT_COMMIT=$(git rev-parse HEAD) - BEST_BRANCH="" - CLOSEST_DISTANCE=999999 - - for remote_branch in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin/ | grep -v 'HEAD\|PR-CHECKER'); do - branch_name=$(echo "$remote_branch" | sed 's|^origin/||') - - # Skip if it's the current branch - if [ "$branch_name" = "${{ github.ref_name }}" ]; then - continue - fi - - MERGE_BASE=$(git merge-base HEAD "$remote_branch" 2>/dev/null || echo "") - - # Check if this branch shares history and isn't the current commit - if [ -n "$MERGE_BASE" ] && [ "$MERGE_BASE" != "$CURRENT_COMMIT" ]; then - # Calculate distance (number of commits) from merge-base to current HEAD - DISTANCE=$(git rev-list --count ${MERGE_BASE}..HEAD 2>/dev/null || echo "999999") - - # Find the branch with the shortest distance (most recent common ancestor) - if [ "$DISTANCE" -lt "$CLOSEST_DISTANCE" ]; then - CLOSEST_DISTANCE=$DISTANCE - BEST_BRANCH=$branch_name - fi - fi - done - - if [ -n "$BEST_BRANCH" ]; then - BASE_BRANCH=$BEST_BRANCH - echo "Found common ancestor with $BASE_BRANCH (distance: $CLOSEST_DISTANCE commits)" - fi - fi + # Reuse base branch from compare-results stage (already computed) + BASE_BRANCH="${{ needs.compare-results.outputs.base_branch }}" if [ -z "$BASE_BRANCH" ]; then - echo "ERROR: Could not determine base branch for PR" + echo "ERROR: Could not determine base branch for PR (compare-results did not find one)" exit 1 fi From 1de32e35c395d1c601dc53d2fc0229661f3ff3de Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Thu, 16 Oct 2025 21:04:43 +0000 Subject: [PATCH 7/8] Fix PR creation stage Signed-off-by: Shreeya Patel --- .../kernel-build-and-test-x86_64.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/kernel-build-and-test-x86_64.yml b/.github/workflows/kernel-build-and-test-x86_64.yml index 35e4bc0f77bb..ec83a3e1ab5b 100644 --- a/.github/workflows/kernel-build-and-test-x86_64.yml +++ b/.github/workflows/kernel-build-and-test-x86_64.yml @@ -5,6 +5,7 @@ permissions: contents: read actions: read packages: read + pull-requests: write jobs: build: @@ -199,12 +200,13 @@ jobs: if: success() || failure() outputs: base_branch: ${{ steps.base_branch.outputs.base_branch }} + comparison_status: ${{ steps.comparison.outputs.comparison_status }} steps: - name: Checkout kernel source uses: actions/checkout@v4 with: - fetch-depth: 0 + fetch-depth: 0 # Full history needed for reliable merge-base detection - name: Download current kselftest logs uses: actions/download-artifact@v4 @@ -359,15 +361,23 @@ jobs: if: success() || failure() steps: - - name: Check if all stages passed + - name: Check if tests passed and no regressions run: | + # Skip PR if any test stage failed if [ "${{ needs.build.result }}" != "success" ] || \ [ "${{ needs.boot.result }}" != "success" ] || \ [ "${{ needs.test-kselftest.result }}" != "success" ]; then echo "One or more test stages failed, skipping PR creation" exit 1 fi - echo "All test stages passed, proceeding with PR creation" + + # Skip PR if regression was detected (but allow if comparison was skipped/unavailable) + if [ "${{ needs.compare-results.outputs.comparison_status }}" = "failed" ]; then + echo "Test regression detected, skipping PR creation" + exit 1 + fi + + echo "All test stages passed and no regressions detected, proceeding with PR creation" - name: Checkout kernel source uses: actions/checkout@v4 @@ -495,6 +505,5 @@ jobs: --base "$BASE_BRANCH" \ --head "${{ github.ref_name }}" \ --title "[${{ github.ref_name }}] ${{ steps.commit_msg.outputs.commit_subject }}" \ - --body-file pr_body.md \ - --label "automated,tested,ready-for-review" + --body-file pr_body.md fi From b37386b8a41e1e7177a41d7aa27f288e9097a2a7 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Thu, 16 Oct 2025 20:20:53 +0000 Subject: [PATCH 8/8] firmware: arm_scpi: Ensure scpi_info is not assigned if the probe fails Fixes a bug where scpi_info global variable could be set even when probe fails, leading to potential use-after-free or NULL pointer dereference issues. Changes: - Use local scpi_drvinfo variable during probe - Only assign to global scpi_info after successful initialization - Clear scpi_info if subsequent operations fail - Ensures scpi_info is NULL when driver is not properly initialized --- drivers/firmware/arm_scpi.c | 61 +++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index ddf0b9ff9e15..435d0e2658a4 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -815,7 +815,7 @@ static int scpi_init_versions(struct scpi_drvinfo *info) info->firmware_version = le32_to_cpu(caps.platform_version); } /* Ignore error if not implemented */ - if (scpi_info->is_legacy && ret == -EOPNOTSUPP) + if (info->is_legacy && ret == -EOPNOTSUPP) return 0; return ret; @@ -913,13 +913,14 @@ static int scpi_probe(struct platform_device *pdev) struct resource res; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; + struct scpi_drvinfo *scpi_drvinfo; - scpi_info = devm_kzalloc(dev, sizeof(*scpi_info), GFP_KERNEL); - if (!scpi_info) + scpi_drvinfo = devm_kzalloc(dev, sizeof(*scpi_drvinfo), GFP_KERNEL); + if (!scpi_drvinfo) return -ENOMEM; if (of_match_device(legacy_scpi_of_match, &pdev->dev)) - scpi_info->is_legacy = true; + scpi_drvinfo->is_legacy = true; count = of_count_phandle_with_args(np, "mboxes", "#mbox-cells"); if (count < 0) { @@ -927,19 +928,19 @@ static int scpi_probe(struct platform_device *pdev) return -ENODEV; } - scpi_info->channels = devm_kcalloc(dev, count, sizeof(struct scpi_chan), - GFP_KERNEL); - if (!scpi_info->channels) + scpi_drvinfo->channels = + devm_kcalloc(dev, count, sizeof(struct scpi_chan), GFP_KERNEL); + if (!scpi_drvinfo->channels) return -ENOMEM; - ret = devm_add_action(dev, scpi_free_channels, scpi_info); + ret = devm_add_action(dev, scpi_free_channels, scpi_drvinfo); if (ret) return ret; - for (; scpi_info->num_chans < count; scpi_info->num_chans++) { + for (; scpi_drvinfo->num_chans < count; scpi_drvinfo->num_chans++) { resource_size_t size; - int idx = scpi_info->num_chans; - struct scpi_chan *pchan = scpi_info->channels + idx; + int idx = scpi_drvinfo->num_chans; + struct scpi_chan *pchan = scpi_drvinfo->channels + idx; struct mbox_client *cl = &pchan->cl; struct device_node *shmem = of_parse_phandle(np, "shmem", idx); @@ -986,45 +987,53 @@ static int scpi_probe(struct platform_device *pdev) return ret; } - scpi_info->commands = scpi_std_commands; + scpi_drvinfo->commands = scpi_std_commands; - platform_set_drvdata(pdev, scpi_info); + platform_set_drvdata(pdev, scpi_drvinfo); - if (scpi_info->is_legacy) { + if (scpi_drvinfo->is_legacy) { /* Replace with legacy variants */ scpi_ops.clk_set_val = legacy_scpi_clk_set_val; - scpi_info->commands = scpi_legacy_commands; + scpi_drvinfo->commands = scpi_legacy_commands; /* Fill priority bitmap */ for (idx = 0; idx < ARRAY_SIZE(legacy_hpriority_cmds); idx++) set_bit(legacy_hpriority_cmds[idx], - scpi_info->cmd_priority); + scpi_drvinfo->cmd_priority); } - ret = scpi_init_versions(scpi_info); + scpi_info = scpi_drvinfo; + + ret = scpi_init_versions(scpi_drvinfo); if (ret) { dev_err(dev, "incorrect or no SCP firmware found\n"); + scpi_info = NULL; return ret; } - if (scpi_info->is_legacy && !scpi_info->protocol_version && - !scpi_info->firmware_version) + if (scpi_drvinfo->is_legacy && !scpi_drvinfo->protocol_version && + !scpi_drvinfo->firmware_version) dev_info(dev, "SCP Protocol legacy pre-1.0 firmware\n"); else dev_info(dev, "SCP Protocol %lu.%lu Firmware %lu.%lu.%lu version\n", FIELD_GET(PROTO_REV_MAJOR_MASK, - scpi_info->protocol_version), + scpi_drvinfo->protocol_version), FIELD_GET(PROTO_REV_MINOR_MASK, - scpi_info->protocol_version), + scpi_drvinfo->protocol_version), FIELD_GET(FW_REV_MAJOR_MASK, - scpi_info->firmware_version), + scpi_drvinfo->firmware_version), FIELD_GET(FW_REV_MINOR_MASK, - scpi_info->firmware_version), + scpi_drvinfo->firmware_version), FIELD_GET(FW_REV_PATCH_MASK, - scpi_info->firmware_version)); - scpi_info->scpi_ops = &scpi_ops; + scpi_drvinfo->firmware_version)); + + scpi_drvinfo->scpi_ops = &scpi_ops; - return devm_of_platform_populate(dev); + ret = devm_of_platform_populate(dev); + if (ret) + scpi_info = NULL; + + return ret; } static const struct of_device_id scpi_of_match[] = {