Required OpenCode Review ContextualWisdomLab/fast-mlsirm#1506@773b70f37138bc55b51e33ed0bdc9bb7f2db82a6 #7413
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Required OpenCode Review | |
| run-name: >- | |
| Required OpenCode Review ${{ github.event.pull_request.base.repo.full_name || | |
| github.repository }}#${{ github.event.pull_request.number || 'event' }}@${{ | |
| github.event.pull_request.head.sha || github.sha }} | |
| on: | |
| # This required-workflow entrypoint never checks out or executes pull-request | |
| # content and never binds repository secrets. Privileged review execution is | |
| # isolated in opencode-review-dispatch.yml on repository_dispatch only. | |
| pull_request_target: | |
| # `converted_to_draft` is included so a draft conversion gets an immediate | |
| # exempting run. Every non-closed | |
| # admission path revalidates the live PR/head/state before dispatching, | |
| # exempting, or checking the receipt so out-of-order draft/ready/closed | |
| # events cannot publish stale evidence. | |
| types: [opened, synchronize, reopened, ready_for_review, converted_to_draft, closed] | |
| concurrency: | |
| # Coalesce before runner admission. The live-head job and scheduler still | |
| # reject or replace a delayed stale event after native queue cancellation. | |
| group: >- | |
| required-opencode-review-${{ | |
| github.event.pull_request.base.repo.full_name || github.repository }}-${{ | |
| github.event.pull_request.number || github.run_id }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| id-token: write | |
| jobs: | |
| required-workflow-bootstrap: | |
| name: required-workflow-bootstrap | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Materialize the required review workflow | |
| run: >- | |
| echo "Required OpenCode workflow materialized without checking out or | |
| executing pull-request content." | |
| - name: Reject untrusted fork review resource consumption | |
| env: | |
| PR_ACTION: ${{ github.event.action }} | |
| BASE_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name }} | |
| HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$PR_ACTION" = "closed" ]; then | |
| echo "PR closed; fork-resource-consumption check is not required." | |
| exit 0 | |
| fi | |
| if [ -z "$BASE_REPOSITORY" ] || [ -z "$HEAD_REPOSITORY" ] || [ "$HEAD_REPOSITORY" != "$BASE_REPOSITORY" ]; then | |
| echo "::error::Long-running required review is restricted to branches in the base repository. A maintainer must materialize an external contribution on a trusted branch before review." | |
| exit 1 | |
| fi | |
| - name: Resolve immutable central policy source | |
| id: trusted_source | |
| env: | |
| JOB_CONTEXT_JSON: ${{ toJSON(job) }} | |
| WORKFLOW_SHA: ${{ github.workflow_sha }} | |
| WORKFLOW_REF: ${{ github.workflow_ref }} | |
| run: | | |
| set -euo pipefail | |
| python3 <<'PY' >>"$GITHUB_OUTPUT" | |
| import json | |
| import os | |
| import re | |
| import sys | |
| try: | |
| job_context = json.loads(os.environ.get("JOB_CONTEXT_JSON") or "{}") | |
| except json.JSONDecodeError as exc: | |
| print(f"::error::Could not parse GitHub workflow context JSON: {exc}", file=sys.stderr) | |
| raise SystemExit(1) | |
| expected_repository = "ContextualWisdomLab/.github" | |
| expected_file = ".github/workflows/opencode-review.yml" | |
| workflow_sha = str( | |
| job_context.get("workflow_sha") or os.environ.get("WORKFLOW_SHA") or "" | |
| ).strip() | |
| workflow_ref = str( | |
| job_context.get("workflow_ref") or os.environ.get("WORKFLOW_REF") or "" | |
| ).strip() | |
| workflow_ref_head, separator, _ = workflow_ref.partition("@") | |
| if not separator: | |
| print("::error::Required workflow ref is missing its immutable ref separator.", file=sys.stderr) | |
| raise SystemExit(1) | |
| ref_parts = workflow_ref_head.split("/", 2) | |
| if len(ref_parts) < 2 or not ref_parts[0] or not ref_parts[1]: | |
| print("::error::Required workflow ref does not identify a repository.", file=sys.stderr) | |
| raise SystemExit(1) | |
| workflow_repository = "/".join(ref_parts[:2]) | |
| workflow_file_path = str(job_context.get("workflow_file_path") or "").strip() | |
| if not workflow_file_path: | |
| prefix = f"{expected_repository}/{expected_file}@" | |
| if workflow_ref.startswith(prefix): | |
| workflow_file_path = expected_file | |
| if workflow_repository != expected_repository: | |
| print( | |
| f"::error::Required workflow repository resolved to {workflow_repository}, expected {expected_repository}.", | |
| file=sys.stderr, | |
| ) | |
| raise SystemExit(1) | |
| if not re.fullmatch(r"[0-9a-fA-F]{40}", workflow_sha): | |
| print("::error::Required workflow SHA is missing or malformed.", file=sys.stderr) | |
| raise SystemExit(1) | |
| if workflow_file_path != expected_file: | |
| print("::error::Required workflow file path is missing or unexpected.", file=sys.stderr) | |
| raise SystemExit(1) | |
| expected_ref_prefix = f"{expected_repository}/{expected_file}@" | |
| if not workflow_ref.startswith(expected_ref_prefix): | |
| print("::error::Required workflow ref is missing or inconsistent.", file=sys.stderr) | |
| raise SystemExit(1) | |
| print(f"repository={workflow_repository}") | |
| print(f"sha={workflow_sha}") | |
| print(f"workflow_file_path={workflow_file_path}") | |
| PY | |
| - name: Materialize trusted central policy source | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TRUSTED_SOURCE_REF: ${{ steps.trusted_source.outputs.sha }} | |
| run: | | |
| set -euo pipefail | |
| if [[ ! "$TRUSTED_SOURCE_REF" =~ ^[0-9a-fA-F]{40}$ ]]; then | |
| echo "::error::Trusted central policy source ref must resolve to the immutable workflow commit SHA before archive materialization." | |
| exit 1 | |
| fi | |
| trusted_archive="${RUNNER_TEMP}/trusted-opencode-policy-source.tar.gz" | |
| trusted_source_dir="${GITHUB_WORKSPACE}/.cwl-required-source" | |
| api_url="${GITHUB_API_URL:-https://api.github.com}" | |
| mkdir -p "$trusted_source_dir" | |
| curl -fsSL \ | |
| -H "Authorization: Bearer ${GH_TOKEN}" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -o "$trusted_archive" \ | |
| "${api_url}/repos/ContextualWisdomLab/.github/tarball/${TRUSTED_SOURCE_REF}" | |
| python3 - "$trusted_archive" "$trusted_source_dir" <<'PY' | |
| import shutil | |
| import sys | |
| import tarfile | |
| from pathlib import Path, PurePosixPath | |
| archive_path = Path(sys.argv[1]) | |
| root = Path(sys.argv[2]) | |
| try: | |
| if root.is_symlink(): | |
| raise ValueError("trusted source directory must not be a symlink") | |
| if root.exists(): | |
| shutil.rmtree(root) | |
| root.mkdir(parents=True) | |
| with tarfile.open(archive_path, "r:gz") as archive: | |
| members = archive.getmembers() | |
| top_levels: set[str] = set() | |
| targets: set[str] = set() | |
| directories: list[tuple[tarfile.TarInfo, tuple[str, ...]]] = [] | |
| files: list[tuple[tarfile.TarInfo, tuple[str, ...]]] = [] | |
| for member in members: | |
| name = member.name | |
| if not name or name.startswith("/") or "\x00" in name or "\\" in name: | |
| raise ValueError(f"unsafe archive member path: {name!r}") | |
| parts = PurePosixPath(name).parts | |
| if not parts or parts[0] in {".", ".."}: | |
| raise ValueError(f"unsafe archive member path: {name!r}") | |
| top_levels.add(parts[0]) | |
| relative_parts = parts[1:] | |
| if not relative_parts: | |
| if not member.isdir(): | |
| raise ValueError("archive root must be a directory") | |
| continue | |
| if any(part in {"", ".", ".."} for part in relative_parts): | |
| raise ValueError(f"unsafe archive member path: {name!r}") | |
| relative_key = "/".join(relative_parts) | |
| if relative_key in targets: | |
| raise ValueError(f"duplicate archive member path: {relative_key}") | |
| targets.add(relative_key) | |
| if member.isdir(): | |
| directories.append((member, relative_parts)) | |
| elif member.isfile(): | |
| files.append((member, relative_parts)) | |
| else: | |
| raise ValueError(f"unsupported archive member type: {name!r}") | |
| if len(top_levels) != 1: | |
| raise ValueError("archive must contain exactly one top-level directory") | |
| for _member, relative_parts in sorted( | |
| directories, key=lambda item: len(item[1]) | |
| ): | |
| (root / Path(*relative_parts)).mkdir(parents=True, exist_ok=True) | |
| for member, relative_parts in files: | |
| destination = root / Path(*relative_parts) | |
| destination.parent.mkdir(parents=True, exist_ok=True) | |
| source = archive.extractfile(member) | |
| if source is None: | |
| raise ValueError(f"archive member is not readable: {member.name!r}") | |
| with source, destination.open("xb") as output: | |
| shutil.copyfileobj(source, output) | |
| except (OSError, tarfile.TarError, ValueError) as exc: | |
| raise SystemExit(f"trusted source archive failed closed: {exc}") from exc | |
| PY | |
| - name: Verify immutable central policy source | |
| env: | |
| EXPECTED_FILE: ${{ steps.trusted_source.outputs.workflow_file_path }} | |
| run: | | |
| set -euo pipefail | |
| trusted_source_dir="$GITHUB_WORKSPACE/.cwl-required-source" | |
| if [ ! -f "$trusted_source_dir/$EXPECTED_FILE" ] || [ -L "$trusted_source_dir/$EXPECTED_FILE" ]; then | |
| printf '::error::Required workflow source file is missing or symlinked: %s.\n' \ | |
| "$EXPECTED_FILE" | |
| exit 1 | |
| fi | |
| if [ ! -f "$trusted_source_dir/scripts/ci/pingora_edge_policy.py" ] || [ -L "$trusted_source_dir/scripts/ci/pingora_edge_policy.py" ]; then | |
| echo "::error::Trusted Pingora edge policy helper is missing or symlinked." | |
| exit 1 | |
| fi | |
| - name: Enforce Cloudflare Pingora edge policy | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} | |
| PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number || 0 }} | |
| PULL_REQUEST_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| EVENT_ACTION: ${{ github.event.action || 'unknown' }} | |
| run: | | |
| set -euo pipefail | |
| python3 .cwl-required-source/scripts/ci/pingora_edge_policy.py \ | |
| --repository "$TARGET_REPOSITORY" \ | |
| --pull-request "$PULL_REQUEST_NUMBER" \ | |
| --head-sha "$PULL_REQUEST_HEAD_SHA" \ | |
| --event-action "$EVENT_ACTION" \ | |
| --api-url "https://api.github.com" | |
| admit-current-head: | |
| name: admit-current-head | |
| needs: [required-workflow-bootstrap] | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| outputs: | |
| admitted: ${{ steps.live_head.outputs.admitted }} | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| steps: | |
| - name: Admit only the exact live OpenCode head | |
| id: live_head | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} | |
| PR_NUMBER: ${{ github.event.pull_request.number || '' }} | |
| EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || '' }} | |
| EXPECTED_ACTION: ${{ github.event.action || '' }} | |
| run: | | |
| set -euo pipefail | |
| echo "admitted=false" >>"$GITHUB_OUTPUT" | |
| if ! [[ "$TARGET_REPOSITORY" =~ ^ContextualWisdomLab/[A-Za-z0-9_.-]+$ ]] || | |
| ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || | |
| ! [[ "$EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then | |
| echo "::error::OpenCode admission rejected malformed pull request metadata." | |
| exit 1 | |
| fi | |
| live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" | |
| live_head="$(jq -r '.head.sha // empty' <<<"$live_pr")" | |
| live_state="$(jq -r '.state // empty' <<<"$live_pr")" | |
| expected_state=open | |
| [ "$EXPECTED_ACTION" = "closed" ] && expected_state=closed | |
| if [ "${live_head,,}" != "${EXPECTED_HEAD_SHA,,}" ] || [ "$live_state" != "$expected_state" ]; then | |
| echo "::notice::OpenCode admission retired a stale event before review queue entry." | |
| exit 0 | |
| fi | |
| echo "admitted=true" >>"$GITHUB_OUTPUT" | |
| echo "Exact live OpenCode head admitted for ${TARGET_REPOSITORY}#${PR_NUMBER}." | |
| coverage-source-tree: | |
| name: coverage-source-tree | |
| needs: [required-workflow-bootstrap, admit-current-head] | |
| if: needs.admit-current-head.outputs.admitted == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - run: >- | |
| echo "PR-head source and coverage execution are delegated to the | |
| authenticated default-branch OpenCode review dispatch." | |
| coverage-evidence: | |
| name: coverage-evidence | |
| # Deliberately NOT `needs: [coverage-source-tree]`. Neither job declares | |
| # `outputs:`, so that edge only ordered two single-`echo` context holders -- | |
| # and a job is not created until its `needs:` complete, so under a saturated | |
| # queue each link waits out the whole queue again. Measured on | |
| # naruon#1528 (run 33581213805): coverage-source-tree waited 9h40m to run for | |
| # 4s, then coverage-evidence waited a further 13h01m to run for 5s, holding | |
| # the actual review behind ~22h41m of pure queueing. Depending on | |
| # `admit-current-head` directly lets the two run in parallel. The `if:` below | |
| # restates the admission gate this job previously inherited transitively | |
| # through coverage-source-tree, so an unadmitted head still skips it. | |
| needs: [required-workflow-bootstrap, admit-current-head] | |
| if: needs.admit-current-head.outputs.admitted == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - run: >- | |
| echo "This required-workflow job preserves the stable branch-protection | |
| context without executing pull-request content." | |
| opencode-review-target: | |
| name: opencode-review | |
| # `coverage-evidence` is deliberately absent here. This job never reads it | |
| # at runtime -- the only consumer of that context is | |
| # `opencode-review-dispatch.yml`, which resolves it through | |
| # `scripts/ci/opencode_coverage_identity.py` against the check-runs API on | |
| # its own schedule, so it does not care when this job ran relative to it. | |
| # The edge was pure ordering, and ordering is expensive: a job is not | |
| # created until its `needs:` finish, so this link cost a further 12h13m of | |
| # queue wait on naruon#1528 (run 33581213805). Admission is still enforced | |
| # directly by this job's own `if:` below, not inherited through that edge. | |
| needs: [admit-current-head] | |
| if: needs.admit-current-head.outputs.admitted == 'true' | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| id-token: write | |
| steps: | |
| - name: Request current-head OpenCode review execution | |
| if: github.event.action != 'closed' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| OIDC_AUDIENCE: opencode-github-action | |
| OPENCODE_API_BASE_URL: https://api.opencode.ai | |
| TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| PR_DRAFT: ${{ github.event.pull_request.draft }} | |
| BASE_BRANCH: ${{ github.event.pull_request.base.ref }} | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_REF: ${{ github.event.pull_request.head.ref }} | |
| WORKFLOW_SHA: ${{ github.workflow_sha }} | |
| run: | | |
| set -euo pipefail | |
| live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" | |
| live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" | |
| live_draft="$(printf '%s' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" | |
| live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" | |
| if [ -z "$live_head" ] || [ -z "$live_draft" ] || [ -z "$live_state" ]; then | |
| echo "::error::Could not validate live pull request state before review dispatch." | |
| exit 1 | |
| fi | |
| if [ "$live_state" != "open" ] && [ "$live_state" != "closed" ]; then | |
| echo "::error::Could not validate live pull request state before review dispatch." | |
| exit 1 | |
| fi | |
| if [ "$live_state" = "closed" ]; then | |
| echo "PR is closed on the live exact head; a current-head OpenCode review is not requested." | |
| exit 0 | |
| fi | |
| if [ "$live_draft" = "true" ]; then | |
| echo "PR is still a draft on the live exact head; a current-head OpenCode review is not requested until it is marked ready for review." | |
| exit 0 | |
| fi | |
| if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then | |
| echo "Pull request head moved on the live open, ready-for-review PR; a fresh dispatch will fire for the current head." | |
| exit 0 | |
| fi | |
| if [ "$PR_DRAFT" = "true" ]; then | |
| echo "Event draft snapshot is stale; continuing current-head OpenCode review dispatch for the live ready PR." | |
| fi | |
| effective_pr_draft="$live_draft" | |
| helper="$(mktemp)" | |
| trap 'rm -f "$helper"' EXIT | |
| gh api "repos/ContextualWisdomLab/.github/contents/scripts/ci/opencode_review_receipt_gate.py?ref=${WORKFLOW_SHA}" \ | |
| --jq .content | base64 --decode >"$helper" | |
| receipt_state="$(python3 - "$helper" "$TARGET_REPOSITORY" "$PR_NUMBER" "$HEAD_SHA" "$effective_pr_draft" <<'PY' | |
| import importlib.machinery | |
| import importlib.util | |
| import sys | |
| helper_path, repository, number, head_sha, draft = sys.argv[1:] | |
| loader = importlib.machinery.SourceFileLoader( | |
| "trusted_opencode_receipt_gate", helper_path | |
| ) | |
| spec = importlib.util.spec_from_loader(loader.name, loader) | |
| if spec is None or spec.loader is None: | |
| raise RuntimeError("trusted OpenCode receipt helper could not be loaded") | |
| gate = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(gate) | |
| reviews = gate.fetch_reviews(repository, int(number)) | |
| receipt, _reason = gate.evaluate_receipts( | |
| reviews, head_sha, is_draft=draft.lower() == "true" | |
| ) | |
| print("present" if receipt is not None else "missing") | |
| PY | |
| )" | |
| if [ "$receipt_state" = "present" ]; then | |
| echo "Current-head substantive OpenCode verdict already exists; scheduler wake skipped." | |
| exit 0 | |
| fi | |
| if [ "$receipt_state" != "missing" ]; then | |
| echo "::error::Trusted OpenCode receipt helper returned an invalid state." | |
| exit 1 | |
| fi | |
| if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then | |
| echo "::error::OpenCode review dispatch requires GitHub OIDC." | |
| exit 1 | |
| fi | |
| separator='&' | |
| [[ "$ACTIONS_ID_TOKEN_REQUEST_URL" == *\?* ]] || separator='?' | |
| oidc_token="$(curl -fsS -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" "${ACTIONS_ID_TOKEN_REQUEST_URL}${separator}audience=${OIDC_AUDIENCE}" | jq -r '.value // empty')" | |
| if [ -z "$oidc_token" ]; then | |
| echo "::error::OpenCode review dispatch could not obtain its OIDC token." | |
| exit 1 | |
| fi | |
| app_token="$(curl -fsS -X POST -H "Authorization: Bearer ${oidc_token}" "${OPENCODE_API_BASE_URL}/exchange_github_app_token" | jq -r '.token // empty')" | |
| if [ -z "$app_token" ]; then | |
| echo "::error::OpenCode review dispatch could not obtain its repository-scoped app token." | |
| exit 1 | |
| fi | |
| echo "::add-mask::$app_token" | |
| jq -cn \ | |
| --arg target_repository "$TARGET_REPOSITORY" \ | |
| --arg pr_number "$PR_NUMBER" \ | |
| --arg pr_base_ref "$BASE_BRANCH" \ | |
| --arg pr_base_sha "$BASE_SHA" \ | |
| --arg pr_head_ref "$HEAD_REF" \ | |
| --arg pr_head_sha "$HEAD_SHA" \ | |
| --arg required_run_id "$GITHUB_RUN_ID" \ | |
| '{event_type:"opencode-review",client_payload:{target_repository:$target_repository,pr_number:$pr_number,pr_base_ref:$pr_base_ref,pr_base_sha:$pr_base_sha,pr_head_ref:$pr_head_ref,pr_head_sha:$pr_head_sha,required_run_id:$required_run_id}}' | | |
| GH_TOKEN="$app_token" gh api -X POST repos/ContextualWisdomLab/.github/dispatches --input - | |
| - name: Fail closed without a current-head OpenCode verdict | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| PR_ACTION: ${{ github.event.action }} | |
| PR_DRAFT: ${{ github.event.pull_request.draft }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$PR_ACTION" = "closed" ]; then | |
| echo "PR closed; a current-head OpenCode verdict is not required." | |
| exit 0 | |
| fi | |
| if [ -z "${PR_NUMBER:-}" ] || [ -z "${HEAD_SHA:-}" ]; then | |
| echo "::error::Missing PR number or head SHA; cannot verify a current-head OpenCode verdict." | |
| exit 1 | |
| fi | |
| live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" | |
| live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" | |
| live_draft="$(printf '%s' "$live_pr" | jq -r 'if (.draft | type) == "boolean" then (.draft | tostring) else empty end')" | |
| live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" | |
| if [ -z "$live_head" ] || [ -z "$live_draft" ] || [ -z "$live_state" ]; then | |
| echo "::error::Could not validate live pull request state before verdict admission." | |
| exit 1 | |
| fi | |
| if [ "$live_state" != "open" ] && [ "$live_state" != "closed" ]; then | |
| echo "::error::Could not validate live pull request state before verdict admission." | |
| exit 1 | |
| fi | |
| if [ "$live_state" = "closed" ]; then | |
| echo "PR is closed on the live exact head; a current-head OpenCode verdict is not required." | |
| exit 0 | |
| fi | |
| if [ "$live_draft" = "true" ]; then | |
| echo "PR is still a draft on the live exact head; a current-head OpenCode verdict is not required until it is marked ready for review." | |
| exit 0 | |
| fi | |
| if [ "${live_head,,}" != "${HEAD_SHA,,}" ]; then | |
| echo "Pull request head moved on the live open, ready-for-review PR; a fresh run will check the current head." | |
| exit 0 | |
| fi | |
| if [ "$PR_DRAFT" = "true" ]; then | |
| echo "Event draft snapshot is stale; checking the verdict for the live ready PR." | |
| fi | |
| reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")" | |
| verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' | |
| (add // []) | |
| | [ | |
| .[] | |
| | select( | |
| (.user.login // "" | ascii_downcase) as $user | |
| | $user == "opencode-agent" or $user == "opencode-agent[bot]" | |
| ) | |
| | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) | |
| | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") | |
| ] | |
| | (last // {}) as $review | |
| | ($review.body // "" | ascii_downcase) as $body | |
| | if $review.state == "CHANGES_REQUESTED" then | |
| "CHANGES_REQUESTED" | |
| elif $review.state == "APPROVED" | |
| and ($body | contains("deterministic current-head evidence") | not) | |
| and ($body | contains("deterministic fallback approval") | not) | |
| and ($body | contains("model-unavailable evidence fallback") | not) | |
| and ($body | contains("did not emit a usable current-head control block") | not) | |
| and ($body | contains("scope: `unsupported`") | not) | |
| and ($body | contains("model-pool outcome: `unknown`") | not) | |
| then | |
| "APPROVED" | |
| else | |
| empty | |
| end | |
| ')" | |
| if [ -z "$verdict" ]; then | |
| echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. The dispatch workflow will rerun this failed job after publishing an authenticated exact-head verdict." | |
| exit 1 | |
| fi | |
| echo "Current-head OpenCode verdict: ${verdict}." | |
| cancel-superseded-opencode-review-runs: | |
| # This job -- not the bootstrap concurrency group above -- is the primary | |
| # mechanism that actively cancels a same-PR run for an outdated head. The | |
| # bootstrap group is now `cancel-in-progress: false` (see its own comment): | |
| # nothing is ever preempted there, by design, to structurally close the | |
| # #1568 stale-cancels-fresh race regardless of arrival order. This job | |
| # achieves precise, safe "cancel only outdated runs of the same PR" | |
| # instead: it re-verifies the live PR head immediately before selecting | |
| # candidates AND immediately before every individual cancellation call, so | |
| # a cleanup run that is itself delayed/stale cannot cancel a | |
| # still-authoritative run, and it only ever targets runs whose recorded | |
| # head no longer matches the live one. The target job also revalidates the | |
| # live PR before dispatch and verdict admission. | |
| if: github.event_name == 'pull_request_target' && github.event.action == 'synchronize' | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| actions: write | |
| contents: read | |
| pull-requests: read | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} | |
| TARGET_PR_NUMBER: ${{ github.event.pull_request.number }} | |
| TARGET_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| CURRENT_RUN_ID: ${{ github.run_id }} | |
| steps: | |
| - name: Cancel queued and running OpenCode review runs for a superseded pull request head | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| live_head_matches() { | |
| local live_head | |
| if ! live_head="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${TARGET_PR_NUMBER}" --jq '.head.sha' 2>/tmp/opencode-cleanup-gh-error)"; then | |
| echo "::warning::OpenCode review cleanup could not verify the live pull request head; leaving runs unchanged." | |
| sed 's/^/ /' /tmp/opencode-cleanup-gh-error >&2 || true | |
| return 1 | |
| fi | |
| [ "${live_head,,}" = "${TARGET_PR_HEAD_SHA,,}" ] | |
| } | |
| cancel_runs() { | |
| local status="$1" | |
| if ! live_head_matches; then | |
| echo "::notice::OpenCode review cleanup target changed before run selection; leaving runs unchanged." | |
| return 0 | |
| fi | |
| local runs_url="repos/${TARGET_REPOSITORY}/actions/runs?status=${status}&per_page=100" | |
| local runs_json | |
| if ! runs_json="$(gh api --paginate "$runs_url" 2>/tmp/opencode-cleanup-gh-error)"; then | |
| echo "::warning::OpenCode review cleanup could not inspect ${TARGET_REPOSITORY}; leaving runs unchanged." | |
| sed 's/^/ /' /tmp/opencode-cleanup-gh-error >&2 || true | |
| return 0 | |
| fi | |
| local run_ids | |
| if ! run_ids="$(jq -r --arg pr "$TARGET_PR_NUMBER" --arg head_sha "$TARGET_PR_HEAD_SHA" \ | |
| --arg repo "$TARGET_REPOSITORY" --arg current "$CURRENT_RUN_ID" ' | |
| .workflow_runs[] | |
| | select((.id | tostring) != $current) | |
| | select(.name == "Required OpenCode Review") | |
| | select(.event == "pull_request_target") | |
| | ((.display_title // "") | startswith("Required OpenCode Review " + $repo + "#" + $pr + "@")) as $title_matches | |
| | ((.pull_requests // []) | any((.number | tostring) == $pr)) as $metadata_matches | |
| | select($title_matches or $metadata_matches) | |
| | ((.display_title // "") | endswith("@" + $head_sha)) as $title_is_current | |
| | ((.pull_requests // []) | any( | |
| ((.number | tostring) == $pr) | |
| and ((.head.sha // "") | ascii_downcase) == ($head_sha | ascii_downcase) | |
| )) as $metadata_is_current | |
| | select(($title_is_current or $metadata_is_current) | not) | |
| | .id | |
| ' <<<"$runs_json")"; then | |
| echo "::warning::OpenCode review cleanup received invalid run data for ${TARGET_REPOSITORY}; leaving runs unchanged." | |
| return 0 | |
| fi | |
| while IFS= read -r run_id; do | |
| [ -n "$run_id" ] || continue | |
| if ! live_head_matches; then | |
| echo "::notice::OpenCode review cleanup target changed before cancellation; leaving runs unchanged." | |
| return 0 | |
| fi | |
| if gh api --method POST "repos/${TARGET_REPOSITORY}/actions/runs/${run_id}/cancel" >/dev/null 2>/tmp/opencode-cleanup-cancel-error || | |
| gh api --method POST "repos/${TARGET_REPOSITORY}/actions/runs/${run_id}/force-cancel" >/dev/null 2>>/tmp/opencode-cleanup-cancel-error; then | |
| echo "Cancelled superseded Required OpenCode Review run ${run_id} in ${TARGET_REPOSITORY} for PR #${TARGET_PR_NUMBER}." | |
| else | |
| echo "::warning::OpenCode review cleanup could not cancel run ${run_id} in ${TARGET_REPOSITORY}; it may have finished or the credential lacks Actions write access." | |
| sed 's/^/ /' /tmp/opencode-cleanup-cancel-error >&2 || true | |
| fi | |
| done <<<"$run_ids" | |
| } | |
| for active_status in queued in_progress requested waiting pending; do | |
| cancel_runs "$active_status" | |
| done | |
| echo "Superseded OpenCode review run cleanup completed." |