Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ on:
branches-ignore:
# SapMachine 2020-11-04: Ignore sapmachine branch
- sapmachine17
# SapMachine 2020-11-04: Trigger on pull request
pull_request:
branches:
- sapmachine17
# SapMachine 2026-06-01: do not trigger github actions for docs files
paths-ignore:
- '**/*.md'
- 'doc/**'
workflow_dispatch:
inputs:
platforms:
Expand All @@ -60,8 +60,6 @@ jobs:

prepare:
name: 'Prepare the run'
# SapMachine 2022-06-23: On 'pull_request' we only want to run GHA if the PR comes from a remote repo. Otherwise we have the run on 'push' already as a check.
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name }}
runs-on: ubuntu-22.04
outputs:
linux-x64: ${{ steps.include.outputs.linux-x64 }}
Expand Down Expand Up @@ -98,8 +96,7 @@ jobs:
function check_platform() {
if [[ $GITHUB_EVENT_NAME == workflow_dispatch ]]; then
input='${{ github.event.inputs.platforms }}'
# SapMachine 2022-06-24: Also handle 'pull_request' event.
elif [[ $GITHUB_EVENT_NAME == push ]] || [[ $GITHUB_EVENT_NAME == pull_request ]]; then
elif [[ $GITHUB_EVENT_NAME == push ]]; then
if [[ '${{ !secrets.JDK_SUBMIT_FILTER || startsWith(github.ref, 'refs/heads/submit/') }}' == 'false' ]]; then
# If JDK_SUBMIT_FILTER is set, and this is not a "submit/" branch, don't run anything
>&2 echo 'JDK_SUBMIT_FILTER is set and not a "submit/" branch'
Expand Down
243 changes: 243 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
#
# Copyright (c) 2026 SAP SE. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#

# Fires in the upstream repo context when a fork PR is opened or updated.
# Polls the fork repo's Actions API for the "SapMachine GHA Sanity Checks"
# run on the PR head commit, then mirrors every individual job as a native
# Check Run on the upstream PR — matching the appearance of the fork's own
# checks tab. No submit branches are created; no CI is re-run upstream.
name: 'Pre-submit tests'

on:
pull_request_target:
types:
- opened
- synchronize
- reopened

permissions:
checks: write
pull-requests: read

jobs:
mirror:
name: 'Mirror fork CI result'
runs-on: ubuntu-24.04
# Only act on fork PRs — same-repo PRs get CI directly from main.yml.
if: github.event.pull_request.head.repo.full_name != github.repository
steps:
- name: 'Poll fork CI and mirror per-job results'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
FORK_REPO: ${{ github.event.pull_request.head.repo.full_name }}
UPSTREAM_REPO: ${{ github.repository }}
run: |
# ── Phase 1: Wait for the fork workflow run to appear ───────────────
# The fork may not have triggered its CI yet immediately after push.
MAX_WAIT=20
ATTEMPT=0
RUN_ID=""

while [[ $ATTEMPT -lt $MAX_WAIT ]]; do
ATTEMPT=$((ATTEMPT + 1))
echo "Waiting for fork workflow run (attempt ${ATTEMPT}/${MAX_WAIT})..."

RUN_ID=$(gh api \
"repos/${FORK_REPO}/actions/runs?head_sha=${HEAD_SHA}&per_page=10" \
--jq '.workflow_runs[] | select(.name == "SapMachine GHA Sanity Checks") | .id' \
2>/dev/null | head -1)

if [[ -n "$RUN_ID" && "$RUN_ID" != "null" ]]; then
echo "Found fork workflow run: ${RUN_ID}"
break
fi

sleep 60
done

if [[ -z "$RUN_ID" || "$RUN_ID" == "null" ]]; then
echo "No fork workflow run found within ${MAX_WAIT} minutes — giving up."
exit 1
fi

# ── Phase 2: Mirror each fork job as an upstream Check Run ──────────
# job name → upstream check run ID
declare -A JOB_CHECK_RUN_IDS
# job name → '1' once the check run has been finalized
declare -A JOB_DONE

MAX_POLL=50
POLL=0
OVERALL_CONCLUSION=""

while [[ $POLL -lt $MAX_POLL ]]; do
POLL=$((POLL + 1))
echo "Poll ${POLL}/${MAX_POLL} — syncing fork job statuses..."

JOBS_JSON=$(gh api \
"repos/${FORK_REPO}/actions/runs/${RUN_ID}/jobs?per_page=100" \
2>/dev/null)

if [[ -z "$JOBS_JSON" ]]; then
sleep 60
continue
fi

JOB_COUNT=$(echo "$JOBS_JSON" | jq '.jobs | length')

for i in $(seq 0 $((JOB_COUNT - 1))); do
JOB_NAME=$(echo "$JOBS_JSON" | jq -r ".jobs[$i].name")
JOB_STATUS=$(echo "$JOBS_JSON" | jq -r ".jobs[$i].status")
JOB_CONCLUSION=$(echo "$JOBS_JSON" | jq -r ".jobs[$i].conclusion")
JOB_URL=$(echo "$JOBS_JSON" | jq -r ".jobs[$i].html_url")

# Create a Check Run for this job the first time we see it.
if [[ -z "${JOB_CHECK_RUN_IDS[$JOB_NAME]}" ]]; then
echo " Creating check run: ${JOB_NAME}"
CR_ID=$(gh api \
-X POST \
"repos/${UPSTREAM_REPO}/check-runs" \
-f name="${JOB_NAME}" \
-f head_sha="${HEAD_SHA}" \
-f status="in_progress" \
-f output[title]="Waiting for fork job: ${JOB_NAME}" \
-f output[summary]="Polling fork repository for job '${JOB_NAME}' on commit ${HEAD_SHA}. [View job](${JOB_URL})" \
--jq '.id')
JOB_CHECK_RUN_IDS[$JOB_NAME]=$CR_ID
echo " → check run ID: ${CR_ID}"
fi

# Finalize any job that has completed since the last poll.
if [[ "$JOB_STATUS" == "completed" && -z "${JOB_DONE[$JOB_NAME]}" ]]; then
CR_ID="${JOB_CHECK_RUN_IDS[$JOB_NAME]}"
# Guard against null conclusion (e.g. cancelled mid-queue).
[[ -z "$JOB_CONCLUSION" || "$JOB_CONCLUSION" == "null" ]] && JOB_CONCLUSION="cancelled"

if [[ "$JOB_CONCLUSION" == "success" ]]; then
TITLE="Passed: ${JOB_NAME}"
SUMMARY="Job **${JOB_NAME}** passed on the fork. [View job](${JOB_URL})"
else
TITLE="${JOB_CONCLUSION^}: ${JOB_NAME}"
SUMMARY="Job **${JOB_NAME}** reported **${JOB_CONCLUSION}** on the fork. [View job](${JOB_URL})"
fi

gh api \
-X PATCH \
"repos/${UPSTREAM_REPO}/check-runs/${CR_ID}" \
-f status="completed" \
-f conclusion="${JOB_CONCLUSION}" \
-f output[title]="${TITLE}" \
-f output[summary]="${SUMMARY}" \
-f details_url="${JOB_URL}"

JOB_DONE[$JOB_NAME]=1
echo " ✓ Finalized ${JOB_NAME}: ${JOB_CONCLUSION}"
fi
done

# Check whether the overall run has finished.
RUN_JSON=$(gh api \
"repos/${FORK_REPO}/actions/runs/${RUN_ID}" \
--jq '{status: .status, conclusion: .conclusion}' \
2>/dev/null)

RUN_STATUS=$(echo "$RUN_JSON" | jq -r '.status')
OVERALL_CONCLUSION=$(echo "$RUN_JSON" | jq -r '.conclusion')

if [[ "$RUN_STATUS" == "completed" ]]; then
echo "Fork workflow run completed with conclusion: ${OVERALL_CONCLUSION}"
# Do one final job sync to catch any jobs that finished in this last window.
JOBS_JSON=$(gh api \
"repos/${FORK_REPO}/actions/runs/${RUN_ID}/jobs?per_page=100" \
2>/dev/null)
JOB_COUNT=$(echo "$JOBS_JSON" | jq '.jobs | length')
for i in $(seq 0 $((JOB_COUNT - 1))); do
JOB_NAME=$(echo "$JOBS_JSON" | jq -r ".jobs[$i].name")
JOB_STATUS=$(echo "$JOBS_JSON" | jq -r ".jobs[$i].status")
JOB_CONCLUSION=$(echo "$JOBS_JSON" | jq -r ".jobs[$i].conclusion")
JOB_URL=$(echo "$JOBS_JSON" | jq -r ".jobs[$i].html_url")
if [[ -z "${JOB_CHECK_RUN_IDS[$JOB_NAME]}" ]]; then
CR_ID=$(gh api \
-X POST \
"repos/${UPSTREAM_REPO}/check-runs" \
-f name="${JOB_NAME}" \
-f head_sha="${HEAD_SHA}" \
-f status="in_progress" \
-f output[title]="Waiting for fork job: ${JOB_NAME}" \
-f output[summary]="Polling fork repository for job '${JOB_NAME}' on commit ${HEAD_SHA}. [View job](${JOB_URL})" \
--jq '.id')
JOB_CHECK_RUN_IDS[$JOB_NAME]=$CR_ID
fi
if [[ "$JOB_STATUS" == "completed" && -z "${JOB_DONE[$JOB_NAME]}" ]]; then
CR_ID="${JOB_CHECK_RUN_IDS[$JOB_NAME]}"
[[ -z "$JOB_CONCLUSION" || "$JOB_CONCLUSION" == "null" ]] && JOB_CONCLUSION="cancelled"
if [[ "$JOB_CONCLUSION" == "success" ]]; then
TITLE="Passed: ${JOB_NAME}"
SUMMARY="Job **${JOB_NAME}** passed on the fork. [View job](${JOB_URL})"
else
TITLE="${JOB_CONCLUSION^}: ${JOB_NAME}"
SUMMARY="Job **${JOB_NAME}** reported **${JOB_CONCLUSION}** on the fork. [View job](${JOB_URL})"
fi
gh api \
-X PATCH \
"repos/${UPSTREAM_REPO}/check-runs/${CR_ID}" \
-f status="completed" \
-f conclusion="${JOB_CONCLUSION}" \
-f output[title]="${TITLE}" \
-f output[summary]="${SUMMARY}" \
-f details_url="${JOB_URL}"
JOB_DONE[$JOB_NAME]=1
echo " ✓ Finalized ${JOB_NAME}: ${JOB_CONCLUSION}"
fi
done
break
fi

sleep 60
done

# ── Phase 3: Handle timeout ─────────────────────────────────────────
if [[ -z "$OVERALL_CONCLUSION" || "$OVERALL_CONCLUSION" == "null" ]]; then
OVERALL_CONCLUSION="timed_out"
echo "Timed out — marking remaining open check runs."
for JOB_NAME in "${!JOB_CHECK_RUN_IDS[@]}"; do
if [[ -z "${JOB_DONE[$JOB_NAME]}" ]]; then
CR_ID="${JOB_CHECK_RUN_IDS[$JOB_NAME]}"
gh api \
-X PATCH \
"repos/${UPSTREAM_REPO}/check-runs/${CR_ID}" \
-f status="completed" \
-f conclusion="timed_out" \
-f output[title]="Timed out: ${JOB_NAME}" \
-f output[summary]="The polling window expired before job '${JOB_NAME}' completed."
fi
done
fi

# Exit non-zero so the upstream PR shows a red check if CI did not pass.
if [[ "$OVERALL_CONCLUSION" != "success" ]]; then
exit 1
fi
Loading