Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions .github/workflows/pr-agent-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: PR Review (Qodo + DeepSeek V4 Pro via OpenRouter)

# Generic PR-Agent workflow template.
# Source of truth: D:\Windows-CI\Templates\pr-agent-workflow.yml
# Deployed to consumer repos via D:\Windows-CI\Scripts\Add-PRAgentToRepo.ps1
#
# How it works:
# - Triggers on PR open / push / slash commands (/review, /describe, /improve, /ask)
# - Runs on GitHub-hosted ubuntu-latest. PR-Agent is a Docker container action
# that ONLY runs on Linux; Windows self-hosted runners cannot execute it.
# GitHub-hosted ubuntu-latest minutes are free up to 2000/month for personal
# repos, which covers ~1000 PR reviews at ~2 min/run.
Comment on lines +9 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Header docs are out of sync with runtime runner selection.

Line 9 says this runs on ubuntu-latest, but Line 89 uses a self-hosted runner. This drift will mislead maintenance and cost/privacy expectations.

✏️ Proposed fix
-# - Runs on GitHub-hosted ubuntu-latest. PR-Agent is a Docker container action
-#   that ONLY runs on Linux; Windows self-hosted runners cannot execute it.
-#   GitHub-hosted ubuntu-latest minutes are free up to 2000/month for personal
-#   repos, which covers ~1000 PR reviews at ~2 min/run.
+# - Runs on a self-hosted Linux runner (WSL2 Ubuntu on the host machine).
+#   PR-Agent is a Docker container action and requires Linux execution context.
+#   GitHub-hosted minutes are not used by this workflow unless runs-on is changed.

Also applies to: 89-89

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pr-agent-review.yml around lines 9 - 12, The header
comment claiming the workflow "runs on ubuntu-latest" is out of sync with the
actual job runner selection; locate the workflow's runs-on keys (search for
"runs-on:" and the "self-hosted" value used in the job, e.g., the job with
runs-on: self-hosted at the later section) and either update the header comment
to accurately describe that this uses a self-hosted runner (privacy/cost note)
or change the job's runs-on to "ubuntu-latest" if you intended GitHub-hosted
runners; ensure the "runs-on" value and the top comment are consistent.

# - Calls Qodo PR-Agent → OpenRouter → DeepSeek V4 Pro
# - Posts auto-description + auto-review on every PR
#
# Cost: ~$0.015-0.03 per PR via OpenRouter LLM tokens, plus GitHub Actions
# minutes (free within quota; $0.008/min Linux beyond 2000 min/mo for private repos).
# Privacy: US-hosted (OpenRouter for LLM, GitHub-hosted Linux for runner).
# Diff is sent to (a) OpenRouter→inference provider and (b) GitHub's ephemeral
# runner during execution. If trading code in diff is too sensitive even for
# ephemeral GitHub runners, options are:
# 1. Use a self-hosted Linux runner (e.g. WSL2 Ubuntu via
# Install-Wsl2LinuxRunner.ps1) and change runs-on to those labels.
# 2. Convert to PR-Agent's Python CLI (pip install pr-agent) running
# directly on a Windows self-hosted runner without the container action.
#
# Customization per-repo:
# - paths-ignore: edit to match repo's docs/configs that don't need review
# - CONFIG.MODEL: swap to a different model if needed
# - GITHUB_ACTION_CONFIG.AUTO_IMPROVE: enable for code suggestions on every PR

on:
pull_request:
types: [opened, ready_for_review, synchronize]
paths-ignore:
- "**.md"
- "**.txt"
- "**.rst"
- "**.png"
- "**.jpg"
- "**.jpeg"
- "**.gif"
- "**.pdf"
- "**.docx"
- "**.pptx"
- "**.xlsx"
- ".gitignore"
- ".gitattributes"
- "LICENSE"
- "CHANGELOG.md"
- "docs/**"
- ".github/**/*.md"

# Allow PR comments like /review, /describe, /improve, /ask to trigger PR-Agent
issue_comment:
types: [created]

workflow_dispatch:
inputs:
pr_number:
description: "PR number (manual trigger)"
required: true
type: string
Comment on lines +58 to +63
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Event branches handled by PR-Agent GitHub action runner:"
curl -fsSL https://raw.githubusercontent.com/qodo-ai/pr-agent/main/pr_agent/servers/github_action_runner.py \
  | rg -n 'GITHUB_EVENT_NAME ==|pull_request_target|issue_comment|pull_request_review_comment|workflow_dispatch'

Repository: singanuk/test1

Length of output: 508


🏁 Script executed:

cat -n .github/workflows/pr-agent-review.yml | sed -n '45,75p'

Repository: singanuk/test1

Length of output: 1359


Remove workflow_dispatch trigger and its unused condition from the workflow.

PR-Agent's GitHub action runner does not support manual dispatch triggers. The verification shows it only handles pull_request, pull_request_target, issue_comment, and pull_request_review_comment events. While workflow_dispatch is declared with a pr_number input and included in the job condition (line 72), manual dispatch triggers will execute the job but the action will not process them, resulting in no-ops.

🔧 Fix
-  workflow_dispatch:
-    inputs:
-      pr_number:
-        description: "PR number (manual trigger)"
-        required: true
-        type: string

And at line 72, replace:

-      github.event_name == 'workflow_dispatch'
+      false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
workflow_dispatch:
inputs:
pr_number:
description: "PR number (manual trigger)"
required: true
type: string
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pr-agent-review.yml around lines 48 - 53, Remove the
manual dispatch trigger and its unused input: delete the workflow_dispatch block
(including the pr_number input) from the workflow header and then remove the job
condition that checks for the manual dispatch (the condition referencing
workflow_dispatch / inputs.pr_number or github.event_name ==
'workflow_dispatch') so the workflow only relies on supported events
(pull_request, pull_request_target, issue_comment, pull_request_review_comment);
ensure no remaining references to pr_number remain in the file.


# Cancel any in-flight review when a new commit lands on the PR
concurrency:
group: pr-agent-${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }}
cancel-in-progress: true

jobs:
pr_agent:
if: |
(github.event_name == 'pull_request' &&
github.event.pull_request.draft == false &&
github.event.pull_request.user.type != 'Bot') ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request != null &&
(startsWith(github.event.comment.body, '/review') ||
startsWith(github.event.comment.body, '/describe') ||
startsWith(github.event.comment.body, '/improve') ||
startsWith(github.event.comment.body, '/ask'))) ||
Comment on lines +76 to +81
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Slash-command trigger is too permissive for public PR comments.

Any commenter on a PR can currently trigger /review, /describe, /improve, or /ask, which can be abused for cost burn/noise.

🔒 Proposed hardening
       (github.event_name == 'issue_comment' &&
        github.event.issue.pull_request != null &&
+       github.event.comment.user.type != 'Bot' &&
+       contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) &&
        (startsWith(github.event.comment.body, '/review') ||
         startsWith(github.event.comment.body, '/describe') ||
         startsWith(github.event.comment.body, '/improve') ||
         startsWith(github.event.comment.body, '/ask'))) ||
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request != null &&
(startsWith(github.event.comment.body, '/review') ||
startsWith(github.event.comment.body, '/describe') ||
startsWith(github.event.comment.body, '/improve') ||
startsWith(github.event.comment.body, '/ask'))) ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request != null &&
github.event.comment.user.type != 'Bot' &&
contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) &&
(startsWith(github.event.comment.body, '/review') ||
startsWith(github.event.comment.body, '/describe') ||
startsWith(github.event.comment.body, '/improve') ||
startsWith(github.event.comment.body, '/ask'))) ||
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pr-agent-review.yml around lines 66 - 71, The workflow
currently allows any PR commenter to invoke slash commands (/review, /describe,
/improve, /ask); restrict this by adding an authorization check on
github.event.comment.author_association and/or matching the PR author: update
the conditional that wraps (startsWith(github.event.comment.body, '/review') ||
...) to also require (github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR' ||
github.event.comment.user.login == github.event.issue.user.login), so only repo
owners/members/collaborators or the PR author can trigger the commands.

github.event_name == 'workflow_dispatch'

# Self-hosted WSL2 Ubuntu runner on the trading machine. Set up via
# D:\Windows-CI\Scripts\Install-Wsl2LinuxRunner.ps1 (one-time, per repo).
# Avoids GitHub-hosted Linux minutes entirely. The `Linux` label
# differentiates from the Windows runners (which use `Windows`).
# Code never leaves the trading machine; OpenRouter is the only outbound.
runs-on: [self-hosted, Linux, X64, local-wsl, windows-ci]
timeout-minutes: 10

permissions:
pull-requests: write
contents: read
issues: write

steps:
- name: PR Agent
uses: qodo-ai/pr-agent@main
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Current main branch commit:"
curl -fsSL https://api.github.com/repos/qodo-ai/pr-agent/branches/main | jq -r '.commit.sha'

echo
echo "Recent tags (for pinning alternatives):"
curl -fsSL 'https://api.github.com/repos/qodo-ai/pr-agent/tags?per_page=10' | jq -r '.[].name'

Repository: singanuk/test1

Length of output: 154


🏁 Script executed:

cat .github/workflows/pr-agent-review.yml | head -100

Repository: singanuk/test1

Length of output: 3411


🌐 Web query:

GitHub Actions security best practices pinning commit SHA vs branch main

💡 Result:

For GitHub Actions security best practices, always pin actions and reusable workflows to a full-length commit SHA rather than mutable references like the main branch. Pinning to SHA provides immutability, preventing supply chain attacks where a malicious actor force-pushes to main or rewrites tags, injecting malicious code into your workflows [1][2][3][4]. Branches like main are highly mutable and unstable, as new commits can be added anytime, making them unsuitable for production security [5][6]. Tags are better than branches but still risky if deleted/recreated by compromised maintainers [1]. Example of secure pinning: uses: actions/checkout@19269a0f... # v4.1.7 Add the version comment for context. Use Dependabot to automate updates to new SHAs [4][7]. GitHub now supports org/repo-level policies to enforce SHA pinning across the full dependency tree [3]. Audit third-party actions, prefer verified creators, and review source code [1]. Never use short SHAs [2].

Citations:


Pin the action to an immutable commit SHA instead of @main.

Using @main makes executions non-reproducible and increases supply-chain risk. GitHub's security hardening guidance recommends pinning to a full-length commit SHA to prevent malicious code injection via force-pushes or branch rewrites.

🔧 Proposed fix
-        uses: qodo-ai/pr-agent@main
+        uses: qodo-ai/pr-agent@<full_commit_sha>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pr-agent-review.yml at line 87, Replace the floating ref
"uses: qodo-ai/pr-agent@main" with a full immutable commit SHA to pin the
action; locate the workflow step that contains the exact string "uses:
qodo-ai/pr-agent@main" and change it to "uses:
qodo-ai/pr-agent@<FULL_COMMIT_SHA>" where <FULL_COMMIT_SHA> is the 40-character
commit hash from the qodo-ai/pr-agent repository (obtain the latest trusted
commit on the action's repo or your approved release) so the workflow uses a
fixed, auditable revision.

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

CONFIG.MODEL: "openrouter/deepseek/deepseek-v4-pro"
CONFIG.FALLBACK_MODELS: '["openrouter/deepseek/deepseek-v4-flash"]'
OPENROUTER.KEY: ${{ secrets.OPENROUTER_API_KEY }}

GITHUB_ACTION_CONFIG.AUTO_REVIEW: "true"
GITHUB_ACTION_CONFIG.AUTO_DESCRIBE: "true"
GITHUB_ACTION_CONFIG.AUTO_IMPROVE: "false"
# Default PR_ACTIONS is ["opened","reopened","ready_for_review","review_requested"].
# Adding "synchronize" so PR-Agent re-fires when new commits land on the PR.
GITHUB_ACTION_CONFIG.PR_ACTIONS: '["opened","reopened","ready_for_review","review_requested","synchronize"]'

PR_REVIEWER.REQUIRE_TESTS_REVIEW: "true"
PR_REVIEWER.REQUIRE_SECURITY_REVIEW: "true"
PR_REVIEWER.REQUIRE_FOCUSED_REVIEW: "true"
PR_REVIEWER.NUM_CODE_SUGGESTIONS: "0"

PR_DESCRIPTION.PUBLISH_LABELS: "true"
PR_DESCRIPTION.ADD_ORIGINAL_USER_DESCRIPTION: "true"
PR_DESCRIPTION.GENERATE_AI_TITLE: "false"

CONFIG.MAX_DESCRIPTION_TOKENS: "1500"
CONFIG.MAX_COMMITS_TOKENS: "500"
CONFIG.PATCH_EXTRA_LINES: "1"
CONFIG.LOG_LEVEL: "INFO"
CONFIG.GIT_PROVIDER: "github"
# DeepSeek V4 (Pro/Flash) and other newer models aren't in PR-Agent's
# hardcoded MAX_TOKENS dict yet. Without this, calls fail with:
# "Ensure <model> is defined in MAX_TOKENS in ./pr_agent/algo/__init__.py"
# 65536 is well below V4's 1M context but plenty for PR diff review.
CONFIG.CUSTOM_MODEL_MAX_TOKENS: "65536"
Loading