Skip to content
171 changes: 171 additions & 0 deletions .github/workflows/noema-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
name: Required Noema Review

on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
workflow_run:
workflows: ["Required OpenCode Review", "Strix Security Scan"]
types: [completed]
workflow_dispatch:
inputs:
pr_number:
description: Pull request number to review
required: true
type: string
target_repository:
description: Repository that owns the pull request, in owner/name form
required: false
default: ""
type: string
canonical_ref:
description: Ref of ContextualWisdomLab/.github to use for trusted review scripts
required: false
default: main
type: string

concurrency:
group: >-
noema-review-${{ github.event_name }}-${{
github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository }}-${{
github.event_name == 'pull_request_target' && format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha) ||
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.inputs.pr_number || github.run_id }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: read
checks: read
id-token: write

jobs:
noema-review:
name: noema-review
runs-on: ubuntu-latest
if: >-
github.event_name == 'workflow_dispatch'
|| github.event_name == 'workflow_run'
|| (
github.event_name == 'pull_request_target'
&& 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.inputs.target_repository || github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number || github.event.workflow_run.pull_requests[0].number || github.event.inputs.pr_number || '' }}
steps:
- name: Resolve trusted Noema review source ref
id: trusted_source
env:
INPUT_CANONICAL_REF: ${{ github.event.inputs.canonical_ref || '' }}
WORKFLOW_REF: ${{ github.workflow_ref }}
run: |
set -euo pipefail
trusted_ref="${INPUT_CANONICAL_REF:-main}"
case "$WORKFLOW_REF" in
ContextualWisdomLab/.github/.github/workflows/noema-review.yml@*)
trusted_ref="${WORKFLOW_REF##*@}"
;;
esac
printf 'ref=%s\n' "$trusted_ref" >>"$GITHUB_OUTPUT"

- name: Checkout trusted Noema review gate
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ContextualWisdomLab/.github
ref: ${{ steps.trusted_source.outputs.ref }}
fetch-depth: 1
persist-credentials: false

- name: Exchange Noema app token
id: noema_app_token
env:
OIDC_AUDIENCE: ${{ vars.NOEMA_OIDC_AUDIENCE || 'cwl-noema-review' }}
TOKEN_EXCHANGE_URL: ${{ vars.NOEMA_TOKEN_EXCHANGE_URL || '' }}
run: |
set -euo pipefail

mark_unavailable() {
echo "available=false" >>"$GITHUB_OUTPUT"
}

if [ -z "${TOKEN_EXCHANGE_URL:-}" ]; then
echo "Noema app token exchange unavailable: NOEMA_TOKEN_EXCHANGE_URL is not configured."
mark_unavailable
exit 0
fi

if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then
echo "Noema app token exchange unavailable: OIDC request environment is missing."
mark_unavailable
exit 0
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
echo "Noema app token exchange unavailable: OIDC token request did not complete."
mark_unavailable
exit 0
fi

oidc_token="$(jq -r '.value // empty' <<<"$oidc_response")"
if [ -z "$oidc_token" ]; then
echo "Noema app token exchange unavailable: OIDC token response was empty."
mark_unavailable
exit 0
fi

if ! token_response="$(
curl -fsS \
-X POST \
-H "Authorization: Bearer ${oidc_token}" \
"${TOKEN_EXCHANGE_URL}"
)"; then
echo "Noema app token exchange unavailable: app token request did not complete."
mark_unavailable
exit 0
fi

app_token="$(jq -r '.token // empty' <<<"$token_response")"
if [ -z "$app_token" ]; then
echo "Noema app token exchange unavailable: app token response was empty."
mark_unavailable
exit 0
fi

echo "::add-mask::$app_token"
{
echo "available=true"
echo "token=$app_token"
} >>"$GITHUB_OUTPUT"

- name: Run Noema LLM review and submit verdict
env:
GH_TOKEN: ${{ steps.noema_app_token.outputs.token }}
NOEMA_REVIEW_TOKEN_SOURCE: 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 || '' }}
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 "::notice::Noema app token is unavailable; review skipped."
exit 0
fi
python3 scripts/ci/noema_review_gate.py \
--repo "$TARGET_REPOSITORY" \
--pr-number "$PR_NUMBER"
Loading