Skip to content

Commit b3ac240

Browse files
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
1 parent e8927e1 commit b3ac240

File tree

2 files changed

+236
-0
lines changed

2 files changed

+236
-0
lines changed

.github/scripts/create-pr-body.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/bin/bash
2+
3+
# Script to create PR body
4+
# Arguments: build_time total_time passed failed run_id comparison_section
5+
6+
BUILD_TIME="$1"
7+
TOTAL_TIME="$2"
8+
PASSED="$3"
9+
FAILED="$4"
10+
RUN_ID="$5"
11+
COMPARISON_SECTION="$6"
12+
REPO="$7"
13+
14+
cat << EOF
15+
## Summary
16+
This PR has been automatically created after successful completion of all CI stages.
17+
18+
## Commit Message(s)
19+
\`\`\`
20+
EOF
21+
22+
cat /tmp/commit_message.txt
23+
24+
cat << EOF
25+
\`\`\`
26+
27+
## Test Results
28+
29+
### ✅ Build Stage
30+
- Status: Passed
31+
- Build Time: ${BUILD_TIME}
32+
- Total Time: ${TOTAL_TIME}
33+
- [View build logs](https://github.com/${REPO}/actions/runs/${RUN_ID})
34+
35+
### ✅ Boot Verification
36+
- Status: Passed
37+
- [View boot logs](https://github.com/${REPO}/actions/runs/${RUN_ID})
38+
39+
### ✅ Kernel Selftests
40+
- **Passed:** ${PASSED}
41+
- **Failed:** ${FAILED}
42+
- [View kselftest logs](https://github.com/${REPO}/actions/runs/${RUN_ID})
43+
44+
${COMPARISON_SECTION}
45+
46+
---
47+
🤖 This PR was automatically generated by GitHub Actions
48+
Run ID: ${RUN_ID}
49+
EOF

.github/workflows/kernel-build-and-test-x86_64.yml

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)