From 346bd1996aa7d3f8d00199532f28eed33bb67e8c Mon Sep 17 00:00:00 2001 From: "lightning-it-shared-assets-sync[bot]" <307342877+lightning-it-shared-assets-sync[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2026 06:52:27 +0000 Subject: [PATCH] chore: sync repository quality assets Shared-Assets-Source-SHA: 571b2cc4ef39809576e4abf4ab6e8a13d460c934 Shared-Assets-Source-Run: 32699067967 Shared-Assets-Source-Attempt: 1 Shared-Assets-Sync-App-ID: 4351516 --- .github/workflows/copilot-review-refresh.yml | 202 +++++++- .github/workflows/promote-develop-to-main.yml | 436 +++++++++++++++++- 2 files changed, 591 insertions(+), 47 deletions(-) diff --git a/.github/workflows/copilot-review-refresh.yml b/.github/workflows/copilot-review-refresh.yml index 834f8f6..548aec6 100644 --- a/.github/workflows/copilot-review-refresh.yml +++ b/.github/workflows/copilot-review-refresh.yml @@ -67,6 +67,9 @@ jobs: BASE_SHA: ${{ github.event.pull_request.base.sha }} CONTROLLER_SHA: ${{ github.workflow_sha }} EVENT_ACTION: ${{ github.event.action }} + EVENT_COMMENT_CREATED_AT: ${{ github.event.comment.created_at }} + EVENT_COMMENT_ID: ${{ github.event.comment.id }} + EVENT_COMMENT_UPDATED_AT: ${{ github.event.comment.updated_at }} EVENT_NAME: ${{ github.event_name }} GH_TOKEN: ${{ github.token }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} @@ -307,37 +310,180 @@ jobs: exit 0 fi - # Before a neutral result exists, maintainer thread/review activity - # has nothing to invalidate. It must stay a successful no-op and - # must never become an alternate authorization signal. Only the - # exact submitted Copilot review below may recover a late result. - if [ "${EVENT_NAME}" != pull_request_review ] || - [ "${EVENT_ACTION}" != submitted ] || - [ "${GITHUB_ACTOR}" != Copilot ]; then + rerun_reason='' + trigger_occurred_at='' + if [ "${EVENT_NAME}" = pull_request_review ] \ + && [ "${EVENT_ACTION}" = submitted ] \ + && [ "${GITHUB_ACTOR}" = Copilot ]; then + # GitHub records the Copilot App as actor/triggering_actor + # `Copilot`, while the Review API login below is the distinct + # `copilot-pull-request-reviewer[bot]`. Keep both bindings. + # Live provenance: Actions run 32602223567 on 2026-08-22. + rerun_reason=delayed-review + elif [ "${EVENT_NAME}" = pull_request_review_comment ] \ + && [ "${EVENT_ACTION}" = created ]; then + # A maintainer evidence reply may recover a verifier that exhausted + # its bounded thread-resolution wait. The reply must target the + # exact current-head Copilot finding, every Copilot finding on that + # head must already be resolved, and the rerun never requests AI. + # Event author association is only a trigger prefilter: COLLABORATOR + # can mean read or triage. Authorize only a live repository role + # whose effective permission includes push. + [[ "${GITHUB_ACTOR}" =~ ^[A-Za-z0-9]([A-Za-z0-9-]{0,37}[A-Za-z0-9])?$ ]] + actor_permission="$(gh api \ + "repos/${REPOSITORY}/collaborators/${GITHUB_ACTOR}/permission")" + jq -e ' + (.permission | type == "string") + and (.role_name | type == "string" and length > 0) + and (.user.login | type == "string" and length > 0) + and .user.type == "User" + and (.user.permissions.push | type == "boolean") + ' <<<"${actor_permission}" >/dev/null + if ! jq -e \ + --arg actor "${GITHUB_ACTOR}" ' + (.user.login | ascii_downcase) == ($actor | ascii_downcase) + and .user.permissions.push == true + and (.permission == "write" + or .permission == "maintain" + or .permission == "admin") + ' <<<"${actor_permission}" >/dev/null; then + echo "Review-comment actor lacks current write permission; no rerun is authorized." + exit 0 + fi + [[ "${EVENT_COMMENT_ID}" =~ ^[1-9][0-9]*$ ]] + test -n "${EVENT_COMMENT_CREATED_AT}" + test "${EVENT_COMMENT_CREATED_AT}" = "${EVENT_COMMENT_UPDATED_AT}" + comment="$(gh api \ + "repos/${REPOSITORY}/pulls/comments/${EVENT_COMMENT_ID}")" + jq -e \ + --arg actor "${GITHUB_ACTOR}" \ + --arg created_at "${EVENT_COMMENT_CREATED_AT}" \ + --argjson comment_id "${EVENT_COMMENT_ID}" ' + .id == $comment_id + and .user.login == $actor + and .created_at == $created_at + and .updated_at == $created_at + and (.commit_id | type == "string" + and test("^[0-9a-f]{40}$")) + and has("in_reply_to_id") + ' <<<"${comment}" >/dev/null + if ! jq -e \ + --arg head "${HEAD_SHA}" ' + .commit_id == $head + and (.in_reply_to_id | type == "number" and . > 0) + ' <<<"${comment}" >/dev/null; then + echo "Review comment is not an exact current-head evidence reply; no rerun is authorized." + exit 0 + fi + parent_id="$(jq -er \ + '.in_reply_to_id | select(type == "number" and . > 0)' \ + <<<"${comment}")" + parent="$(gh api \ + "repos/${REPOSITORY}/pulls/comments/${parent_id}")" + jq -e \ + --argjson parent_id "${parent_id}" ' + .id == $parent_id + and (.user.login | type == "string" and length > 0) + and (.user.type | type == "string" and length > 0) + and (.commit_id | type == "string" + and test("^[0-9a-f]{40}$")) + and (.path | type == "string" and length > 0) + and (.pull_request_review_id | type == "number" and . > 0) + ' <<<"${parent}" >/dev/null + if ! jq -e \ + --arg head "${HEAD_SHA}" \ + --arg path "$(jq -r .path <<<"${comment}")" ' + (.user.login == "Copilot" + or .user.login == "copilot-pull-request-reviewer" + or .user.login == "copilot-pull-request-reviewer[bot]") + and .user.type == "Bot" + and .commit_id == $head + and .path == $path + ' <<<"${parent}" >/dev/null; then + echo "Review reply does not target an exact current-head Copilot finding; no rerun is authorized." + exit 0 + fi + REVIEW_ID="$(jq -er \ + '.pull_request_review_id | select(type == "number" and . > 0)' \ + <<<"${parent}")" + + read -r owner name <<<"${REPOSITORY//\// }" + # shellcheck disable=SC2016 # GraphQL variables stay literal. + resolved_query='query($owner:String!,$name:String!,$number:Int!,$after:String){repository(owner:$owner,name:$name){pullRequest(number:$number){headRefOid reviewThreads(first:100,after:$after){pageInfo{hasNextPage endCursor} nodes{isResolved comments(first:100){pageInfo{hasNextPage} nodes{databaseId author{login} pullRequestReview{databaseId commit{oid}}}}}}}}}' + threads='[]' + after='' + while true; do + arguments=(-f query="${resolved_query}" -F owner="${owner}" \ + -F name="${name}" -F number="${PR_NUMBER}") + if [ -n "${after}" ]; then arguments+=(-f after="${after}"); fi + page="$(gh api graphql "${arguments[@]}")" + jq -e --arg head "${HEAD_SHA}" \ + '.data.repository.pullRequest.headRefOid == $head' \ + <<<"${page}" >/dev/null + page_threads="$(jq \ + '.data.repository.pullRequest.reviewThreads.nodes' \ + <<<"${page}")" + threads="$(jq -c --argjson page "${page_threads}" \ + '. + $page' <<<"${threads}")" + if [ "$(jq -r \ + '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage' \ + <<<"${page}")" != true ]; then + break + fi + after="$(jq -r \ + '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor // empty' \ + <<<"${page}")" + test -n "${after}" + done + jq -e 'all(.[]; .comments.pageInfo.hasNextPage == false)' \ + <<<"${threads}" >/dev/null + if ! jq -e \ + --arg head "${HEAD_SHA}" \ + --argjson parent_id "${parent_id}" \ + --argjson review_id "${REVIEW_ID}" ' + ([.[] | select(any(.comments.nodes[]; + .databaseId == $parent_id + and (.author.login == "Copilot" + or .author.login == "copilot-pull-request-reviewer" + or .author.login == "copilot-pull-request-reviewer[bot]") + and .pullRequestReview.databaseId == $review_id + and .pullRequestReview.commit.oid == $head))] | length) == 1 + and ([.[] | select(any(.comments.nodes[]; + .databaseId == $parent_id)) | select(.isResolved == true)] + | length) == 1 + and ([.[] | select(.isResolved == false) | + select(any(.comments.nodes[]; + (.author.login == "copilot-pull-request-reviewer" + or .author.login == "copilot-pull-request-reviewer[bot]") + and .pullRequestReview.commit.oid == $head))] | length) == 0 + ' <<<"${threads}" >/dev/null; then + echo "Resolved-thread evidence is incomplete or non-authorizing; no rerun is authorized." + exit 0 + fi + rerun_reason=resolved-finding + trigger_occurred_at="${EVENT_COMMENT_CREATED_AT}" + else echo "No neutral result exists and this event cannot authorize a rerun." exit 0 fi - # A missing neutral result may be recovered only from one delayed, - # submitted Copilot review. Comment/thread events never authorize a - # producer rerun, and this workflow never requests an AI review. - # GitHub records the Copilot App as actor/triggering_actor `Copilot` - # for this Actions event, while the Review API login below is the - # distinct `copilot-pull-request-reviewer[bot]`. Keep both bindings. - # Live provenance: Actions run 32602223567 on 2026-08-22. - test "${EVENT_NAME}" = pull_request_review - test "${EVENT_ACTION}" = submitted - test "${GITHUB_ACTOR}" = Copilot + # A missing neutral result may be recovered only from one exact + # delayed review or one resolved-finding evidence reply. Both paths + # reuse the original review and rerun only the verifier job. test "${PR_AUTHOR}" != 'lightning-it-release-automation[bot]' test "${PR_AUTHOR}" != 'lightning-it-shared-assets-sync[bot]' [[ "${CONTROLLER_SHA}" =~ ^[0-9a-f]{40}$ ]] - [[ "${REVIEW_COMMIT_SHA}" =~ ^[0-9a-f]{40}$ ]] [[ "${REVIEW_ID}" =~ ^[1-9][0-9]*$ ]] - test "${REVIEW_COMMIT_SHA}" = "${HEAD_SHA}" - test -n "${REVIEW_SUBMITTED_AT}" review="$(gh api \ "repos/${REPOSITORY}/pulls/${PR_NUMBER}/reviews/${REVIEW_ID}")" + REVIEW_COMMIT_SHA="$(jq -er \ + '.commit_id | select(type == "string" and test("^[0-9a-f]{40}$"))' \ + <<<"${review}")" + REVIEW_SUBMITTED_AT="$(jq -er \ + '.submitted_at | select(type == "string" and length > 0)' \ + <<<"${review}")" + test "${REVIEW_COMMIT_SHA}" = "${HEAD_SHA}" jq -e \ --arg head "${HEAD_SHA}" \ --arg submitted "${REVIEW_SUBMITTED_AT}" \ @@ -354,6 +500,12 @@ jobs: and (contains("premium requests quota") | not) and (contains("encountered an error") | not)) ' <<<"${review}" >/dev/null + if [ "${rerun_reason}" = delayed-review ]; then + # Bind the ordering check and published evidence to the timestamp + # re-read from the authoritative Reviews API, not the event copy. + trigger_occurred_at="${REVIEW_SUBMITTED_AT}" + fi + test -n "${trigger_occurred_at}" for attempt in $(seq 1 10); do response="$(gh api "${runs_url}?event=pull_request_target&head_sha=${HEAD_SHA}&per_page=100")" @@ -418,7 +570,7 @@ jobs: <<<"${verifier_jobs}")" first_verifier_completed_at="$(jq -er '.[0].completed_at | \ select(type == "string" and length > 0)' <<<"${verifier_jobs}")" - test "$(date -u -d "${REVIEW_SUBMITTED_AT}" +%s)" -gt \ + test "$(date -u -d "${trigger_occurred_at}" +%s)" -gt \ "$(date -u -d "${first_verifier_completed_at}" +%s)" # GitHub documents Pull requests: read as one of the alternative @@ -445,13 +597,15 @@ jobs: select(.head_sha == $head)] | length' \ <<<"${existing_authorizations}")" -eq 0 - authorization_external_id="rep60-late-review-rerun:v1:${GITHUB_RUN_ID}:${PR_NUMBER}:${producer_run_id}:${BASE_SHA}:${HEAD_SHA}:${REVIEW_ID}" + authorization_external_id="rep60-late-review-rerun:v1:${rerun_reason}:${GITHUB_RUN_ID}:${PR_NUMBER}:${producer_run_id}:${BASE_SHA}:${HEAD_SHA}:${REVIEW_ID}" authorization_summary="$(jq -cn \ --arg base "${BASE_SHA}" \ --arg controller "${CONTROLLER_SHA}" \ --arg head "${HEAD_SHA}" \ + --arg reason "${rerun_reason}" \ --arg refresh_url "${refresh_url}" \ --arg review_submitted_at "${REVIEW_SUBMITTED_AT}" \ + --arg trigger_occurred_at "${trigger_occurred_at}" \ --argjson pr_number "${PR_NUMBER}" \ --argjson producer_id "${producer_run_id}" \ --argjson refresh_id "${GITHUB_RUN_ID}" \ @@ -460,8 +614,10 @@ jobs: controller_sha:$controller,head_sha:$head, producer_run_id:$producer_id,pull_request_number:$pr_number, refresh_run_id:$refresh_id,refresh_run_url:$refresh_url, + reason:$reason, review_id:$review_id, - review_submitted_at:$review_submitted_at}')" + review_submitted_at:$review_submitted_at, + trigger_occurred_at:$trigger_occurred_at}')" authorization="$(gh api --method POST \ "repos/${REPOSITORY}/check-runs" \ -f name='Late review rerun authorization' \ diff --git a/.github/workflows/promote-develop-to-main.yml b/.github/workflows/promote-develop-to-main.yml index 07b1ac3..7f8dd6a 100644 --- a/.github/workflows/promote-develop-to-main.yml +++ b/.github/workflows/promote-develop-to-main.yml @@ -158,28 +158,62 @@ jobs: exit 0 fi + expected_base="$(git rev-parse origin/main)" expected_head="$(git rev-parse origin/develop)" head_marker="" + run_marker="" + pending_marker="" dispatch_review=false - pr_number="$( - gh pr list \ - --repo "${REPOSITORY}" \ - --state open \ - --base main \ - --head develop \ - --json number \ - --jq '.[0].number // empty' - )" + open_promotions="$(gh api --paginate --slurp \ + "repos/${REPOSITORY}/pulls?state=open&per_page=100")" + open_candidates="$(jq -c \ + --arg head "${expected_head}" \ + --arg marker "${head_marker}" \ + --arg repository "${REPOSITORY}" '[ + add[] | select( + .user.login == "lightning-it-release-automation[bot]" + and .base.repo.full_name == $repository + and .head.repo.full_name == $repository + and ( + (.base.ref == "main" and .head.ref == "develop") + or ([((.body // "") | split("\n")[]) | + select(. == $marker)] == [$marker]) + or ( + [((.body // "") | split("\n")[]) | + select(startswith("$" + ) end + ' <<<"${state_markers}")" \ + || ! jq -e ' + length == 1 + and (.[0] | test( + "^$" + )) + ' <<<"${run_markers}" >/dev/null; then + malformed_comment='Closing a malformed or unfinalized promotion; ' + malformed_comment+='the same develop head remains consumed and cannot be retried.' + gh pr close "${pr_number}" --repo "${REPOSITORY}" \ + --comment "${malformed_comment}" + malformed_error='::error::Recovered a malformed promotion state; ' + malformed_error+='advance develop before creating another promotion.' + echo "${malformed_error}" + exit 1 + fi + stored_state="$(jq -r .state <<<"${state_binding}")" + stored_base="$(jq -r .base <<<"${state_binding}")" + stored_head="$(jq -r .head <<<"${state_binding}")" + jq -e \ + --arg expected_head "${expected_head}" \ + --arg stored_head "${stored_head}" \ + --arg owner "${REPOSITORY_OWNER}" ' + .baseRefName == "main" + and (.baseRefOid | test("^[0-9a-f]{40}$")) + and .headRefName == "develop" + and .headRefOid == $expected_head + and $stored_head == $expected_head + and .headRepositoryOwner.login == $owner + and .state == "OPEN"' \ + <<<"${existing}" | grep -qx true + if [ "${stored_state}" = succeeded ]; then + jq -e --arg stored_base "${stored_base}" \ + '.baseRefOid == $stored_base' <<<"${existing}" | grep -qx true + else + failure_marker="" + updated_body="$(jq -er \ + --arg failure_marker "${failure_marker}" ' + (.body // "") | split("\n") | + map(if startswith("$" + ) | not))) + and ($promotion.head.sha == $head + or ($markers | index($marker)) != null) + ) | .number + ] | unique' <<<"${closed_promotions}")" + if [ "$(jq 'length' <<<"${malformed_closed}")" -ne 0 ]; then + echo '::error::Malformed closed promotion head-marker history blocks this develop head.' + exit 1 + fi + closed_same_head="$(jq -c \ + --arg head "${expected_head}" \ + --arg marker "${head_marker}" \ + --arg repository "${REPOSITORY}" '[ + add[] | select( + .user.login == "lightning-it-release-automation[bot]" + and .base.repo.full_name == $repository + and .head.repo.full_name == $repository + and ( + [((.body // "") | split("\n")[]) | + select(. == $marker)] == [$marker] + or ( + [((.body // "") | split("\n")[]) | + select(startswith("" + run_marker="" + pending_marker="" + success_marker="" + promotion="$(gh api "repos/${REPOSITORY}/pulls/${PR_NUMBER}")" + jq -e \ + --arg repository "${REPOSITORY}" \ + --arg head_marker "${head_marker}" \ + --arg run_marker "${run_marker}" \ + --arg pending_marker "${pending_marker}" \ + --arg base "${EXPECTED_BASE}" \ + --arg head "${EXPECTED_HEAD}" ' + .user.login == "lightning-it-release-automation[bot]" + and .state == "open" + and .base.repo.full_name == $repository + and .base.ref == "main" + and .base.sha == $base + and .head.repo.full_name == $repository + and .head.ref == "develop" + and .head.sha == $head + and [(.body // "") | split("\n")[] | + select(startswith("" + head_marker="" + promotion='' + for attempt in $(seq 1 5); do + if open_promotions="$(gh api --paginate --slurp \ + "repos/${REPOSITORY}/pulls?state=open&per_page=100")"; then + cleanup_candidates="$(jq -c \ + --arg marker "${run_marker}" \ + --arg repository "${REPOSITORY}" '[ + add[] | select( + .user.login == "lightning-it-release-automation[bot]" + and .base.repo.full_name == $repository + and .head.repo.full_name == $repository + and [(.body // "") | split("\n")[] | + select(. == $marker)] == [$marker] + ) + ]' <<<"${open_promotions}")" + cleanup_count="$(jq 'length' <<<"${cleanup_candidates}")" + if [ "${cleanup_count}" -gt 1 ]; then + echo '::error::Failed workflow run resolved to an ambiguous promotion PR set.' + exit 1 + fi + if [ "${cleanup_count}" -eq 1 ]; then + promotion="$(jq -c '.[0]' <<<"${cleanup_candidates}")" + break + fi + fi + if [ "${attempt}" -lt 5 ]; then sleep 2; fi + done + if [ -z "${promotion}" ] && [ -n "${CAPTURED_PR_NUMBER}" ]; then + promotion="$(gh api \ + "repos/${REPOSITORY}/pulls/${CAPTURED_PR_NUMBER}" 2>/dev/null || true)" + fi + if [ -z "${promotion}" ]; then + echo '::error::The attempted promotion could not be recovered after bounded discovery.' + exit 1 + fi + captured_number=0 + if [ -n "${CAPTURED_PR_NUMBER}" ]; then + captured_number="${CAPTURED_PR_NUMBER}" + fi + jq -e \ + --arg repository "${REPOSITORY}" \ + --arg run_marker "${run_marker}" \ + --arg head_marker "${head_marker}" \ + --arg base "${EXPECTED_BASE}" \ + --arg head "${EXPECTED_HEAD}" \ + --argjson captured_number "${captured_number}" ' + .user.login == "lightning-it-release-automation[bot]" + and .state == "open" + and .base.repo.full_name == $repository + and .base.ref == "main" + and .base.sha == $base + and .head.repo.full_name == $repository + and .head.ref == "develop" + and .head.sha == $head + and ( + (.number == $captured_number and $captured_number != 0) + or ( + [(.body // "") | split("\n")[] | + select(startswith("" + updated_body="$(jq -er --arg marker "${failure_marker}" ' + (.body // "") | split("\n") | + map(select(startswith("