Required Noema Review ContextualWisdomLab/ContextualWisdomLab.github.io#119@a70e02cdc72462b4a3216fac4a62ab97d807aae7 #13
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 Noema Review | |
| run-name: >- | |
| Required Noema Review ${{ github.event.client_payload.target_repository || | |
| github.event.pull_request.base.repo.full_name || github.repository }}#${{ | |
| github.event.client_payload.pr_number || github.event.pull_request.number || | |
| github.event.workflow_run.pull_requests[0].number || 'event' }}@${{ | |
| github.event.client_payload.pr_head_sha || github.event.pull_request.head.sha || | |
| github.event.workflow_run.head_sha || github.sha }} | |
| on: | |
| pull_request_target: | |
| types: [opened, synchronize, reopened, ready_for_review, closed] | |
| workflow_run: | |
| workflows: ["Required OpenCode Review", "Strix Security Scan"] | |
| types: [completed] | |
| # Default-branch-only retry entrypoint; no caller-selected workflow ref. | |
| repository_dispatch: | |
| types: [noema-review] | |
| concurrency: | |
| group: >- | |
| noema-review-${{ | |
| github.event_name == 'pull_request_target' && github.event.pull_request.base.repo.full_name || | |
| github.event_name == 'repository_dispatch' && github.event.client_payload.target_repository || | |
| github.repository }}-${{ github.event_name }}-${{ | |
| github.event_name == 'pull_request_target' && format('pr-{0}', github.event.pull_request.number) || | |
| github.event_name == 'workflow_run' && github.event.workflow_run.pull_requests[0].number && format('pr-{0}', github.event.workflow_run.pull_requests[0].number) || | |
| github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number || | |
| github.run_id }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| checks: read | |
| id-token: write | |
| jobs: | |
| cancel-closed-pr-runs: | |
| if: github.event_name == 'pull_request_target' && github.event.action == 'closed' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: echo "PR closed; this run only cancels older runs through workflow concurrency." | |
| noema-review: | |
| name: noema-review | |
| runs-on: ubuntu-latest | |
| if: >- | |
| github.event_name == 'repository_dispatch' | |
| || ( | |
| github.event_name == 'workflow_run' | |
| && github.event.workflow_run.conclusion != 'cancelled' | |
| ) | |
| || ( | |
| github.event_name == 'pull_request_target' | |
| && github.event.action != 'closed' | |
| && github.event.pull_request.head.repo.full_name == github.repository | |
| ) | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.client_payload.target_repository || github.repository }} | |
| PR_NUMBER: ${{ github.event.pull_request.number || github.event.workflow_run.pull_requests[0].number || github.event.client_payload.pr_number || '' }} | |
| steps: | |
| - name: Skip events without pull request context | |
| if: env.PR_NUMBER == '' | |
| run: | | |
| echo "::notice::Noema review skipped: no pull request number is associated with this event." | |
| - name: Resolve trusted Noema review source ref | |
| if: env.PR_NUMBER != '' | |
| id: trusted_source | |
| env: | |
| JOB_CONTEXT_JSON: ${{ toJSON(job) }} | |
| GITHUB_CONTEXT_JSON: ${{ toJSON(github) }} | |
| 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 "{}") | |
| github_context = json.loads(os.environ.get("GITHUB_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) | |
| trusted_repository = str( | |
| job_context.get("workflow_repository") or "ContextualWisdomLab/.github" | |
| ).strip() | |
| trusted_ref = str( | |
| job_context.get("workflow_sha") or github_context.get("workflow_sha") or "" | |
| ).strip() | |
| workflow_ref = str( | |
| job_context.get("workflow_ref") or github_context.get("workflow_ref") or "" | |
| ).strip() | |
| if not trusted_ref: | |
| trusted_ref = "main" | |
| prefix = "ContextualWisdomLab/.github/.github/workflows/noema-review.yml@" | |
| if workflow_ref.startswith(prefix): | |
| trusted_ref = workflow_ref.split("@", 1)[1] | |
| if trusted_repository != "ContextualWisdomLab/.github": | |
| print("::error::Trusted Noema workflow repository resolved outside ContextualWisdomLab/.github.", file=sys.stderr) | |
| raise SystemExit(1) | |
| if not re.fullmatch(r"[0-9a-fA-F]{40}|refs/[^\s]+|[A-Za-z0-9._/-]+", trusted_ref): | |
| print("::error::Trusted Noema workflow ref resolved to an invalid value.", file=sys.stderr) | |
| raise SystemExit(1) | |
| print(f"repository={trusted_repository}") | |
| print(f"ref={trusted_ref}") | |
| PY | |
| - name: Materialize trusted Noema review gate | |
| if: env.PR_NUMBER != '' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TRUSTED_SOURCE_REF: ${{ steps.trusted_source.outputs.ref }} | |
| run: | | |
| set -euo pipefail | |
| if [[ ! "$TRUSTED_SOURCE_REF" =~ ^[0-9a-fA-F]{40}$ ]]; then | |
| echo "::error::Trusted Noema source ref must resolve to the immutable workflow commit SHA before archive materialization." | |
| exit 1 | |
| fi | |
| trusted_archive="${RUNNER_TEMP}/trusted-noema-source.tar.gz" | |
| api_url="${GITHUB_API_URL:-https://api.github.com}" | |
| 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}" | |
| tar -xzf "$trusted_archive" -C "$GITHUB_WORKSPACE" --strip-components=1 | |
| test -f scripts/ci/noema_review_gate.py | |
| - name: Select fail-closed Noema reviewer credential | |
| if: env.PR_NUMBER != '' | |
| id: noema_credential | |
| env: | |
| NOEMA_GITHUB_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID || '' }} | |
| NOEMA_GITHUB_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY || '' }} | |
| TOKEN_EXCHANGE_URL: ${{ vars.NOEMA_TOKEN_EXCHANGE_URL || vars.NOEMA_EXCHANGE_URL || '' }} | |
| NOEMA_REVIEW_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| if [[ ! "$TARGET_REPOSITORY" =~ ^ContextualWisdomLab/[A-Za-z0-9_.-]+$ ]]; then | |
| echo "::error::Noema target repository must belong to ContextualWisdomLab; observed ${TARGET_REPOSITORY:-<empty>}." | |
| exit 1 | |
| fi | |
| repository_name="${TARGET_REPOSITORY#*/}" | |
| echo "repository=$repository_name" >>"$GITHUB_OUTPUT" | |
| if [ -n "${NOEMA_REVIEW_TOKEN:-}" ]; then | |
| echo "source=pat" >>"$GITHUB_OUTPUT" | |
| echo "::notice::Noema reviewer using the NOEMA_REVIEW_TOKEN secret fallback identity." | |
| exit 0 | |
| fi | |
| if [ -n "${NOEMA_GITHUB_APP_CLIENT_ID:-}" ] && [ -n "${NOEMA_GITHUB_APP_PRIVATE_KEY:-}" ]; then | |
| echo "source=github-app" >>"$GITHUB_OUTPUT" | |
| echo "::notice::Noema reviewer will mint a repository-scoped cwl-noema-review installation token." | |
| exit 0 | |
| fi | |
| if [ -n "${TOKEN_EXCHANGE_URL:-}" ]; then | |
| echo "source=oidc" >>"$GITHUB_OUTPUT" | |
| echo "::notice::Noema reviewer will use the configured OIDC app-token exchange." | |
| exit 0 | |
| fi | |
| echo "::error::Noema reviewer credential is unconfigured: set NOEMA_GITHUB_APP_CLIENT_ID with NOEMA_GITHUB_APP_PRIVATE_KEY, NOEMA_REVIEW_TOKEN, or NOEMA_TOKEN_EXCHANGE_URL. Review cannot be skipped." | |
| exit 1 | |
| - name: Mint repository-scoped Noema GitHub App token | |
| if: env.PR_NUMBER != '' && steps.noema_credential.outputs.source == 'github-app' | |
| id: noema_github_app_token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| with: | |
| client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} | |
| private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} | |
| owner: ContextualWisdomLab | |
| repositories: ${{ steps.noema_credential.outputs.repository }} | |
| permission-actions: read | |
| permission-checks: read | |
| permission-contents: read | |
| permission-metadata: read | |
| permission-pull-requests: write | |
| permission-security-events: read | |
| permission-statuses: read | |
| permission-vulnerability-alerts: read | |
| - name: Exchange Noema app token through OIDC | |
| if: env.PR_NUMBER != '' && steps.noema_credential.outputs.source == 'oidc' | |
| id: noema_oidc_token | |
| env: | |
| OIDC_AUDIENCE: ${{ vars.NOEMA_OIDC_AUDIENCE || 'cwl-noema-review' }} | |
| TOKEN_EXCHANGE_URL: ${{ vars.NOEMA_TOKEN_EXCHANGE_URL || vars.NOEMA_EXCHANGE_URL || '' }} | |
| run: | | |
| set -euo pipefail | |
| fail_unavailable() { | |
| local message="$1" | |
| echo "::error::$message" | |
| exit 1 | |
| } | |
| if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then | |
| fail_unavailable "Noema app token exchange unavailable: OIDC request environment is missing." | |
| fi | |
| request_url="${ACTIONS_ID_TOKEN_REQUEST_URL}" | |
| separator="&" | |
| case "$request_url" in | |
| *\?*) ;; | |
| *) separator="?" ;; | |
| esac | |
| if ! oidc_response="$( | |
| curl -fsS \ | |
| -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \ | |
| "${request_url}${separator}audience=${OIDC_AUDIENCE}" | |
| )"; then | |
| fail_unavailable "Noema app token exchange unavailable: OIDC token request did not complete." | |
| fi | |
| oidc_token="$(jq -r '.value // empty' <<<"$oidc_response")" | |
| if [ -z "$oidc_token" ]; then | |
| fail_unavailable "Noema app token exchange unavailable: OIDC token response was empty." | |
| fi | |
| if ! token_response="$( | |
| curl -fsS \ | |
| -X POST \ | |
| -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer ${oidc_token}" \ | |
| --data "$(jq -cn --arg target_repository "$TARGET_REPOSITORY" '{target_repository:$target_repository}')" \ | |
| "${TOKEN_EXCHANGE_URL}" | |
| )"; then | |
| fail_unavailable "Noema app token exchange unavailable: app token request did not complete." | |
| fi | |
| app_token="$(jq -r '.token // empty' <<<"$token_response")" | |
| if [ -z "$app_token" ]; then | |
| fail_unavailable "Noema app token exchange unavailable: app token response was empty." | |
| fi | |
| echo "::add-mask::$app_token" | |
| echo "token=$app_token" >>"$GITHUB_OUTPUT" | |
| - name: Run Noema LLM review and submit verdict | |
| if: env.PR_NUMBER != '' | |
| env: | |
| GH_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token }} | |
| NOEMA_REVIEW_TOKEN_SOURCE: ${{ steps.noema_credential.outputs.source == 'pat' && 'noema-review-pat' || steps.noema_credential.outputs.source == 'github-app' && 'noema-review-github-app' || 'noema-review-app-oidc' }} | |
| NOEMA_LLM_API_URL: ${{ vars.NOEMA_LLM_API_URL || '' }} | |
| NOEMA_LLM_MODEL: ${{ vars.NOEMA_LLM_MODEL || '' }} | |
| NOEMA_LLM_API_KEY: ${{ secrets.NOEMA_LLM_API_KEY || secrets.OPENAI_API_KEY || '' }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${PR_NUMBER:-}" ]; then | |
| echo "No pull request number was available for this event; skipping." | |
| exit 0 | |
| fi | |
| if [ -z "${GH_TOKEN:-}" ]; then | |
| echo "::error::Noema reviewer credential selection succeeded but no token was minted; review cannot submit a verdict." | |
| exit 1 | |
| fi | |
| if [ -z "${NOEMA_LLM_API_URL:-}" ] || [ -z "${NOEMA_LLM_MODEL:-}" ] || [ -z "${NOEMA_LLM_API_KEY:-}" ]; then | |
| echo "::error::Noema LLM is unconfigured: NOEMA_LLM_API_URL, NOEMA_LLM_MODEL, and NOEMA_LLM_API_KEY (or OPENAI_API_KEY) are required." | |
| exit 1 | |
| fi | |
| python3 scripts/ci/noema_review_gate.py \ | |
| --repo "$TARGET_REPOSITORY" \ | |
| --pr-number "$PR_NUMBER" |