feat: publish review check on reviewed HEAD

Adds a dedicated `review` check for the exact pull request HEAD
Keeps manual review commands reported separately from the default branch job
Documents the new review check behavior in contributing guidelines
This commit is contained in:
Bohdan Triapitsyn
2026-07-22 10:21:11 +03:00
parent 1c7cdf68e3
commit a62731bd6e
2 changed files with 130 additions and 10 deletions
+125 -10
View File
@@ -17,12 +17,16 @@ concurrency:
jobs:
review:
# Manual comment workflows run on the default branch, so their native job
# check cannot represent the reviewed PR HEAD. Publish that check explicitly.
name: automation
if: |
github.event_name == 'pull_request_target' ||
(github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.user.login != 'openchamber-bot[bot]' && (github.event.comment.body == '/oc-review' || startsWith(github.event.comment.body, '/oc-review ') || github.event.comment.body == '@openchamber-bot review' || startsWith(github.event.comment.body, '@openchamber-bot review '))) ||
(github.event_name == 'pull_request_review_comment' && github.event.comment.user.login != 'openchamber-bot[bot]' && (github.event.comment.body == '/oc-review' || startsWith(github.event.comment.body, '/oc-review ') || github.event.comment.body == '@openchamber-bot review' || startsWith(github.event.comment.body, '@openchamber-bot review ')))
runs-on: ubuntu-latest
permissions:
checks: write
contents: read
pull-requests: write
issues: write
@@ -32,21 +36,17 @@ jobs:
with:
fetch-depth: 1
- name: Generate review app token
id: app-token
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2
with:
app-id: ${{ secrets.OC_REVIEW_APP_ID }}
private-key: ${{ secrets.OC_REVIEW_APP_PRIVATE_KEY }}
- name: Resolve pull request context
id: pr
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
GH_TOKEN: ${{ github.token }}
EVENT_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
run: |
pr_json="$(gh pr view "$EVENT_PR_NUMBER" --json number,url,author,baseRefName,headRefName,headRefOid,headRepositoryOwner,isDraft)"
echo "number=$(printf '%s' "$pr_json" | jq -r '.number')" >> "$GITHUB_OUTPUT"
{
echo "number=$(printf '%s' "$pr_json" | jq -r '.number')"
echo "head_sha=$(printf '%s' "$pr_json" | jq -r '.headRefOid')"
} >> "$GITHUB_OUTPUT"
if [ "$(printf '%s' "$pr_json" | jq -r '.isDraft')" = "true" ]; then
echo "draft=true" >> "$GITHUB_OUTPUT"
@@ -59,10 +59,43 @@ jobs:
echo "author=$(printf '%s' "$pr_json" | jq -r '.author.login')"
echo "base_ref=$(printf '%s' "$pr_json" | jq -r '.baseRefName')"
echo "head_ref=$(printf '%s' "$pr_json" | jq -r '.headRefName')"
echo "head_sha=$(printf '%s' "$pr_json" | jq -r '.headRefOid')"
echo "head_repo_owner=$(printf '%s' "$pr_json" | jq -r '.headRepositoryOwner.login')"
} >> "$GITHUB_OUTPUT"
- name: Start review check
id: review-check
env:
GH_TOKEN: ${{ github.token }}
REVIEW_HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
run: |
check_run="$(jq -n \
--arg head_sha "$REVIEW_HEAD_SHA" \
--arg details_url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
'{
name: "review",
head_sha: $head_sha,
status: "in_progress",
details_url: $details_url,
output: {
title: "OpenChamber review in progress",
summary: "Reviewing the current pull request HEAD."
}
}')"
check_id="$(printf '%s' "$check_run" | gh api \
--method POST \
"repos/${GITHUB_REPOSITORY}/check-runs" \
--input - \
--jq '.id')"
echo "id=$check_id" >> "$GITHUB_OUTPUT"
- name: Generate review app token
id: app-token
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2
with:
app-id: ${{ secrets.OC_REVIEW_APP_ID }}
private-key: ${{ secrets.OC_REVIEW_APP_PRIVATE_KEY }}
- name: Clear review status for draft
if: steps.pr.outputs.draft == 'true'
env:
@@ -246,6 +279,7 @@ jobs:
Required reviewed HEAD: $REVIEW_HEAD_SHA"
- name: Verify and enforce review verdict
id: verdict
if: always() && steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
@@ -366,3 +400,84 @@ jobs:
echo "Review verdict is $verdict; only pass satisfies this check." >&2
exit 1
fi
- name: Publish review check
if: always() && steps.review-check.outputs.id != ''
env:
GH_TOKEN: ${{ github.token }}
CHECK_RUN_ID: ${{ steps.review-check.outputs.id }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
REVIEW_HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
DRAFT: ${{ steps.pr.outputs.draft }}
VERDICT_OUTCOME: ${{ steps.verdict.outcome }}
run: |
current_head="$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')"
review_label="$(gh pr view "$PR_NUMBER" --json labels --jq '[.labels[].name | select(startswith("review:"))] | first // ""')"
set_review_status() {
local target_label="$1"
local remove_args=()
while IFS= read -r label; do
case "$label" in
review:*) remove_args+=(--remove-label "$label") ;;
esac
done < <(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name')
if [ -n "$target_label" ]; then
gh pr edit "$PR_NUMBER" "${remove_args[@]}" --add-label "$target_label"
elif [ "${#remove_args[@]}" -gt 0 ]; then
gh pr edit "$PR_NUMBER" "${remove_args[@]}"
fi
review_label="$target_label"
}
if [ "$current_head" = "$REVIEW_HEAD_SHA" ]; then
if [ "$DRAFT" = "true" ]; then
set_review_status ""
elif [ "$VERDICT_OUTCOME" != "success" ]; then
case "$review_label" in
review:needs-evidence|review:blocked|review:human-required|review:automation-failed) ;;
*) set_review_status "review:automation-failed" ;;
esac
fi
fi
if [ "$current_head" != "$REVIEW_HEAD_SHA" ]; then
conclusion="cancelled"
title="Review superseded by a newer HEAD"
summary="The pull request HEAD moved before this review completed."
elif [ "$DRAFT" = "true" ]; then
conclusion="neutral"
title="Review skipped for draft pull request"
summary="Mark the pull request ready for review to start the readiness check."
elif [ "$VERDICT_OUTCOME" = "success" ] && [ "$review_label" = "review:ready" ]; then
conclusion="success"
title="OpenChamber review passed"
summary="The reviewed HEAD is ready for maintainer review."
else
conclusion="failure"
title="OpenChamber review did not pass"
summary="Current readiness state: ${review_label:-review:automation-failed}."
fi
check_run="$(jq -n \
--arg conclusion "$conclusion" \
--arg title "$title" \
--arg summary "$summary" \
--arg details_url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
'{
status: "completed",
conclusion: $conclusion,
details_url: $details_url,
output: {
title: $title,
summary: $summary
}
}')"
printf '%s' "$check_run" | gh api \
--method PATCH \
"repos/${GITHUB_REPOSITORY}/check-runs/${CHECK_RUN_ID}" \
--input - >/dev/null
+5
View File
@@ -219,6 +219,11 @@ the previous readiness label before it starts, and only `review:ready` means
the pull request is ready to enter the maintainer review queue. Draft pull
requests have no readiness label.
The workflow publishes a separate `review` check on the exact reviewed HEAD;
only `review:ready` passes it. The `automation` job reports workflow execution
independently so a manual `/oc-review` result can update readiness without
being attached to the default-branch commit that triggered the command.
Each completed review creates a new comment tied to its reviewed HEAD so the
conversation remains chronological. Previous review comments are not rewritten.