From bc620a2c7ae358b8d5a5990d2a6d227f3395d80c 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: Sat, 22 Aug 2026 06:21:07 +0000 Subject: [PATCH] chore: sync repository quality assets Shared-Assets-Source-SHA: e046de46c0e9606a31397a54c4292baced49cf8b Shared-Assets-Source-Run: 32556727549 Shared-Assets-Source-Attempt: 1 Shared-Assets-Sync-App-ID: 4351516 --- .github/workflows/copilot-review.yml | 336 ++++++++++++++++++- .github/workflows/current-revision-rerun.yml | 147 +++++++- 2 files changed, 463 insertions(+), 20 deletions(-) diff --git a/.github/workflows/copilot-review.yml b/.github/workflows/copilot-review.yml index fc14c94..9a631f2 100644 --- a/.github/workflows/copilot-review.yml +++ b/.github/workflows/copilot-review.yml @@ -9,7 +9,7 @@ name: Current revision review gate on: pull_request_target: - types: [opened, synchronize, reopened, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review, edited] permissions: contents: read @@ -200,7 +200,8 @@ jobs: github.event_name == 'pull_request_target' && github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository && - (((github.event.action == 'opened' || + (github.event.action == 'edited' || + (((github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.action == 'reopened' || github.event.action == 'ready_for_review') && @@ -216,7 +217,13 @@ jobs: startsWith( github.event.pull_request.title, 'chore(governance): record main ancestry before ' - ))) + )))) + # Serialize every producer for this PR. Different event actions may overlap + # at workflow level so the one-time Copilot request is never cancelled, but + # no two verifier jobs may invalidate or publish the neutral check at once. + concurrency: + group: copilot-review-verify-${{ github.event.pull_request.number }} + cancel-in-progress: false permissions: actions: read checks: write @@ -227,6 +234,177 @@ jobs: timeout-minutes: 30 steps: + - name: Invalidate prior result after pull-request metadata edit + if: github.event.action == 'edited' + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + EVENT_BASE_REF: ${{ github.event.pull_request.base.ref }} + EVENT_BODY: ${{ github.event.pull_request.body }} + EVENT_TITLE: ${{ github.event.pull_request.title }} + EVENT_UPDATED_AT: ${{ github.event.pull_request.updated_at }} + GH_TOKEN: ${{ github.token }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPOSITORY: ${{ github.repository }} + run: | + set -euo pipefail + api_read() { + local attempt output + for attempt in $(seq 1 5); do + if output="$(gh api "$@")"; then + printf '%s' "${output}" + return 0 + fi + if [ "${attempt}" -eq 5 ]; then + echo "GitHub API read failed after five attempts." >&2 + return 1 + fi + sleep 5 + done + } + api_patch() { + local attempt output + for attempt in $(seq 1 5); do + if output="$(gh api --method PATCH "$@")"; then + printf '%s' "${output}" + return 0 + fi + if [ "${attempt}" -eq 5 ]; then + echo "Idempotent GitHub check invalidation failed after five attempts." >&2 + return 1 + fi + sleep 5 + done + } + [[ "${BASE_SHA}" =~ ^[0-9a-f]{40}$ ]] + [[ "${HEAD_SHA}" =~ ^[0-9a-f]{40}$ ]] + [[ "${PR_NUMBER}" =~ ^[1-9][0-9]*$ ]] + pr="$(api_read "repos/${REPOSITORY}/pulls/${PR_NUMBER}")" + jq -e \ + --arg base_ref "${EVENT_BASE_REF}" \ + --arg base_sha "${BASE_SHA}" \ + --arg body "${EVENT_BODY}" \ + --arg head_ref "${HEAD_REF}" \ + --arg head_sha "${HEAD_SHA}" \ + --arg repository "${REPOSITORY}" \ + --arg title "${EVENT_TITLE}" \ + --arg updated_at "${EVENT_UPDATED_AT}" ' + .state == "open" + and .draft == false + and .updated_at == $updated_at + and .title == $title + and (.body // "") == $body + and .base.ref == $base_ref + and .base.sha == $base_sha + and .head.ref == $head_ref + and .head.sha == $head_sha + and .head.repo.full_name == $repository + ' <<<"${pr}" >/dev/null + checks="$(api_read --paginate --slurp \ + "repos/${REPOSITORY}/commits/${HEAD_SHA}/check-runs?check_name=Current%20revision%20review&filter=all&per_page=100")" + named="$(jq -c '[.[].check_runs[]? | + select(.name == "Current revision review") | + select(.app.id == 15368 and .app.slug == "github-actions") | + select((.external_id // "") | + startswith("mlx90-current-revision:"))]' \ + <<<"${checks}")" + count="$(jq 'length' <<<"${named}")" + if [ "${count}" -gt 1 ]; then + echo "Multiple protected Current revision review results exist for ${HEAD_SHA}." >&2 + exit 1 + fi + if [ "${count}" -eq 0 ]; then + exit 0 + fi + check_id="$(jq -er '.[0].id | select(type == "number" and . > 0)' <<<"${named}")" + run_url="${GITHUB_SERVER_URL}/${REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + evidence="$(jq -cn \ + --arg base "${BASE_SHA}" \ + --arg head "${HEAD_SHA}" \ + --arg reason "pull-request metadata changed" \ + --argjson pr_number "${PR_NUMBER}" \ + --argjson run_id "${GITHUB_RUN_ID}" \ + --arg run_url "${run_url}" \ + '{schema:4,base_sha:$base,head_sha:$head,pull_request_number:$pr_number, + producer_run_id:$run_id,reason:$reason,run_url:$run_url}')" + completed_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + updated="$(api_patch \ + "repos/${REPOSITORY}/check-runs/${check_id}" \ + -f status=completed \ + -f conclusion=failure \ + -f "completed_at=${completed_at}" \ + -f "details_url=${run_url}" \ + -f "external_id=mlx90-current-revision:metadata-edit:v1:${PR_NUMBER}:${GITHUB_RUN_ID}:${BASE_SHA}:${HEAD_SHA}" \ + -f "output[title]=Current revision metadata changed; revalidation required" \ + -f "output[summary]=${evidence}")" + jq -e \ + --arg evidence "${evidence}" \ + --arg head "${HEAD_SHA}" \ + --argjson check_id "${check_id}" ' + .id == $check_id + and .name == "Current revision review" + and .app.id == 15368 + and .app.slug == "github-actions" + and .head_sha == $head + and .status == "completed" + and .conclusion == "failure" + and .output.summary == $evidence + ' <<<"${updated}" >/dev/null + + - name: Capture live pull-request metadata revision + id: metadata-revision + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPOSITORY: ${{ github.repository }} + run: | + set -euo pipefail + owner="${REPOSITORY%%/*}" + repository="${REPOSITORY#*/}" + read -r -d '' query <<'GRAPHQL' || true + query($owner: String!, $repository: String!, $number: Int!) { + repository(owner: $owner, name: $repository) { + pullRequest(number: $number) { + number + lastEditedAt + } + } + } + GRAPHQL + response='' + for attempt in $(seq 1 5); do + if response="$(gh api graphql \ + -f query="${query}" \ + -F owner="${owner}" \ + -F repository="${repository}" \ + -F number="${PR_NUMBER}")" \ + && jq -e \ + --argjson number "${PR_NUMBER}" ' + ((.errors // []) | length) == 0 + and .data.repository.pullRequest.number == $number + and ( + .data.repository.pullRequest.lastEditedAt == null + or ( + (.data.repository.pullRequest.lastEditedAt | type) == "string" + and (.data.repository.pullRequest.lastEditedAt | + test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$")) + ) + ) + ' <<<"${response}" >/dev/null; then + break + fi + if [ "${attempt}" -eq 5 ]; then + echo "Unable to capture the protected pull-request metadata revision." >&2 + exit 1 + fi + sleep 5 + done + last_edited_at="$(jq -r \ + '.data.repository.pullRequest.lastEditedAt // "null"' \ + <<<"${response}")" + echo "last_edited_at=${last_edited_at}" >>"${GITHUB_OUTPUT}" + - name: Classify trusted automation pull request id: trusted-automation env: @@ -461,9 +639,12 @@ jobs: steps.trusted-automation.outputs.trusted != 'true' && github.event.pull_request.user.login != 'lightning-it-release-automation[bot]' env: + BOUND_LAST_EDITED_AT: ${{ steps.metadata-revision.outputs.last_edited_at }} + FRESH_REVIEW_AFTER: ${{ github.event.pull_request.updated_at }} GH_TOKEN: ${{ github.token }} PR_NUMBER: ${{ github.event.pull_request.number }} REPOSITORY: ${{ github.repository }} + REQUIRE_FRESH_REVIEW: ${{ github.event.action == 'edited' }} run: | set -euo pipefail @@ -496,6 +677,7 @@ jobs: repository(owner: $owner, name: $repository) { pullRequest(number: $number) { headRefOid + lastEditedAt reviews(last: 100, before: $before) { pageInfo { hasPreviousPage startCursor } nodes { @@ -503,6 +685,7 @@ jobs: author { login } commit { oid } state + submittedAt } } } @@ -665,11 +848,21 @@ jobs: break fi head_sha="${page_head_sha}" + page_last_edited_at="$(jq -r \ + '.data.repository.pullRequest.lastEditedAt // "null"' \ + <<<"${response}")" + if [ "${page_last_edited_at}" != "${BOUND_LAST_EDITED_AT}" ]; then + echo "Pull-request metadata changed after this review run was bound." >&2 + exit 1 + fi candidate_review_ids="$( jq -r \ --arg head "${head_sha}" \ + --arg last_edited_at "${BOUND_LAST_EDITED_AT}" \ + --arg not_before "${FRESH_REVIEW_AFTER}" \ --arg reviewer "${COPILOT_REVIEWER_LOGIN}" \ + --arg require_fresh "${REQUIRE_FRESH_REVIEW}" \ '(.data.repository.pullRequest.reviews.nodes // [])[] | select( (.author.login // "") as $login @@ -677,6 +870,14 @@ jobs: ) | select(.state == "COMMENTED" or .state == "APPROVED") | select(.commit.oid == $head) + | select( + $last_edited_at == "null" + or ((.submittedAt // "") > $last_edited_at) + ) + | select( + $require_fresh != "true" + or ((.submittedAt // "") > $not_before) + ) | (.id // empty)' <<<"${response}" )" while IFS= read -r review_id; do @@ -840,10 +1041,15 @@ jobs: - name: Publish bound neutral result env: + BOUND_LAST_EDITED_AT: ${{ steps.metadata-revision.outputs.last_edited_at }} + EVENT_ACTION: ${{ github.event.action }} GH_TOKEN: ${{ github.token }} EVENT_HEAD: ${{ github.event.pull_request.head.sha }} EVENT_HEAD_REF: ${{ github.event.pull_request.head.ref }} EVENT_BASE: ${{ github.event.pull_request.base.sha }} + EVENT_BASE_REF: ${{ github.event.pull_request.base.ref }} + EVENT_BODY: ${{ github.event.pull_request.body }} + EVENT_TITLE: ${{ github.event.pull_request.title }} DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} PR_NUMBER: ${{ github.event.pull_request.number }} REPOSITORY: ${{ github.repository }} @@ -880,10 +1086,58 @@ jobs: sleep 5 done } + read_metadata_revision() { + local attempt output + local owner="${REPOSITORY%%/*}" + local repository="${REPOSITORY#*/}" + local query + read -r -d '' query <<'GRAPHQL' || true + query($owner: String!, $repository: String!, $number: Int!) { + repository(owner: $owner, name: $repository) { + pullRequest(number: $number) { + number + lastEditedAt + } + } + } + GRAPHQL + for attempt in $(seq 1 5); do + if output="$(gh api graphql \ + -f query="${query}" \ + -F owner="${owner}" \ + -F repository="${repository}" \ + -F number="${PR_NUMBER}")" \ + && jq -e \ + --argjson number "${PR_NUMBER}" ' + ((.errors // []) | length) == 0 + and .data.repository.pullRequest.number == $number + and ( + .data.repository.pullRequest.lastEditedAt == null + or ( + (.data.repository.pullRequest.lastEditedAt | type) == "string" + and (.data.repository.pullRequest.lastEditedAt | + test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$")) + ) + ) + ' <<<"${output}" >/dev/null; then + jq -r '.data.repository.pullRequest.lastEditedAt // "null"' \ + <<<"${output}" + return 0 + fi + if [ "${attempt}" -eq 5 ]; then + echo "Unable to re-prove the protected pull-request metadata revision." >&2 + return 1 + fi + sleep 5 + done + } [[ "${EVENT_BASE}" =~ ^[0-9a-f]{40}$ ]] [[ "${EVENT_HEAD}" =~ ^[0-9a-f]{40}$ ]] [[ "${PR_NUMBER}" =~ ^[1-9][0-9]*$ ]] [[ "${TRUSTED_WORKFLOW_SHA}" =~ ^[0-9a-f]{40}$ ]] + [[ "${BOUND_LAST_EDITED_AT}" == null \ + || "${BOUND_LAST_EDITED_AT}" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ ]] + test "$(read_metadata_revision)" = "${BOUND_LAST_EDITED_AT}" test "${DEFAULT_BRANCH}" = develop test "${TRUSTED_WORKFLOW_REF}" = \ "${REPOSITORY}/.github/workflows/copilot-review.yml@refs/heads/${DEFAULT_BRANCH}" @@ -910,10 +1164,24 @@ jobs: and .head_repository.full_name == $repository ' <<<"${protected_run}" >/dev/null pr="$(gh api "repos/${REPOSITORY}/pulls/${PR_NUMBER}")" - test "$(jq -r .state <<<"${pr}")" = open - test "$(jq -r .draft <<<"${pr}")" = false - test "$(jq -r .head.sha <<<"${pr}")" = "${EVENT_HEAD}" - test "$(jq -r .base.sha <<<"${pr}")" = "${EVENT_BASE}" + jq -e \ + --arg base_ref "${EVENT_BASE_REF}" \ + --arg base_sha "${EVENT_BASE}" \ + --arg body "${EVENT_BODY}" \ + --arg head_ref "${EVENT_HEAD_REF}" \ + --arg head_sha "${EVENT_HEAD}" \ + --arg repository "${REPOSITORY}" \ + --arg title "${EVENT_TITLE}" ' + .state == "open" + and .draft == false + and .title == $title + and (.body // "") == $body + and .base.ref == $base_ref + and .base.sha == $base_sha + and .head.ref == $head_ref + and .head.sha == $head_sha + and .head.repo.full_name == $repository + ' <<<"${pr}" >/dev/null base_ref="$(jq -er '.base.ref | select(. == "develop" or . == "main")' <<<"${pr}")" test -n "${base_ref}" test "$(jq -r .base.repo.full_name <<<"${pr}")" = "${REPOSITORY}" @@ -988,18 +1256,22 @@ jobs: test "${TRUSTED_KIND}" != repository-quality test "${TRUSTED_KIND}" != ancestry-backmerge test "${author}" != 'lightning-it-shared-assets-sync[bot]' + test "${author}" != 'lightning-it-release-automation[bot]' fi run_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" evidence="$(jq -cn \ --arg base "${EVENT_BASE}" \ --arg controller "${TRUSTED_WORKFLOW_SHA}" \ --arg head "${EVENT_HEAD}" \ + --arg last_edited_at "${BOUND_LAST_EDITED_AT}" \ --arg review_path "${review_path}" \ --argjson pr_number "${PR_NUMBER}" \ --argjson run_id "${GITHUB_RUN_ID}" \ --arg run_url "${run_url}" \ '{schema:4,base_sha:$base,head_sha:$head,controller_sha:$controller, pull_request_number:$pr_number,producer_run_id:$run_id, + pull_request_last_edited_at:( + if $last_edited_at == "null" then null else $last_edited_at end), review_path:$review_path,run_url:$run_url}')" publish_once() { local check_name="$1" external_id="$2" title="$3" @@ -1022,6 +1294,11 @@ jobs: exit 1 fi if [ "${count}" -eq 1 ]; then + if [ "${EVENT_ACTION}" = edited ]; then + expected_reservation="mlx90-current-revision:metadata-edit:v1:${PR_NUMBER}:${GITHUB_RUN_ID}:${EVENT_BASE}:${EVENT_HEAD}" + test "$(jq -r '.[0].external_id // empty' <<<"${named}")" = \ + "${expected_reservation}" + fi check_id="$(jq -er '.[0].id | select(type == "number" and . > 0)' <<<"${named}")" check_url="${GITHUB_SERVER_URL}/${REPOSITORY}/runs/${check_id}" # A new bound producer must refresh the terminal timestamp. GitHub's @@ -1055,6 +1332,7 @@ jobs: and .conclusion == "success" and .output.summary == $evidence ' <<<"${updated}" >/dev/null + printf '%s' "${check_id}" return fi if ! created="$(gh api --method POST "repos/${REPOSITORY}/check-runs" \ @@ -1115,11 +1393,51 @@ jobs: and .conclusion == "success" and .output.summary == $evidence ' <<<"${created}" >/dev/null + printf '%s' "${check_id}" } - publish_once \ + published_check_id="$(publish_once \ 'Current revision review' \ "mlx90-current-revision:${external_kind}:v6:${PR_NUMBER}:${GITHUB_RUN_ID}:${EVENT_BASE}:${EVENT_HEAD}" \ - "${result_title}" + "${result_title}")" + [[ "${published_check_id}" =~ ^[1-9][0-9]*$ ]] + final_last_edited_at='' + if ! final_last_edited_at="$(read_metadata_revision)" \ + || [ "${final_last_edited_at}" != "${BOUND_LAST_EDITED_AT}" ]; then + failure_evidence="$(jq -cn \ + --arg base "${EVENT_BASE}" \ + --arg head "${EVENT_HEAD}" \ + --arg reason "pull-request metadata changed during result publication" \ + --argjson pr_number "${PR_NUMBER}" \ + --argjson run_id "${GITHUB_RUN_ID}" \ + --arg run_url "${run_url}" \ + '{schema:4,base_sha:$base,head_sha:$head,pull_request_number:$pr_number, + producer_run_id:$run_id,reason:$reason,run_url:$run_url}')" + completed_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + invalidated="$(api_patch \ + "repos/${REPOSITORY}/check-runs/${published_check_id}" \ + -f status=completed \ + -f conclusion=failure \ + -f "completed_at=${completed_at}" \ + -f "details_url=${run_url}" \ + -f "external_id=mlx90-current-revision:metadata-race:v1:${PR_NUMBER}:${GITHUB_RUN_ID}:${EVENT_BASE}:${EVENT_HEAD}" \ + -f 'output[title]=Current revision metadata changed during publication' \ + -f "output[summary]=${failure_evidence}")" + jq -e \ + --arg evidence "${failure_evidence}" \ + --arg head "${EVENT_HEAD}" \ + --argjson check_id "${published_check_id}" ' + .id == $check_id + and .name == "Current revision review" + and .app.id == 15368 + and .app.slug == "github-actions" + and .head_sha == $head + and .status == "completed" + and .conclusion == "failure" + and .output.summary == $evidence + ' <<<"${invalidated}" >/dev/null + echo "Pull-request metadata changed during result publication." >&2 + exit 1 + fi request-protected-verifier-reevaluation: name: Request protected verifier re-evaluation diff --git a/.github/workflows/current-revision-rerun.yml b/.github/workflows/current-revision-rerun.yml index fedaeac..24556a0 100644 --- a/.github/workflows/current-revision-rerun.yml +++ b/.github/workflows/current-revision-rerun.yml @@ -69,8 +69,34 @@ jobs: neutral_pages="$(gh api --paginate --slurp \ "repos/${REPOSITORY}/commits/${EXPECTED_HEAD}/check-runs?check_name=Current%20revision%20review&filter=all&per_page=100")" + jq -e ' + type == "array" + and length > 0 + and all(.[]; + type == "object" + and (.total_count | type) == "number" + and (.check_runs | type) == "array") + and all(.[].check_runs[]; + type == "object" + and (.id | type) == "number" and .id > 0 + and (.name | type) == "string" + and (.head_sha | type) == "string" + and (.status | type) == "string" + and has("conclusion") + and ((.conclusion | type) == "string" or .conclusion == null) + and has("details_url") + and ((.details_url | type) == "string" or .details_url == null) + and has("external_id") + and ((.external_id | type) == "string" or .external_id == null) + and (.app | type) == "object" + and (.app.id | type) == "number" + and (.app.slug | type) == "string" + and (.output | type) == "object" + and (.output | has("summary")) + and ((.output.summary | type) == "string" or .output.summary == null)) + ' <<<"${neutral_pages}" >/dev/null neutral="$(jq -c ' - [.[].check_runs[]? | + [.[].check_runs[] | select(.name == "Current revision review") | select(.app.id == 15368 and .app.slug == "github-actions") | select(.status == "completed" and .conclusion == "success")] @@ -280,36 +306,135 @@ jobs: exit 0 fi test "$(jq -r .conclusion <<<"${run}")" = failure - run_attempt="$(jq -er '.run_attempt | select(type == "number" and . >= 1)' <<<"${run}")" - if [ "${run_attempt}" -ne 1 ]; then + run_attempt=$(jq -er '.run_attempt | select(type == "number" and . >= 1)' <<<"${run}") + if [ "${run_attempt}" -gt 2 ]; then + echo "Protected verifier exceeded its single allowed rerun." >&2 + exit 1 + fi + if [ "${run_attempt}" -eq 2 ]; then echo "Protected verifier already consumed its single rerun; no further retry is allowed." exit 0 fi + attempt_one_jobs_pages="$(gh api --paginate --slurp \ + "repos/${REPOSITORY}/actions/runs/${run_id}/attempts/1/jobs?filter=all&per_page=100")" + jq -e ' + type == "array" + and length > 0 + and all(.[]; + type == "object" + and (.total_count | type) == "number" + and (.jobs | type) == "array") + and all(.[].jobs[]; + type == "object" + and (.id | type) == "number" and .id > 0 + and (.name | type) == "string" + and (.run_attempt | type) == "number" and .run_attempt >= 1 + and .status == "completed" + and (.conclusion | type) == "string") + ' <<<"${attempt_one_jobs_pages}" >/dev/null + rerunnable_jobs=$(jq -c '[.[].jobs[] | select(.name == "Required current-revision workflow") | select(.run_attempt == 1) | select(.status == "completed" and .conclusion == "failure")]' <<<"${attempt_one_jobs_pages}") + test "$(jq 'length' <<<"${rerunnable_jobs}")" -eq 1 + required_job_id=$(jq -er '.[0].id | select(type == "number" and . > 0)' \ + <<<"${rerunnable_jobs}") if ! gh api --method POST \ - "repos/${REPOSITORY}/actions/runs/${run_id}/rerun" >/dev/null; then - # GitHub can accept the rerun and still close the client request + "repos/${REPOSITORY}/actions/jobs/${required_job_id}/rerun" >/dev/null; then + # GitHub can accept the job rerun and still close the client request # with a non-success response when another service-side request # wins the same transition. Accept that outcome only after the # live run proves that attempt two was actually materialized. run="$(gh api "repos/${REPOSITORY}/actions/runs/${run_id}")" observed_attempt="$(jq -er '.run_attempt | select(type == "number" and . >= 1)' <<<"${run}")" - if [ "${observed_attempt}" -lt 2 ]; then - echo "Rerun request failed without a materialized second attempt." >&2 + if [ "${observed_attempt}" -ne 2 ]; then + echo "Required-job rerun failed without exactly one materialized second attempt." >&2 exit 1 fi - echo "A concurrent service-side request already materialized the protected rerun." + echo "A concurrent service-side request already materialized the required-job rerun." fi for completion_attempt in $(seq 1 60); do run="$(gh api "repos/${REPOSITORY}/actions/runs/${run_id}")" observed_attempt="$(jq -er '.run_attempt | select(type == "number" and . >= 1)' <<<"${run}")" status="$(jq -er '.status | select(type == "string" and length > 0)' <<<"${run}")" - if [ "${observed_attempt}" -ge 2 ] && [ "${status}" = completed ]; then + if [ "${observed_attempt}" -gt 2 ]; then + echo "Protected verifier exceeded its single allowed rerun." >&2 + exit 1 + fi + if [ "${observed_attempt}" -eq 2 ] && [ "${status}" = completed ]; then test "$(jq -r .conclusion <<<"${run}")" = success - echo "Protected verifier rerun completed successfully." + attempt_two_jobs_pages="$(gh api --paginate --slurp \ + "repos/${REPOSITORY}/actions/runs/${run_id}/attempts/2/jobs?filter=all&per_page=100")" + jq -e ' + type == "array" + and length > 0 + and all(.[]; + type == "object" + and (.total_count | type) == "number" + and (.jobs | type) == "array") + and all(.[].jobs[]; + type == "object" + and (.id | type) == "number" and .id > 0 + and (.name | type) == "string" + and (.run_attempt | type) == "number" and .run_attempt >= 1 + and .status == "completed" + and (.conclusion | type) == "string") + ' <<<"${attempt_two_jobs_pages}" >/dev/null + completed_required_jobs="$(jq -c ' + [.[].jobs[] | + select(.name == "Required current-revision workflow") | + select(.run_attempt == 2) | + select(.status == "completed" and .conclusion == "success")] + ' <<<"${attempt_two_jobs_pages}")" + test "$(jq 'length' <<<"${completed_required_jobs}")" -eq 1 + post_neutral_pages="$(gh api --paginate --slurp \ + "repos/${REPOSITORY}/commits/${EXPECTED_HEAD}/check-runs?check_name=Current%20revision%20review&filter=all&per_page=100")" + jq -e ' + type == "array" + and length > 0 + and all(.[]; + type == "object" + and (.total_count | type) == "number" + and (.check_runs | type) == "array") + and all(.[].check_runs[]; + type == "object" + and (.id | type) == "number" and .id > 0 + and (.name | type) == "string" + and (.head_sha | type) == "string" + and (.status | type) == "string" + and has("conclusion") + and ((.conclusion | type) == "string" or .conclusion == null) + and has("details_url") + and ((.details_url | type) == "string" or .details_url == null) + and has("external_id") + and ((.external_id | type) == "string" or .external_id == null) + and (.app | type) == "object" + and (.app.id | type) == "number" + and (.app.slug | type) == "string" + and (.output | type) == "object" + and (.output | has("summary")) + and ((.output.summary | type) == "string" or .output.summary == null)) + ' <<<"${post_neutral_pages}" >/dev/null + post_neutral="$(jq -c \ + --arg head "${EXPECTED_HEAD}" ' + [.[].check_runs[] | + select(.name == "Current revision review") | + select(.app.id == 15368 and .app.slug == "github-actions") | + select(.head_sha == $head)] + ' <<<"${post_neutral_pages}")" + test "$(jq 'length' <<<"${post_neutral}")" -eq 1 + jq -e \ + --arg external_id "${neutral_external_id}" \ + --arg head "${EXPECTED_HEAD}" \ + --argjson check_id "${neutral_check_id}" ' + .[0].id == $check_id + and .[0].head_sha == $head + and .[0].external_id == $external_id + and .[0].status == "completed" + and .[0].conclusion == "success" + ' <<<"${post_neutral}" >/dev/null + echo "Protected verifier job rerun completed without duplicating neutral evidence." exit 0 fi if [ "${completion_attempt}" -eq 60 ]; then - echo "Protected verifier rerun did not complete successfully in time." >&2 + echo "Protected verifier job rerun did not complete successfully in time." >&2 exit 1 fi sleep 5