Skip to content

Commit 971b5ac

Browse files
committed
github actions: Add upstream commit checker
This github actions checks the PR commits for references to upstream linux commits (lines starting with "commit <hash>") and does two things: 1. Checks that this hash exists in the upstream linux kernel history 2. Checks if there are any Fixes: references for the referenced commit in the upstream linux kernel history If either of those are found to be true a comment is added to the PR with the pertinent information
1 parent e881f08 commit 971b5ac

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
name: Check Upstream Linux Kernel Commits
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- '**'
7+
- '!mainline'
8+
9+
jobs:
10+
check_upstream_commits:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Install jq
15+
run: |
16+
sudo apt-get update && sudo apt-get install -y jq
17+
18+
- name: Clone Linux Kernel
19+
run: |
20+
echo "Cloning Linux kernel repository (bare clone, single branch, no blobs) into 'linux' directory..."
21+
git clone --bare --filter=blob:none --single-branch https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git linux
22+
echo "Cloning complete."
23+
24+
- name: Get PR commit SHAs via GitHub API
25+
id: pr_commits
26+
env:
27+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Required for GitHub API authentication
28+
run: |
29+
PR_COMMITS_JSON=$(curl -sS -H "Accept: application/vnd.github.v3+json" \
30+
-H "Authorization: token $GITHUB_TOKEN" \
31+
"${{ github.event.pull_request.commits_url }}?per_page=100")
32+
33+
ERROR_MESSAGE=$(echo "$PR_COMMITS_JSON" | jq -r 'if type == "object" and .message then .message else null end')
34+
if [ "$ERROR_MESSAGE" != "null" ] && [ -n "$ERROR_MESSAGE" ]; then
35+
echo "ERROR: Failed to retrieve PR commits from GitHub API: $ERROR_MESSAGE"
36+
echo "API Response snippet: $(echo "$PR_COMMITS_JSON" | head -n 5)"
37+
exit 1
38+
fi
39+
40+
COMMITS=$(echo "$PR_COMMITS_JSON" | jq -r 'map(.sha) | join(" ")')
41+
42+
if [ -z "$COMMITS" ]; then
43+
echo "No commits found in this Pull Request via GitHub API. This might indicate an issue with the PR or API."
44+
exit 1
45+
fi
46+
47+
echo "PR_COMMITS=${COMMITS}" >> "$GITHUB_OUTPUT"
48+
49+
- name: Check each PR commit for Linux upstream hash and related Fixes tag
50+
id: check_results
51+
env:
52+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53+
LINUX_KERNEL_REPO_OWNER: torvalds
54+
LINUX_KERNEL_REPO_NAME: linux
55+
run: |
56+
PR_COMMENT_BODY_ACCUMULATOR=""
57+
58+
# Get the current HEAD commit SHA for the Linux kernel master branch from the local clone
59+
echo "Getting current HEAD of '${LINUX_KERNEL_REPO_OWNER}/${LINUX_KERNEL_REPO_NAME}' master branch from local bare clone..."
60+
61+
HEAD_COMMIT_SHA=$(git -C linux rev-parse master)
62+
63+
if [ -z "$HEAD_COMMIT_SHA" ]; then
64+
echo "ERROR: Could not retrieve HEAD commit for Linux kernel master from local clone."
65+
exit 1
66+
fi
67+
68+
echo "Linux kernel master HEAD: ${HEAD_COMMIT_SHA}"
69+
70+
71+
# Loop through each commit SHA identified in the current PR
72+
for PR_COMMIT_SHA in ${{ steps.pr_commits.outputs.PR_COMMITS }}; do
73+
echo "--- Checking PR commit: ${PR_COMMIT_SHA} ---"
74+
75+
# --- Fetch the full commit message of the PR commit via GitHub API ---
76+
PR_COMMIT_DETAILS_JSON=$(curl -sS -H "Accept: application/vnd.github.v3+json" \
77+
-H "Authorization: token $GITHUB_TOKEN" \
78+
"https://api.github.com/repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/commits/${PR_COMMIT_SHA}")
79+
80+
ERROR_MESSAGE=$(echo "$PR_COMMIT_DETAILS_JSON" | jq -r 'if type == "object" and .message then .message else null end')
81+
if [ "$ERROR_MESSAGE" != "null" ] && [ -n "$ERROR_MESSAGE" ]; then
82+
echo "ERROR: Could not retrieve commit message for PR commit ${PR_COMMIT_SHA} from GitHub API: ${ERROR_MESSAGE}"
83+
echo "API Response snippet: $(echo "$PR_COMMIT_DETAILS_JSON" | head -n 5)"
84+
exit 1
85+
fi
86+
87+
PR_COMMIT_MESSAGE=$(echo "$PR_COMMIT_DETAILS_JSON" | jq -r '.commit.message')
88+
# Extract the subject (first line) of the PR commit message
89+
PR_COMMIT_SUBJECT=$(echo "$PR_COMMIT_MESSAGE" | head -n 1)
90+
91+
# Extract the upstream Linux commit hash from the PR commit message.
92+
UPSTREAM_LINUX_HASH=$(echo "$PR_COMMIT_MESSAGE" | grep -Eo "^commit [0-9a-f]{40}$" | awk '{print $2}')
93+
94+
if [ -z "$UPSTREAM_LINUX_HASH" ]; then
95+
echo "No 'commit <upstream_linux_commit_hash>' line found in PR commit ${PR_COMMIT_SHA}. Skipping upstream check for this commit."
96+
continue # Skip to next PR commit, no comment for this scenario
97+
fi
98+
99+
echo "Found upstream Linux hash to check for fixes: ${UPSTREAM_LINUX_HASH}"
100+
101+
# --- Check if the upstream hash exists in the local cloned Linux kernel repo ---
102+
if ! git -C linux cat-file -e "$UPSTREAM_LINUX_HASH"; then
103+
printf -v PR_COMMENT_BODY_ACCUMULATOR "%s- **PR commit \`%s\` - %s**: References upstream commit \`%s\` which was **NOT found** in the Linux kernel repository. Please verify the hash.\n" \
104+
"$PR_COMMENT_BODY_ACCUMULATOR" "$PR_COMMIT_SHA" "$PR_COMMIT_SUBJECT" "$UPSTREAM_LINUX_HASH"
105+
continue # Skip to next PR commit, but added message to comment
106+
fi
107+
108+
# --- Search for "Fixes:" tag in upstream Linux kernel using git log ---
109+
# Extract the first 12 characters for the short SHA, commonly used in Fixes: tags in Linux kernel.
110+
UPSTREAM_LINUX_HASH_SHORT=$(echo "$UPSTREAM_LINUX_HASH" | cut -c 1-12)
111+
112+
echo "Searching for upstream commits on 'master' branch in range ${UPSTREAM_LINUX_HASH}..${HEAD_COMMIT_SHA} that fix ${UPSTREAM_LINUX_HASH} using 'git log --grep=\"Fixes: ${UPSTREAM_LINUX_HASH_SHORT}\"'..."
113+
114+
# Construct the grep pattern using the SHORT SHA for search.
115+
GREP_PATTERN="Fixes: ${UPSTREAM_LINUX_HASH_SHORT}"
116+
117+
# Use git log to find commits that mention the short SHA in their "Fixes:" line,
118+
# in the range from the specific commit up to HEAD.
119+
# --pretty=format:"%h %s" to get short SHA and subject.
120+
# --regexp-ignore-case for case-insensitive grep.
121+
# We explicitly exclude the UPSTREAM_LINUX_HASH itself from the result range,
122+
# as we are looking for *subsequent* fixes.
123+
GIT_LOG_FIXES_OUTPUT=$(git -C linux log master \
124+
"${UPSTREAM_LINUX_HASH}..${HEAD_COMMIT_SHA}" \
125+
--grep="${GREP_PATTERN}" \
126+
--pretty=format:"%h %s" \
127+
--regexp-ignore-case)
128+
129+
if [ -n "$GIT_LOG_FIXES_OUTPUT" ]; then
130+
printf -v PR_COMMENT_BODY_ACCUMULATOR "%s- **PR commit \`%s\` - %s**: References upstream commit \`%s\` which has the following fixes in the Linux kernel log:\n" \
131+
"$PR_COMMENT_BODY_ACCUMULATOR" "$PR_COMMIT_SHA" "$PR_COMMIT_SUBJECT" "$UPSTREAM_LINUX_HASH"
132+
# Add a markdown code block for the git log output
133+
printf -v PR_COMMENT_BODY_ACCUMULATOR "%s\`\`\`\n%s\n\`\`\`\n" \
134+
"$PR_COMMENT_BODY_ACCUMULATOR" "$GIT_LOG_FIXES_OUTPUT"
135+
else
136+
echo "No upstream fixes found for ${UPSTREAM_LINUX_HASH} in the specified range."
137+
# No comment added to PR_COMMENT_BODY_ACCUMULATOR for this scenario
138+
fi
139+
echo "" # Newline in action logs for separation
140+
done # End of for PR_COMMIT_SHA loop
141+
142+
# Set the output variable `PR_COMMENT_BODY` using EOF to preserve newlines for the PR comment.
143+
# If no relevant messages were accumulated, this will be an empty string (after trim in JS).
144+
echo "PR_COMMENT_BODY<<EOF" >> "$GITHUB_OUTPUT"
145+
echo "$PR_COMMENT_BODY_ACCUMULATOR" >> "$GITHUB_OUTPUT"
146+
echo "EOF" >> "$GITHUB_OUTPUT"
147+
148+
149+
- name: Post PR comment with results # Step 3: Post the collected results as a comment on the PR
150+
uses: actions/github-script@v7
151+
env:
152+
# Pass the multi-line comment body as an environment variable to the JavaScript script.
153+
COMMENT_BODY: ${{ steps.check_results.outputs.PR_COMMENT_BODY }}
154+
with:
155+
script: |
156+
// Access the comment body directly from the environment variable and trim any whitespace
157+
const commentBody = process.env.COMMENT_BODY.trim();
158+
159+
// Only post a comment if there is actual content
160+
if (commentBody) {
161+
await github.rest.issues.createComment({
162+
issue_number: context.issue.number,
163+
owner: context.repo.owner,
164+
repo: context.repo.repo,
165+
body: commentBody
166+
});
167+
console.log("Posted a PR comment with the results.");
168+
} else {
169+
console.log("No relevant upstream fixes information to post as a PR comment.");
170+
}

0 commit comments

Comments
 (0)