diff --git a/.github/workflows/validate-kernel-commits.yml b/.github/workflows/validate-kernel-commits.yml new file mode 100644 index 0000000000000..c039764238eba --- /dev/null +++ b/.github/workflows/validate-kernel-commits.yml @@ -0,0 +1,238 @@ +name: Validate Kernel Commits + +on: + workflow_call: + # No inputs needed - uses github context from caller + +permissions: + contents: read + pull-requests: write + +jobs: + validate-kernel-commits: + runs-on: ubuntu-latest + timeout-minutes: 120 + + steps: + - name: Checkout PR branch + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.head_ref }} + + - name: Checkout base branch + run: | + git fetch origin ${{ github.base_ref }}:${{ github.base_ref }} + + - name: Checkout kernel-src-tree-tools + uses: actions/checkout@v4 + with: + repository: ctrliq/kernel-src-tree-tools + ref: 'mainline' + path: kernel-src-tree-tools + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Run upstream fixes check + id: check-kernel-commits + working-directory: kernel-src-tree-tools + run: | + set +e # Don't exit on error, we want to capture the output + set -o pipefail # Capture exit code from python script, not tee + python3 check_kernel_commits.py \ + --repo .. \ + --pr_branch "${{ github.head_ref }}" \ + --base_branch "${{ github.base_ref }}" \ + --markdown \ + --check-cves | tee ../ckc_result.txt + EXIT_CODE=$? + + # Check if the script failed + if [ $EXIT_CODE -ne 0 ]; then + echo "❌ Kernel commits check failed with exit code $EXIT_CODE" + exit $EXIT_CODE + fi + + # Check for findings: + # 1. Verify the success message exists + # 2. If it exists, check if there are any OTHER lines (which would indicate issues) + # 3. If success message doesn't exist, that's also a finding + if grep -q "All referenced commits exist upstream and have no Fixes: tags." ../ckc_result.txt; then + # Success message found, check if there are any other lines + LINE_COUNT=$(wc -l < ../ckc_result.txt) + if [ "$LINE_COUNT" -gt 1 ]; then + echo "has_findings=true" >> $GITHUB_OUTPUT + else + echo "has_findings=false" >> $GITHUB_OUTPUT + fi + else + # Success message not found, there must be findings + echo "has_findings=true" >> $GITHUB_OUTPUT + fi + + set -e # Re-enable exit on error + + - name: Comment on PR if issues found + if: steps.check-kernel-commits.outputs.has_findings == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + if ! gh pr comment ${{ github.event.pull_request.number }} \ + --body-file ckc_result.txt \ + --repo ${{ github.repository }}; then + echo "❌ Failed to post check-kernel-commits comment to PR" + exit 1 + fi + + - name: Install build dependencies for patchutils + run: | + sudo apt-get update + sudo apt-get install -y build-essential autoconf automake libtool gnulib + + - name: Clone and build custom patchutils + run: | + git clone https://github.com/kerneltoast/patchutils.git --depth=1 --revision=32e5f1df96920f1d24beb910346f01acab8b0bd8 + cd patchutils + ./bootstrap + ./configure + make -j$(nproc) + + - name: Run interdiff check + id: interdiff + working-directory: kernel-src-tree-tools + run: | + set +e # Don't exit on error, we want to capture the output + set -o pipefail # Capture exit code from python script, not tee + python3 run_interdiff.py \ + --repo .. \ + --pr_branch "${{ github.head_ref }}" \ + --base_branch "${{ github.base_ref }}" \ + --markdown \ + --interdiff ../patchutils/src/interdiff | tee ../interdiff_result.txt + EXIT_CODE=$? + + # Check if the script failed + if [ $EXIT_CODE -ne 0 ]; then + echo "❌ Interdiff check failed with exit code $EXIT_CODE" + exit $EXIT_CODE + fi + + # Check for differences: + # 1. Verify the success message exists + # 2. If it exists, check if there are any OTHER lines (which would indicate differences) + # 3. If success message doesn't exist, that's also a difference + if grep -q "All backported commits match their upstream counterparts." ../interdiff_result.txt; then + # Success message found, check if there are any other lines + LINE_COUNT=$(wc -l < ../interdiff_result.txt) + if [ "$LINE_COUNT" -gt 1 ]; then + echo "has_differences=true" >> $GITHUB_OUTPUT + else + echo "has_differences=false" >> $GITHUB_OUTPUT + fi + else + # Success message not found, there must be differences + echo "has_differences=true" >> $GITHUB_OUTPUT + fi + + set -e # Re-enable exit on error + + - name: Comment on PR if interdiff differences found + if: steps.interdiff.outputs.has_differences == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + if ! gh pr comment ${{ github.event.pull_request.number }} \ + --body-file interdiff_result.txt \ + --repo ${{ github.repository }}; then + echo "❌ Failed to post interdiff comment to PR" + exit 1 + fi + + - name: Install JIRA PR Check dependencies + run: | + python -m pip install --upgrade pip + pip install jira + + - name: Mask JIRA credentials + run: | + echo "::add-mask::${{ secrets.JIRA_API_TOKEN }}" + echo "::add-mask::${{ secrets.JIRA_API_USER }}" + echo "::add-mask::${{ secrets.JIRA_URL }}" + + - name: Run JIRA PR Check + id: jira_check + continue-on-error: true # Allow PR comments to be posted before failing workflow + env: + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + JIRA_API_USER: ${{ secrets.JIRA_API_USER }} + JIRA_URL: ${{ secrets.JIRA_URL }} + working-directory: kernel-src-tree-tools + run: | + # Run script and capture output, ensuring credentials are never echoed + set +x # Disable command echo to prevent credential exposure + set +e # Don't exit on error, we want to capture the output + OUTPUT=$(python3 jira_pr_check.py \ + --kernel-src-tree .. \ + --merge-target ${{ github.base_ref }} \ + --pr-branch ${{ github.head_ref }} 2>&1) + EXIT_CODE=$? + + # Filter out any potential credential leaks from output + FILTERED_OUTPUT=$(echo "$OUTPUT" | grep -v "jira-user\|jira-key\|basic_auth\|Authorization\|$JIRA_API_TOKEN") + + echo "$FILTERED_OUTPUT" + echo "output<<'EOF'" >> $GITHUB_OUTPUT + echo "$FILTERED_OUTPUT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Check if there are any issues based on output patterns + if echo "$FILTERED_OUTPUT" | grep -q "❌ Errors:"; then + echo "has_issues=true" >> $GITHUB_OUTPUT + + # Check specifically for LTS mismatch errors + if echo "$FILTERED_OUTPUT" | grep -q "expects branch"; then + echo "has_lts_mismatch=true" >> $GITHUB_OUTPUT + else + echo "has_lts_mismatch=false" >> $GITHUB_OUTPUT + fi + elif echo "$FILTERED_OUTPUT" | grep -q "⚠️ Warnings:"; then + echo "has_issues=true" >> $GITHUB_OUTPUT + echo "has_lts_mismatch=false" >> $GITHUB_OUTPUT + else + echo "has_issues=false" >> $GITHUB_OUTPUT + echo "has_lts_mismatch=false" >> $GITHUB_OUTPUT + fi + + # Exit with the script's exit code + exit $EXIT_CODE + + - name: Comment PR with JIRA issues + if: steps.jira_check.outputs.has_issues == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + if ! gh pr comment ${{ github.event.pull_request.number }} \ + --body "${{ steps.jira_check.outputs.output }}" \ + --repo ${{ github.repository }}; then + echo "❌ Failed to post JIRA check comment to PR" + exit 1 + fi + + - name: Request changes if LTS mismatch + if: steps.jira_check.outputs.has_lts_mismatch == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh pr review ${{ github.event.pull_request.number }} \ + --request-changes \ + --body "⚠️ This PR contains VULN tickets that do not match the target LTS product. Please review the JIRA ticket assignments and ensure they match the merge target branch." \ + --repo ${{ github.repository }} + + - name: Fail workflow if JIRA errors found + if: steps.jira_check.outcome == 'failure' + run: | + echo "❌ JIRA PR check failed - errors were found in one or more commits" + exit 1