Skip to content

Commit 611594e

Browse files
committed
github actions: support PR-triggered kernelCI runs and forked repo workflows
Split the monolithic workflow into a trigger/runner pair to enable kernelCI on manually opened PRs from forks, where the PR context lacks access to repository secrets. - kernel-build-and-test-multiarch-trigger.yml: lightweight trigger that validates and saves PR metadata as a signed artifact; runs safely in the PR (fork) context - kernel-build-and-test-multiarch.yml: converted from workflow_call to workflow_run so it always executes in the base repo context with full secret access; also supports workflow_dispatch for manual testing PR creation behavior: - PRs created by kernelCI are labeled "created-by-kernelci"; subsequent pushes update the PR body only when this label is present - For manually created PRs, kernelCI appends results as a comment to avoid overwriting the original PR body - PR creation is restricted to push events only All metadata passed between workflows is validated against injection before use. Signed-off-by: Roxana Nicolescu <rnicolescu@ciq.com>
1 parent 47f1211 commit 611594e

File tree

2 files changed

+475
-64
lines changed

2 files changed

+475
-64
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
name: Trigger Automated kernel build and test (multi-arch)
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
architectures:
7+
description: 'Comma-separated architectures to build (x86_64, aarch64)'
8+
required: false
9+
type: string
10+
default: 'x86_64,aarch64'
11+
skip_kabi:
12+
description: 'Skip kABI compatibility check'
13+
required: false
14+
type: boolean
15+
default: false
16+
skip_kselftests:
17+
description: 'Skip the kselftests stage (e.g. for CBR where kselftest coverage is minimal)'
18+
required: false
19+
type: boolean
20+
default: false
21+
22+
permissions:
23+
contents: read
24+
actions: read
25+
packages: read
26+
pull-requests: read
27+
28+
jobs:
29+
trigger-kernelCI:
30+
runs-on: ubuntu-latest
31+
timeout-minutes: 120
32+
33+
steps:
34+
- name: Validate and sanitize inputs
35+
id: validate_inputs
36+
env:
37+
BASE_REF: ${{ github.base_ref }}
38+
HEAD_REF: ${{ github.head_ref }}
39+
PR_NUMBER: ${{ github.event.pull_request.number }}
40+
EVENT_NAME: ${{ github.event_name }}
41+
PUSH_REF: ${{ github.ref_name }}
42+
PUSH_SHA: ${{ github.sha }}
43+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44+
ARCHITECTURES: ${{ inputs.architectures }}
45+
SKIP_KABI: ${{ inputs.skip_kabi }}
46+
SKIP_KSELFTESTS: ${{ inputs.skip_kselftests }}
47+
COMMIT_MSG: ${{ github.event.head_commit.message }}
48+
PR_TITLE: ${{ github.event.pull_request.title }}
49+
run: |
50+
IS_PR=false
51+
if [[ "$EVENT_NAME" == "pull_request" ]]; then
52+
IS_PR=true
53+
fi
54+
55+
# Check for [skip ci] / [ci skip] in commit message or PR title
56+
if echo "$COMMIT_MSG" | grep -qiE '\[(skip ci|ci skip)\]' || \
57+
echo "$PR_TITLE" | grep -qiE '\[(skip ci|ci skip)\]'; then
58+
echo "⏭️ [skip ci] detected — skipping CI"
59+
echo "SKIP_CI=true" >> "$GITHUB_ENV"
60+
# Do not exit here — fall through so the artifact is still uploaded
61+
# with the skip sentinel, allowing the actual workflow to exit cleanly
62+
else
63+
echo "SKIP_CI=false" >> "$GITHUB_ENV"
64+
fi
65+
66+
# On push events there is no PR context; BASE_REF is empty string
67+
# It will be deduced automatically in a later worklow
68+
if [[ "$IS_PR" == "false" ]]; then
69+
BASE_REF=""
70+
HEAD_REF="$PUSH_REF"
71+
PR_NUMBER="0"
72+
73+
# Validate and export SHA early for push events
74+
if ! [[ "$PUSH_SHA" =~ ^[0-9a-f]{40}$ ]]; then
75+
echo "❌ Invalid SHA format: $PUSH_SHA"
76+
exit 1
77+
fi
78+
echo "HEAD_SHA=$PUSH_SHA" >> "$GITHUB_ENV"
79+
80+
# Check if this push is updating an existing open PR
81+
EXISTING_PR=$(gh pr list --repo "$GITHUB_REPOSITORY" --head "$HEAD_REF" --state open --json number,baseRefName --jq '.[0]' 2>/dev/null || echo "")
82+
if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
83+
PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number')
84+
BASE_REF=$(echo "$EXISTING_PR" | jq -r '.baseRefName')
85+
IS_PR=true
86+
echo "Found existing open PR #$PR_NUMBER for branch $HEAD_REF, base: $BASE_REF"
87+
fi
88+
fi
89+
90+
# Validate base branch name (alphanumeric, dots, slashes, dashes, underscores, curly braces)
91+
# Only if pull request is present
92+
if [[ "$IS_PR" == "true" ]]; then
93+
if ! [[ "$BASE_REF" =~ ^[a-zA-Z0-9/_.{}-]+$ ]]; then
94+
echo "❌ Invalid base branch name: $BASE_REF"
95+
exit 1
96+
fi
97+
98+
# Validate base branch name length
99+
if [ ${#BASE_REF} -gt 255 ]; then
100+
echo "❌ Base branch name too long"
101+
exit 1
102+
fi
103+
fi
104+
105+
# Validate head branch name
106+
if ! [[ "$HEAD_REF" =~ ^[a-zA-Z0-9/_.{}-]+$ ]]; then
107+
echo "❌ Invalid head branch name: $HEAD_REF"
108+
exit 1
109+
fi
110+
111+
# Validate head branch name length
112+
if [ ${#HEAD_REF} -gt 255 ]; then
113+
echo "❌ Head branch name too long"
114+
exit 1
115+
fi
116+
117+
# Validate PR number is numeric
118+
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
119+
echo "❌ Invalid PR number: $PR_NUMBER"
120+
exit 1
121+
fi
122+
123+
# Validate architectures - only allow the four valid combinations
124+
if ! [[ "$ARCHITECTURES" =~ ^(x86_64,aarch64|aarch64,x86_64|x86_64|aarch64)$ ]]; then
125+
echo "❌ Invalid architectures value: $ARCHITECTURES"
126+
exit 1
127+
fi
128+
129+
# Validate skip_kabi - must be exactly 'true' or 'false'
130+
if ! [[ "$SKIP_KABI" =~ ^(true|false)$ ]]; then
131+
echo "❌ Invalid skip_kabi value: $SKIP_KABI"
132+
exit 1
133+
fi
134+
135+
# Validate skip_kselftests - must be exactly 'true' or 'false'
136+
if ! [[ "$SKIP_KSELFTESTS" =~ ^(true|false)$ ]]; then
137+
echo "❌ Invalid skip_kselftests value: $SKIP_KSELFTESTS"
138+
exit 1
139+
fi
140+
141+
# Pass validated values to environment
142+
echo "IS_PR=$IS_PR" >> "$GITHUB_ENV"
143+
echo "BASE_REF=$BASE_REF" >> "$GITHUB_ENV"
144+
echo "HEAD_REF=$HEAD_REF" >> "$GITHUB_ENV"
145+
echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_ENV"
146+
echo "ARCHITECTURES=$ARCHITECTURES" >> "$GITHUB_ENV"
147+
echo "SKIP_KABI=$SKIP_KABI" >> "$GITHUB_ENV"
148+
echo "SKIP_KSELFTESTS=$SKIP_KSELFTESTS" >> "$GITHUB_ENV"
149+
150+
- name: Clone base branch
151+
if: github.event_name == 'pull_request'
152+
env:
153+
BASE_CLONE_URL: ${{ github.event.pull_request.base.repo.clone_url }}
154+
run: |
155+
# Use environment variables to prevent injection
156+
git clone --depth=1 --no-checkout "$BASE_CLONE_URL" -b "$BASE_REF" .
157+
158+
- name: Fetch PR branch
159+
if: github.event_name == 'pull_request'
160+
env:
161+
HEAD_CLONE_URL: ${{ github.event.pull_request.head.repo.clone_url }}
162+
run: |
163+
# Use environment variables to prevent command injection
164+
git fetch --depth=1 "$HEAD_CLONE_URL" "$HEAD_REF"
165+
HEAD_SHA=$(git rev-parse FETCH_HEAD)
166+
167+
# Validate SHA format (40 hex characters)
168+
if ! [[ "$HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then
169+
echo "❌ Invalid SHA format: $HEAD_SHA"
170+
exit 1
171+
fi
172+
173+
echo "HEAD_SHA=$HEAD_SHA" >> "$GITHUB_ENV"
174+
175+
- name: Save PR metadata for workflow
176+
env:
177+
REPOSITORY: ${{ github.repository }}
178+
179+
run: |
180+
mkdir -p pr_metadata
181+
182+
# Write skip sentinel — actual workflow will exit cleanly when true
183+
echo "$SKIP_CI" > pr_metadata/skip_ci.txt
184+
185+
if [[ "$SKIP_CI" == "true" ]]; then
186+
echo "⏭️ Writing skip sentinel only — no full metadata saved"
187+
else
188+
# Save validated metadata
189+
echo "$PR_NUMBER" > pr_metadata/pr_number.txt
190+
echo "$REPOSITORY" > pr_metadata/repository.txt
191+
echo "$BASE_REF" > pr_metadata/base_ref.txt
192+
echo "$HEAD_REF" > pr_metadata/head_ref.txt
193+
echo "$HEAD_SHA" > pr_metadata/head_sha.txt
194+
echo "$ARCHITECTURES" > pr_metadata/architectures.txt
195+
echo "$SKIP_KABI" > pr_metadata/skip_kabi.txt
196+
echo "$SKIP_KSELFTESTS" > pr_metadata/skip_kselftests.txt
197+
echo "$IS_PR" > pr_metadata/is_pr.txt
198+
199+
# Create a checksum of metadata for integrity verification
200+
(cd pr_metadata && sha256sum *.txt > checksums.txt)
201+
fi
202+
203+
- name: Upload check results
204+
uses: actions/upload-artifact@v4
205+
if: always() # Upload even if checks fail
206+
with:
207+
name: check-results
208+
path: |
209+
pr_metadata/
210+
retention-days: 3 # Increased from 1 (then 3) to prevent premature deletion and support manual follow-ups

0 commit comments

Comments
 (0)