Skip to content

feat: livekit cast improvements #3865

feat: livekit cast improvements

feat: livekit cast improvements #3865

name: Claude Review
on:
workflow_dispatch:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
pull_request:
types: [opened, synchronize, ready_for_review]
jobs:
auto-review:
if: |
github.event_name == 'pull_request' &&
github.event.pull_request.draft == false &&
!contains(github.event.pull_request.labels.*.name, 'no review') &&
!contains(github.event.pull_request.labels.*.name, 'auto-pr')
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: write
issues: write
statuses: write
steps:
- name: Check PR type
id: check-type
uses: actions/github-script@v7
with:
script: |
const title = context.payload.pull_request.title || '';
const isAutoReview = /^(fix|chore)(\(.*?\))?!?:/i.test(title);
core.setOutput('is-auto-review', isAutoReview.toString());
- name: Set status to pending
if: steps.check-type.outputs.is-auto-review == 'true'
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.pull_request.head.sha,
state: 'pending',
context: 'Claude Review',
description: 'Claude auto-review in progress...'
});
- name: Checkout repository
if: steps.check-type.outputs.is-auto-review == 'true'
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 1
- name: Load review instructions
if: steps.check-type.outputs.is-auto-review == 'true'
id: prompt
run: echo "instructions<<PROMPT_EOF" >> "$GITHUB_OUTPUT" && cat .github/prompts/review-instructions.md >> "$GITHUB_OUTPUT" && echo "PROMPT_EOF" >> "$GITHUB_OUTPUT"
- name: Claude Review
if: steps.check-type.outputs.is-auto-review == 'true'
id: claude
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
track_progress: true
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number }}
This is an automatic review for a fix / chore PR. Review the full PR diff.
${{ steps.prompt.outputs.instructions }}
claude_args: |
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr review:*)"
--max-turns 40
- name: Upload Claude execution output
if: steps.check-type.outputs.is-auto-review == 'true' && always()
uses: actions/upload-artifact@v4
with:
name: claude-execution-output-${{ github.event.pull_request.number }}-${{ github.run_attempt }}
path: /home/runner/work/_temp/claude-execution-output.json
if-no-files-found: warn
retention-days: 30
- name: Set status and approve if eligible
if: steps.check-type.outputs.is-auto-review == 'true' && always()
uses: actions/github-script@v7
with:
script: |
const prNumber = ${{ github.event.pull_request.number }};
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
per_page: 100
});
const claudeComment = comments.data
.reverse()
.find(c => c.user?.login === 'claude[bot]');
const output = claudeComment?.body ?? '';
console.log('Claude comment preview:', output.slice(0, 300));
const failed = output.includes('REVIEW_RESULT: FAIL');
const errored = '${{ steps.claude.outcome }}' === 'failure';
const isSimple = output.includes('COMPLEXITY: SIMPLE');
const qaRequired = !output.includes('QA_REQUIRED: NO');
const hasReviewResult = output.includes('REVIEW_RESULT:');
const passed = !failed && !errored;
if (passed && isSimple) {
const approvalBody = qaRequired
? 'Auto-approved by Claude — simple fix/chore with no blocking issues. QA approval is still required.'
: 'Auto-approved by Claude — simple fix/chore with no blocking issues. No QA needed (non-runtime changes only).';
await github.rest.pulls.createReview({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
event: 'APPROVE',
body: approvalBody
});
const labels = ['claude-approved'];
if (!qaRequired) {
labels.push('no QA needed');
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels
});
} else if (passed && !isSimple) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: '🔍 Claude reviewed this PR and found no blocking issues, but assessed it as **complex** — human DEV review is still required before merging.'
});
}
let description;
if (errored && !output) {
description = 'Claude review failed — credit balance too low or API error';
} else if (errored && output && !hasReviewResult) {
description = 'Claude review failed — reached maximum number of turns';
} else if (errored) {
description = 'Claude review encountered an error';
} else if (failed) {
description = 'Claude found issues that must be resolved';
} else if (isSimple) {
description = 'No blocking issues found — PR auto-approved';
} else {
description = 'No blocking issues — complex change, DEV review required';
}
const state = (failed || errored) ? 'failure' : 'success';
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: '${{ github.event.pull_request.head.sha }}',
state,
context: 'Claude Review',
description
});
- name: Re-run failed enforce-approvals check
if: steps.check-type.outputs.is-auto-review == 'true' && always()
env:
GH_TOKEN: ${{ secrets.ORG_ACCESS_TOKEN }}
run: |
SHA="${{ github.event.pull_request.head.sha }}"
echo "Searching for failed enforce-approvals run for SHA $SHA..."
WORKFLOW_RUN_ID=$(gh run list \
--workflow="Enforce QA and DEV Approvals" \
--limit 100 \
--repo "${{ github.repository }}" \
--json databaseId,event,headSha,conclusion \
| jq -r --arg SHA "$SHA" '
.[] |
select(.event == "pull_request") |
select(.headSha == $SHA) |
select(.conclusion == "failure") |
.databaseId
' | head -n 1)
if [ -z "$WORKFLOW_RUN_ID" ]; then
echo "No failed enforce-approvals run found for this commit."
exit 0
fi
echo "Re-running failed enforce-approvals workflow: $WORKFLOW_RUN_ID"
gh run rerun "$WORKFLOW_RUN_ID" --repo "${{ github.repository }}"
echo "Re-run triggered successfully."
review:
if: |
github.event.issue.pull_request != null &&
(
contains(github.event.comment.body, '@claude review') ||
contains(github.event.comment.body, '@claude re-review') ||
contains(github.event.comment.body, '@claude approve')
) &&
!contains(github.event.issue.labels.*.name, 'no review') &&
!contains(github.event.issue.labels.*.name, 'auto-pr')
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: write
issues: write
statuses: write
steps:
- name: Get PR head SHA
id: pr
uses: actions/github-script@v7
with:
script: |
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
core.setOutput('sha', pr.data.head.sha);
core.setOutput('is_draft', pr.data.draft);
- name: Set status to pending
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: '${{ steps.pr.outputs.sha }}',
state: 'pending',
context: 'Claude Review',
description: 'Claude review in progress...'
});
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ steps.pr.outputs.sha }}
fetch-depth: 1
- name: Load review instructions
id: prompt
run: echo "instructions<<PROMPT_EOF" >> "$GITHUB_OUTPUT" && cat .github/prompts/review-instructions.md >> "$GITHUB_OUTPUT" && echo "PROMPT_EOF" >> "$GITHUB_OUTPUT"
- name: Claude Review
id: claude
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
track_progress: true
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.issue.number }}
TRIGGER: ${{ github.event.comment.body }}
${{ contains(github.event.comment.body, '@claude re-review') &&
'A re-review has been requested. Focus on whether previously raised issues have been addressed. Check prior review comments on this PR before assessing the diff.' ||
'A review has been requested. Review the full PR diff.' }}
${{ steps.prompt.outputs.instructions }}
claude_args: |
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr review:*)"
--max-turns 40
- name: Upload Claude execution output
if: always()
uses: actions/upload-artifact@v4
with:
name: claude-execution-output-${{ github.event.issue.number }}-${{ github.run_attempt }}
path: /home/runner/work/_temp/claude-execution-output.json
if-no-files-found: warn
retention-days: 30
- name: Set final commit status
if: always()
uses: actions/github-script@v7
with:
script: |
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ github.event.issue.number }},
per_page: 100
});
const claudeComment = comments.data
.reverse()
.find(c => c.user?.login === 'claude[bot]');
const output = claudeComment?.body ?? '';
console.log('Claude comment preview:', output.slice(0, 200));
const failed = output.includes('REVIEW_RESULT: FAIL');
const errored = '${{ steps.claude.outcome }}' === 'failure';
const hasReviewResult = output.includes('REVIEW_RESULT:');
let description;
if (errored && !output) {
description = 'Claude review failed — credit balance too low or API error';
} else if (errored && output && !hasReviewResult) {
description = 'Claude review failed — reached maximum number of turns';
} else if (errored) {
description = 'Claude review encountered an error';
} else if (failed) {
description = 'Claude found issues that must be resolved';
} else {
description = 'No blocking issues found';
}
const state = (failed || errored) ? 'failure' : 'success';
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: '${{ steps.pr.outputs.sha }}',
state,
context: 'Claude Review',
description
});