@@ -348,3 +348,190 @@ jobs:
348348 echo "comparison_status=skipped" >> $GITHUB_OUTPUT
349349 echo "comparison_message=No baseline results available from ${{ steps.base_branch.outputs.base_branch }}" >> $GITHUB_OUTPUT
350350 fi
351+ create-pr :
352+ name : Create Pull Request
353+ runs-on : kernel-build
354+ needs : [build, boot, test-kselftest, compare-results]
355+ if : success() || failure()
356+
357+ steps :
358+ - name : Check if all stages passed
359+ run : |
360+ if [ "${{ needs.build.result }}" != "success" ] || \
361+ [ "${{ needs.boot.result }}" != "success" ] || \
362+ [ "${{ needs.test-kselftest.result }}" != "success" ]; then
363+ echo "One or more test stages failed, skipping PR creation"
364+ exit 1
365+ fi
366+ echo "All test stages passed, proceeding with PR creation"
367+
368+ - name : Checkout kernel source
369+ uses : actions/checkout@v4
370+ with :
371+ fetch-depth : 0
372+ token : ${{ secrets.GITHUB_TOKEN }}
373+
374+ - name : Download kernel compilation logs
375+ uses : actions/download-artifact@v4
376+ with :
377+ name : kernel-compilation-logs-x86_64
378+ path : artifacts/build
379+
380+ - name : Download boot logs
381+ uses : actions/download-artifact@v4
382+ with :
383+ name : boot-logs-x86_64
384+ path : artifacts/boot
385+
386+ - name : Download kselftest logs
387+ uses : actions/download-artifact@v4
388+ with :
389+ name : kselftest-logs-x86_64
390+ path : artifacts/test
391+
392+ - name : Extract test statistics
393+ id : stats
394+ run : |
395+ PASSED=$(grep -a '^ok' artifacts/test/kselftests-*.log | wc -l || echo "0")
396+ FAILED=$(grep -a '^not ok' artifacts/test/kselftests-*.log | wc -l || echo "0")
397+ echo "passed=$PASSED" >> $GITHUB_OUTPUT
398+ echo "failed=$FAILED" >> $GITHUB_OUTPUT
399+
400+ - name : Extract build timers
401+ id : build_info
402+ run : |
403+ BUILD_TIME=$(grep -oP '\[TIMER\]\{BUILD\}:\s*\K[0-9]+' artifacts/build/kernel-build.log | head -1 || echo "N/A")
404+ TOTAL_TIME=$(grep -oP '\[TIMER\]\{TOTAL\}\s*\K[0-9]+' artifacts/build/kernel-build.log | head -1 || echo "N/A")
405+ echo "build_time=${BUILD_TIME}s" >> $GITHUB_OUTPUT
406+ echo "total_time=${TOTAL_TIME}s" >> $GITHUB_OUTPUT
407+
408+ - name : Get commit information
409+ id : commit_msg
410+ run : |
411+ # Count commits since origin/main (or appropriate base branch)
412+ BASE_BRANCH="main"
413+ if ! git rev-parse origin/$BASE_BRANCH >/dev/null 2>&1; then
414+ # Try other common base branch names
415+ for branch in master lts-9.2 lts-9; do
416+ if git rev-parse origin/$branch >/dev/null 2>&1; then
417+ BASE_BRANCH=$branch
418+ break
419+ fi
420+ done
421+ fi
422+
423+ COMMIT_COUNT=$(git rev-list --count origin/$BASE_BRANCH..HEAD 2>/dev/null || echo "1")
424+
425+ if [ "$COMMIT_COUNT" -eq "1" ]; then
426+ # Single commit: use commit subject
427+ git log -1 --pretty=%s > /tmp/commit_subject.txt
428+ COMMIT_SUBJECT=$(cat /tmp/commit_subject.txt)
429+ echo "commit_subject=$COMMIT_SUBJECT" >> $GITHUB_OUTPUT
430+
431+ # Save full commit message to file
432+ git log -1 --pretty=%B > /tmp/commit_message.txt
433+ else
434+ # Multiple commits: create summary
435+ echo "commit_subject=Multiple patches tested ($COMMIT_COUNT commits)" >> $GITHUB_OUTPUT
436+
437+ # Get all commit messages and save to file
438+ git log origin/$BASE_BRANCH..HEAD --pretty=format:"### %s%n%n%b%n---" > /tmp/commit_message.txt
439+ fi
440+
441+ - name : Create Pull Request
442+ env :
443+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
444+ run : |
445+ BASE_BRANCH=""
446+
447+ # For PRs, use the base branch directly
448+ if [ -n "${{ github.base_ref }}" ]; then
449+ BASE_BRANCH="${{ github.base_ref }}"
450+ echo "Using PR base branch: $BASE_BRANCH"
451+ else
452+ # For direct pushes, find the common ancestor branch
453+ echo "Not a PR, finding base branch via merge-base"
454+
455+ # Get all remote branches and find the one with closest common ancestor
456+ CURRENT_COMMIT=$(git rev-parse HEAD)
457+ BEST_BRANCH=""
458+ CLOSEST_DISTANCE=999999
459+
460+ for remote_branch in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin/ | grep -v 'HEAD\|PR-CHECKER'); do
461+ branch_name=$(echo "$remote_branch" | sed 's|^origin/||')
462+
463+ # Skip if it's the current branch
464+ if [ "$branch_name" = "${{ github.ref_name }}" ]; then
465+ continue
466+ fi
467+
468+ MERGE_BASE=$(git merge-base HEAD "$remote_branch" 2>/dev/null || echo "")
469+
470+ # Check if this branch shares history and isn't the current commit
471+ if [ -n "$MERGE_BASE" ] && [ "$MERGE_BASE" != "$CURRENT_COMMIT" ]; then
472+ # Calculate distance (number of commits) from merge-base to current HEAD
473+ DISTANCE=$(git rev-list --count ${MERGE_BASE}..HEAD 2>/dev/null || echo "999999")
474+
475+ # Find the branch with the shortest distance (most recent common ancestor)
476+ if [ "$DISTANCE" -lt "$CLOSEST_DISTANCE" ]; then
477+ CLOSEST_DISTANCE=$DISTANCE
478+ BEST_BRANCH=$branch_name
479+ fi
480+ fi
481+ done
482+
483+ if [ -n "$BEST_BRANCH" ]; then
484+ BASE_BRANCH=$BEST_BRANCH
485+ echo "Found common ancestor with $BASE_BRANCH (distance: $CLOSEST_DISTANCE commits)"
486+ fi
487+ fi
488+
489+ if [ -z "$BASE_BRANCH" ]; then
490+ echo "ERROR: Could not determine base branch for PR"
491+ exit 1
492+ fi
493+
494+ echo "Creating PR from ${{ github.ref_name }} to $BASE_BRANCH"
495+
496+ # Determine comparison status message
497+ COMPARISON_RESULT="${{ needs.compare-results.result }}"
498+ if [ "$COMPARISON_RESULT" = "success" ]; then
499+ COMPARISON_SECTION="### ✅ Test Comparison
500+ - Status: Passed - Within acceptable threshold (±3 tests)
501+ - Compared against: $BASE_BRANCH"
502+ else
503+ COMPARISON_SECTION="### ⚠️ Test Comparison
504+ - Status: Skipped
505+ - Reason: No baseline test results available from $BASE_BRANCH
506+ - **Note:** Manual review recommended to ensure no regressions"
507+ fi
508+
509+ # Create PR body using script
510+ chmod +x .github/scripts/create-pr-body.sh
511+ .github/scripts/create-pr-body.sh \
512+ "${{ steps.build_info.outputs.build_time }}" \
513+ "${{ steps.build_info.outputs.total_time }}" \
514+ "${{ steps.stats.outputs.passed }}" \
515+ "${{ steps.stats.outputs.failed }}" \
516+ "${{ github.run_id }}" \
517+ "$COMPARISON_SECTION" \
518+ "${{ github.repository }}" \
519+ > pr_body.md
520+
521+ # Check if PR already exists
522+ EXISTING_PR=$(gh pr list --head "${{ github.ref_name }}" --base "$BASE_BRANCH" --json number --jq '.[0].number' || echo "")
523+
524+ if [ -n "$EXISTING_PR" ]; then
525+ echo "PR #$EXISTING_PR already exists, updating it"
526+ gh pr edit "$EXISTING_PR" \
527+ --title "[${{ github.ref_name }}] ${{ steps.commit_msg.outputs.commit_subject }}" \
528+ --body-file pr_body.md
529+ else
530+ echo "Creating new PR from ${{ github.ref_name }} to $BASE_BRANCH"
531+ gh pr create \
532+ --base "$BASE_BRANCH" \
533+ --head "${{ github.ref_name }}" \
534+ --title "[${{ github.ref_name }}] ${{ steps.commit_msg.outputs.commit_subject }}" \
535+ --body-file pr_body.md \
536+ --label "automated,tested,ready-for-review"
537+ fi
0 commit comments