Merge commit '23e6b0372cb60068beb9a7bf2051ea34d4bd7500' into perf/ui-unit-test-runtime

This commit is contained in:
sai nikhil kethe
2026-10-29 12:10:16 +05:30
440 changed files with 45715 additions and 3571 deletions
+243 -94
View File
@@ -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
+55
View File
@@ -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
+3 -5
View File
@@ -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"
+12
View File
@@ -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=()