mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-05 08:09:22 +00:00
Merge commit '23e6b0372cb60068beb9a7bf2051ea34d4bd7500' into perf/ui-unit-test-runtime
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
name: AI Code Review
|
||||
|
||||
# This file and the engine always run from main; the PR head in ./pr is data only and is never installed, built or run.
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# labeled/unlabeled so adding or removing 'ready-for-ci' starts or resets the review.
|
||||
pull_request_target:
|
||||
branches: [main]
|
||||
# unlabeled/converted_to_draft start a run that only cancels the one in progress (see concurrency).
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled, ready_for_review, converted_to_draft]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
@@ -15,143 +18,277 @@ on:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
ignore_paired:
|
||||
description: "Review here even if a same-named o2-enterprise PR is open"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
ignore_pass_limit:
|
||||
description: "Review even if the PR already used its automatic reviews"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Workflow-level so a run whose job is skipped still cancels the paid one; unrelated label edits get a private group.
|
||||
concurrency:
|
||||
group: ai-review-${{ (contains(fromJSON('["labeled","unlabeled"]'), github.event.action) && github.event.label.name != 'ready-for-ci') && github.run_id || github.event.pull_request.number || inputs.pr_number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
review:
|
||||
# Paid CI runs only on PRs carrying the 'ready-for-ci' label; the merge queue runs unit tests on the merged code.
|
||||
if: ${{ github.event_name != 'pull_request' || (contains(github.event.pull_request.labels.*.name, 'ready-for-ci') && github.event.pull_request.draft == false) }}
|
||||
# Paid CI runs only on non-draft 'ready-for-ci' PRs, and never when vars.AI_REVIEW_ENABLED is 'false' (the kill switch).
|
||||
if: >-
|
||||
${{ vars.AI_REVIEW_ENABLED != 'false' && (github.event_name == 'workflow_dispatch' || (
|
||||
contains(github.event.pull_request.labels.*.name, 'ready-for-ci') &&
|
||||
github.event.pull_request.draft == false &&
|
||||
(github.event.action != 'labeled' || github.event.label.name == 'ready-for-ci')
|
||||
)) }}
|
||||
# Explicit name: without it GitHub serializes the whole matrix object (api_key_env, marker) into the public check name.
|
||||
name: review (${{ matrix.provider.name }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
# Concurrency is per-job (not workflow-level) so it can see `matrix`: keyed per-PR AND
|
||||
# per-provider, so provider legs get independent groups and don't cancel each other.
|
||||
concurrency:
|
||||
group: ai-review-${{ matrix.provider.name }}-${{ github.event.pull_request.number || github.event.inputs.pr_number }}
|
||||
cancel-in-progress: true
|
||||
timeout-minutes: 45
|
||||
environment:
|
||||
name: ai-review
|
||||
deployment: false
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
actions: read
|
||||
strategy:
|
||||
# One provider's failure shouldn't sink the others.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# DeepSeek-V4-Pro is the sole reviewer (the GLM-5.2 head-to-head has ended).
|
||||
# Markers must not name the model: they ship inside the posted PR comment. Adding a
|
||||
# provider? Give it a distinct `<!-- ai-code-review-<suffix> -->` marker — run-review.mjs
|
||||
# matches that shape, so two legs sharing one marker overwrite each other's comment.
|
||||
provider:
|
||||
- name: deepseek
|
||||
provider_id: deepseek-review
|
||||
model_id: deepseek-v4-pro
|
||||
model_variant: ""
|
||||
model_id: deepseek-flash
|
||||
model_variant: max
|
||||
api_key_env: DEEPSEEK_API_KEY
|
||||
marker: "<!-- ai-code-review -->"
|
||||
steps:
|
||||
# Debounce: a push inside this window cancels the run (concurrency) before anything paid starts.
|
||||
- if: github.event_name == 'pull_request'
|
||||
- if: github.event_name == 'pull_request_target'
|
||||
run: sleep 60
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: scripts/ai-review/package-lock.json
|
||||
|
||||
- name: Install AI review dependencies
|
||||
working-directory: scripts/ai-review
|
||||
run: npm ci
|
||||
|
||||
- name: Install opencode
|
||||
env:
|
||||
# Pinned so an upstream opencode release can't silently change review behavior
|
||||
# (agent/tool semantics, prompt handling) underneath this workflow. Bump
|
||||
# deliberately after testing against scripts/ai-review locally.
|
||||
OPENCODE_VERSION: "1.17.13"
|
||||
run: |
|
||||
curl -fsSL https://opencode.ai/install | bash -s -- --version "$OPENCODE_VERSION"
|
||||
echo "$HOME/.opencode/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Set trace ID
|
||||
run: echo "O2_TRACE_ID=$(openssl rand -hex 16)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Set PR number
|
||||
- name: Resolve PR
|
||||
id: pr
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "pr_number=${{ github.event.inputs.pr_number }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "pr_number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
|
||||
set -euo pipefail
|
||||
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
|
||||
echo "::error::PR number must be numeric"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check for skip marker
|
||||
id: skip
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
PR_NUMBER="${{ steps.pr.outputs.pr_number }}"
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "skip_review=false" >> "$GITHUB_OUTPUT"
|
||||
echo "force_full=${{ github.event.inputs.force_full || 'false' }}" >> "$GITHUB_OUTPUT"
|
||||
pr_json="$(gh api "repos/$REPO/pulls/$PR_NUMBER")"
|
||||
state="$(jq -r '.state' <<<"$pr_json")"
|
||||
head_repo="$(jq -r '.head.repo.full_name // ""' <<<"$pr_json")"
|
||||
head_sha="$(jq -r '.head.sha' <<<"$pr_json")"
|
||||
base_sha="$(jq -r '.base.sha' <<<"$pr_json")"
|
||||
{
|
||||
echo "number=$PR_NUMBER"
|
||||
echo "head_sha=$head_sha"
|
||||
echo "head_ref=$(jq -r '.head.ref' <<<"$pr_json")"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
if [ "$state" != "open" ]; then
|
||||
echo "PR #$PR_NUMBER is $state; nothing to review."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check PR body and comments for skip markers
|
||||
PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body' 2>/dev/null || echo "")
|
||||
COMMENTS=$(gh api "repos/${{ github.repository }}/issues/$PR_NUMBER/comments" --jq '.[].body' 2>/dev/null || echo "")
|
||||
|
||||
if echo "$PR_BODY$COMMENTS" | grep -qiE '\[skip (ai )?review\]|\[skip ci\]|break glass'; then
|
||||
echo "Skip marker found. Skipping AI review."
|
||||
echo "skip_review=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "skip_review=false" >> "$GITHUB_OUTPUT"
|
||||
# Fork PRs need the on-request review of v2 (WP-14); until then they are skipped, never run with secrets.
|
||||
if [ "$head_repo" != "$REPO" ]; then
|
||||
echo "PR #$PR_NUMBER comes from a fork ($head_repo); skipped."
|
||||
exit 0
|
||||
fi
|
||||
echo "force_full=false" >> "$GITHUB_OUTPUT"
|
||||
merge_base="$(gh api "repos/$REPO/compare/$base_sha...$head_sha" --jq '.merge_base_commit.sha' || true)"
|
||||
echo "merge_base=$merge_base" >> "$GITHUB_OUTPUT"
|
||||
echo "REVIEW_RUN=true" >> "$GITHUB_ENV"
|
||||
|
||||
# Mint a GitHub App installation token so the review comment is authored by the
|
||||
# "OpenObserve Code Reviewer" app (branded name + avatar) instead of github-actions[bot].
|
||||
# Gated on the OO_REVIEWER_APP_ID repo/org variable: if the app isn't configured the step is
|
||||
# skipped. `continue-on-error` makes it fail-safe even when the variable IS set but the app
|
||||
# isn't installed yet (or the key is wrong): minting fails, the token output is empty, and
|
||||
# the run step's `|| github.token` fallback posts as github-actions[bot] instead of breaking
|
||||
# CI. So this is safe at every rollout stage; it auto-activates once the app is installed and
|
||||
# OO_REVIEWER_APP_ID + OO_REVIEWER_APP_PRIVATE_KEY are provisioned.
|
||||
# If minting fails (app not installed, bad key) the steps below fall back to github.token.
|
||||
- name: Generate OpenObserve Code Reviewer app token
|
||||
id: app_token
|
||||
if: ${{ steps.skip.outputs.skip_review == 'false' && vars.OO_REVIEWER_APP_ID != '' }}
|
||||
if: env.REVIEW_RUN == 'true' && vars.OO_REVIEWER_APP_ID != ''
|
||||
continue-on-error: true
|
||||
# Pinned to a full commit SHA (not a mutable tag) because this action receives the app's
|
||||
# signing private key — a repointed/compromised release could exfiltrate it. Same pin
|
||||
# discipline as OPENCODE_VERSION above. Bump deliberately after review.
|
||||
# Pinned to a full commit SHA because this action receives the app's signing private key.
|
||||
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
|
||||
with:
|
||||
app-id: ${{ vars.OO_REVIEWER_APP_ID }}
|
||||
private-key: ${{ secrets.OO_REVIEWER_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Run AI Code Review
|
||||
if: steps.skip.outputs.skip_review == 'false'
|
||||
# Without it (app not installed on o2-enterprise, or no actions:write) the pairing check is skipped and the PR is reviewed here.
|
||||
- name: Generate app token for the paired o2-enterprise lookup
|
||||
id: ent_token
|
||||
if: env.REVIEW_RUN == 'true' && vars.OO_REVIEWER_APP_ID != '' && !inputs.ignore_paired
|
||||
continue-on-error: true
|
||||
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
|
||||
with:
|
||||
app-id: ${{ vars.OO_REVIEWER_APP_ID }}
|
||||
private-key: ${{ secrets.OO_REVIEWER_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: o2-enterprise
|
||||
permission-actions: write
|
||||
permission-pull-requests: read
|
||||
|
||||
- name: Check out review engine (trusted, from main)
|
||||
if: env.REVIEW_RUN == 'true'
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: engine
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
sparse-checkout: |
|
||||
scripts/ai-review
|
||||
.claude/skills/ui-architect
|
||||
.claude/skills/eslint-error-handling
|
||||
|
||||
# Paired OSS+ENT changes are reviewed once, by the o2-enterprise workflow, which pulls this PR's diff in.
|
||||
- name: Defer to a paired o2-enterprise PR
|
||||
if: env.REVIEW_RUN == 'true' && !inputs.ignore_paired
|
||||
env:
|
||||
# App token when the app is configured (comment posts as OpenObserve Code Reviewer),
|
||||
# else github.token (posts as github-actions[bot]). Same engine either way.
|
||||
ENT_TOKEN: ${{ steps.ent_token.outputs.token }}
|
||||
GH_TOKEN: ${{ steps.app_token.outputs.token || github.token }}
|
||||
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
|
||||
FORCE_FULL: ${{ steps.skip.outputs.force_full }}
|
||||
# Provider selection for this matrix leg (see run-review.mjs for how these are used).
|
||||
BOT_LOGIN: ${{ steps.app_token.outputs.token && format('{0}[bot]', steps.app_token.outputs.app-slug) || 'github-actions[bot]' }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ steps.pr.outputs.number }}
|
||||
BRANCH: ${{ steps.pr.outputs.head_ref }}
|
||||
MARKER: ${{ matrix.provider.marker }}
|
||||
run: |
|
||||
set -uo pipefail
|
||||
if [ -z "$ENT_TOKEN" ]; then
|
||||
echo "::warning::No app token for o2-enterprise; cannot look for a paired PR, reviewing here."
|
||||
exit 0
|
||||
fi
|
||||
ent_repo=openobserve/o2-enterprise
|
||||
# Any lookup error falls back to reviewing here: a missed pairing costs a duplicate review, a failed step costs the review.
|
||||
if ! ent_json="$(GH_TOKEN="$ENT_TOKEN" gh pr list --repo "$ent_repo" --head "$BRANCH" --state open \
|
||||
--json number,isCrossRepository,isDraft,labels \
|
||||
--jq '[.[] | select(.isCrossRepository == false)][0] // {}')"; then
|
||||
echo "::warning::Could not look up a paired $ent_repo PR; reviewing here."
|
||||
exit 0
|
||||
fi
|
||||
ent_pr="$(jq -r '.number // ""' <<<"$ent_json")"
|
||||
if [ -z "$ent_pr" ]; then
|
||||
echo "No open $ent_repo PR on branch '$BRANCH'; reviewing here."
|
||||
exit 0
|
||||
fi
|
||||
ent_ready="$(jq -r '(.isDraft | not) and ([.labels[].name] | index("ready-for-ci") != null)' <<<"$ent_json")"
|
||||
if [ "$ent_ready" = "true" ]; then
|
||||
if ! GH_TOKEN="$ENT_TOKEN" gh workflow run ai-code-review.yml --repo "$ent_repo" --ref main -f pr_number="$ent_pr"; then
|
||||
echo "::warning::Could not dispatch the $ent_repo review; reviewing here."
|
||||
exit 0
|
||||
fi
|
||||
state=reviewed
|
||||
echo "::notice::Paired with $ent_repo#$ent_pr; dispatched its review instead of reviewing here."
|
||||
else
|
||||
state=pending
|
||||
echo "::notice::Paired with $ent_repo#$ent_pr, which is a draft or lacks ready-for-ci; it reviews both PRs once it is ready."
|
||||
fi
|
||||
bash engine/scripts/ai-review/post-paired-pointer.sh "$REPO" "$PR_NUMBER" "$MARKER" "$ent_repo#$ent_pr" "$state" "$BOT_LOGIN" \
|
||||
|| echo "::warning::Could not post the pointer comment."
|
||||
echo "REVIEW_RUN=false" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Check out PR head (data only)
|
||||
if: env.REVIEW_RUN == 'true'
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
ref: ${{ steps.pr.outputs.head_sha }}
|
||||
path: pr
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
# The engine never touches the network with git; this one fetch makes the local-diff fallback possible.
|
||||
- name: Fetch merge base for large diffs
|
||||
if: env.REVIEW_RUN == 'true' && steps.pr.outputs.merge_base != ''
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
MERGE_BASE: ${{ steps.pr.outputs.merge_base }}
|
||||
run: |
|
||||
auth="$(printf 'x-access-token:%s' "$GH_TOKEN" | base64 -w0)"
|
||||
git -C pr -c http.extraheader="AUTHORIZATION: basic $auth" fetch --no-tags --depth=1 origin "$MERGE_BASE" \
|
||||
|| echo "::warning::Could not fetch merge base $MERGE_BASE; diffs over the API limit will fail."
|
||||
|
||||
# Sanitize deletes the PR's AGENTS.md/CLAUDE.md files, so the engine gets the rules of the engine's commit from outside ./pr.
|
||||
# CLAUDE.md stands in wherever there is no AGENTS.md.
|
||||
- name: Fetch repo rules (base branch)
|
||||
if: env.REVIEW_RUN == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
rules="$RUNNER_TEMP/review-rules"
|
||||
mkdir -p "$rules"
|
||||
files="$GITHUB_WORKSPACE/engine/AGENTS.md"
|
||||
if [ ! -f "$files" ] && [ -f "$GITHUB_WORKSPACE/engine/CLAUDE.md" ]; then
|
||||
files="$GITHUB_WORKSPACE/engine/CLAUDE.md"
|
||||
fi
|
||||
fetched=""
|
||||
for name in AGENTS.md CLAUDE.md; do
|
||||
if gh api "repos/$REPO/contents/web/$name?ref=$GITHUB_SHA" -H "Accept: application/vnd.github.raw" > "$rules/web.$name.txt" 2>/dev/null; then
|
||||
files="$files"$'\n'"$rules/web.$name.txt"
|
||||
fetched=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$fetched" ]; then
|
||||
echo "::warning::Could not fetch web/AGENTS.md or web/CLAUDE.md at $GITHUB_SHA; reviewing without the frontend rules."
|
||||
fi
|
||||
{
|
||||
echo "REVIEW_RULES_FILES<<EOF_REVIEW_RULES"
|
||||
echo "$files"
|
||||
echo "EOF_REVIEW_RULES"
|
||||
} >> "$GITHUB_ENV"
|
||||
|
||||
- name: Set up Node.js
|
||||
if: env.REVIEW_RUN == 'true'
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: engine/scripts/ai-review/package-lock.json
|
||||
|
||||
- name: Install engine dependencies
|
||||
if: env.REVIEW_RUN == 'true'
|
||||
working-directory: engine/scripts/ai-review
|
||||
run: npm ci --ignore-scripts
|
||||
|
||||
- name: Install opencode (pinned, checksum verified)
|
||||
if: env.REVIEW_RUN == 'true'
|
||||
run: |
|
||||
bash engine/scripts/ai-review/install-opencode.sh "$RUNNER_TEMP/opencode"
|
||||
echo "OPENCODE_BIN=$RUNNER_TEMP/opencode/opencode" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Set trace ID
|
||||
if: env.REVIEW_RUN == 'true'
|
||||
run: echo "O2_TRACE_ID=$(openssl rand -hex 16)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run AI Code Review
|
||||
if: env.REVIEW_RUN == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app_token.outputs.token || github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ steps.pr.outputs.number }}
|
||||
HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
|
||||
MERGE_BASE_SHA: ${{ steps.pr.outputs.merge_base }}
|
||||
ENGINE_DIR: ${{ github.workspace }}/engine
|
||||
PR_DIR: ${{ github.workspace }}/pr
|
||||
FORCE_FULL: ${{ inputs.force_full || 'false' }}
|
||||
REVIEW_PROVIDER_ID: ${{ matrix.provider.provider_id }}
|
||||
REVIEW_MODEL_ID: ${{ matrix.provider.model_id }}
|
||||
REVIEW_MODEL_VARIANT: ${{ matrix.provider.model_variant }}
|
||||
REVIEW_API_KEY_ENV: ${{ matrix.provider.api_key_env }}
|
||||
REVIEW_MARKER: ${{ matrix.provider.marker }}
|
||||
# run-review.mjs reads only the key named by REVIEW_API_KEY_ENV.
|
||||
# opencode.ci.jsonc: every tool (shell, edit, webfetch) allowed with no prompts; "default" is the read-only opencode.jsonc.
|
||||
REVIEW_OPENCODE_CONFIG: ci-full
|
||||
# Empty under the github.token fallback: any workflow a same-repo PR adds can also post as github-actions[bot].
|
||||
REVIEW_BOT_LOGIN: ${{ steps.app_token.outputs.token && format('{0}[bot]', steps.app_token.outputs.app-slug) || '' }}
|
||||
# Review-only key held in the ai-review environment (the org-wide key is readable by any same-repo PR workflow).
|
||||
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
||||
O2_TRACE_INGEST_URL: ${{ secrets.O2_TRACE_INGEST_URL }}
|
||||
O2_TRACE_ORG: ${{ secrets.O2_TRACE_ORG }}
|
||||
@@ -161,8 +298,20 @@ jobs:
|
||||
O2_TRACE_STREAM: ${{ secrets.O2_TRACE_STREAM }}
|
||||
O2_TRACE_EXPORT_TIMEOUT_MS: ${{ vars.O2_TRACE_EXPORT_TIMEOUT_MS }}
|
||||
O2_TRACE_EXPORT_BATCH_SIZE: ${{ vars.O2_TRACE_EXPORT_BATCH_SIZE }}
|
||||
run: node scripts/ai-review/run-review.mjs
|
||||
# Set the variable to 'true' to also post inline threads; the summary comment always posts.
|
||||
REVIEW_INLINE: ${{ vars.AI_REVIEW_INLINE || 'false' }}
|
||||
# Automatic reviews per PR (engine default 2); the manual input lifts the cap for one run.
|
||||
REVIEW_MAX_PASSES: ${{ vars.AI_REVIEW_MAX_PASSES }}
|
||||
REVIEW_IGNORE_PASS_LIMIT: ${{ inputs.ignore_pass_limit || 'false' }}
|
||||
REVIEW_ARTIFACT_DIR: ${{ runner.temp }}/ai-review-findings
|
||||
run: node engine/scripts/ai-review/run-review.mjs
|
||||
|
||||
- name: Log skip
|
||||
if: steps.skip.outputs.skip_review == 'true'
|
||||
run: echo "AI Code Review skipped due to skip marker or break glass"
|
||||
# Parsed reviewer findings and the coordinator's kept list (model output only, scrubbed), for grading coordinator drops.
|
||||
- name: Upload review findings
|
||||
if: always() && env.REVIEW_RUN == 'true'
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: ai-review-findings-${{ matrix.provider.name }}-${{ github.run_attempt }}
|
||||
path: ${{ runner.temp }}/ai-review-findings/
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
name: AI Review Engine Tests
|
||||
|
||||
# Runs the PR's own engine code, so this workflow gets no secrets and no write token.
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "scripts/ai-review/**"
|
||||
- "opencode.jsonc"
|
||||
- "opencode.ci.jsonc"
|
||||
- ".github/workflows/ai-code-review.yml"
|
||||
- ".github/workflows/ai-review-tests.yml"
|
||||
types: [opened, synchronize, reopened, labeled, ready_for_review]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ai-review-tests-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# Same rule as the rest of CI: only non-draft PRs carrying 'ready-for-ci'.
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'ready-for-ci') && github.event.pull_request.draft == false }}
|
||||
name: engine tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: scripts/ai-review/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: scripts/ai-review
|
||||
run: npm ci --ignore-scripts
|
||||
|
||||
- name: Unit tests
|
||||
working-directory: scripts/ai-review
|
||||
run: npm test
|
||||
|
||||
- name: Install opencode (pinned, checksum verified)
|
||||
run: |
|
||||
bash scripts/ai-review/install-opencode.sh "$RUNNER_TEMP/opencode"
|
||||
echo "OPENCODE_BIN=$RUNNER_TEMP/opencode/opencode" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Sandbox integration tests
|
||||
working-directory: scripts/ai-review
|
||||
run: npm run test:integration
|
||||
@@ -85,6 +85,8 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Blobless: the diff needs commits and trees only, never file contents.
|
||||
filter: blob:none
|
||||
|
||||
# One shared classification for every suite (see .github/actions/classify-changes); a change to
|
||||
# this workflow file re-tests itself on top of it.
|
||||
@@ -278,6 +280,7 @@ jobs:
|
||||
with:
|
||||
# Full history only where the selection diff needs it; merge_group never diffs.
|
||||
fetch-depth: ${{ github.event_name == 'pull_request' && '0' || '1' }}
|
||||
filter: blob:none
|
||||
- name: Selection contract tests
|
||||
run: node --test .github/scripts/build-ci-matrix.test.js
|
||||
- id: gen
|
||||
@@ -572,11 +575,6 @@ jobs:
|
||||
install_browser "chromium_headless_shell" "headless_shell" \
|
||||
"chromium/${CHROMIUM_REV}/chromium-headless-shell-linux.zip"
|
||||
|
||||
# ffmpeg is required for video recording (video: retain-on-failure in playwright.config.js)
|
||||
# The custom install_browser uses CHROMIUM_REV which doesn't match ffmpeg's own revision,
|
||||
# so use the local playwright binary (pinned via package-lock.json) to install ffmpeg only.
|
||||
./node_modules/.bin/playwright install ffmpeg
|
||||
|
||||
# Get list of files to run using join function
|
||||
FILE_LIST="${{ join(matrix.run_files, ' ') }}"
|
||||
echo "DEBUG: FILE_LIST = $FILE_LIST"
|
||||
|
||||
@@ -209,6 +209,10 @@ jobs:
|
||||
if: steps.guard.outputs.skip == 'false'
|
||||
run: pip install -r scripts/translations/requirements.txt
|
||||
|
||||
- name: Set trace ID
|
||||
if: steps.guard.outputs.skip == 'false'
|
||||
run: echo "O2_TRACE_ID=$(openssl rand -hex 16)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run translation script
|
||||
id: run_translation
|
||||
if: steps.guard.outputs.skip == 'false'
|
||||
@@ -232,6 +236,14 @@ jobs:
|
||||
env:
|
||||
INPUT_LANGUAGES: ${{ inputs.languages }}
|
||||
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
||||
O2_TRACE_INGEST_URL: ${{ secrets.O2_TRACE_INGEST_URL }}
|
||||
O2_TRACE_ORG: ${{ secrets.O2_TRACE_ORG }}
|
||||
O2_TRACE_USERNAME: ${{ secrets.O2_TRACE_USERNAME }}
|
||||
O2_TRACE_PASSWORD: ${{ secrets.O2_TRACE_PASSWORD }}
|
||||
O2_TRACE_AUTH: ${{ secrets.O2_TRACE_AUTH }}
|
||||
O2_TRACE_STREAM: ${{ secrets.O2_TRACE_STREAM }}
|
||||
O2_TRACE_EXPORT_TIMEOUT_MS: ${{ vars.O2_TRACE_EXPORT_TIMEOUT_MS }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
cd scripts/translations
|
||||
ARGS=()
|
||||
|
||||
Reference in New Issue
Block a user