From a4d1482b09f347d98e9d3330a1b70110aa67ba02 Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Wed, 15 Apr 2026 14:31:13 +0900 Subject: [PATCH 1/2] feat: add agentic issue tagging, PR description quality and security review --- .github/workflows/pr-quality-check.yml | 33 ++++ .github/workflows/security-review.yml | 42 ++++ .github/workflows/triage.yml | 31 +++ scripts/pr_checker_agent.py | 161 ++++++++++++++++ scripts/security_review_agent.py | 214 +++++++++++++++++++++ scripts/triage_agent.py | 256 +++++++++++++++++++++++++ 6 files changed, 737 insertions(+) create mode 100644 .github/workflows/pr-quality-check.yml create mode 100644 .github/workflows/security-review.yml create mode 100644 .github/workflows/triage.yml create mode 100644 scripts/pr_checker_agent.py create mode 100644 scripts/security_review_agent.py create mode 100644 scripts/triage_agent.py diff --git a/.github/workflows/pr-quality-check.yml b/.github/workflows/pr-quality-check.yml new file mode 100644 index 000000000..9790283c4 --- /dev/null +++ b/.github/workflows/pr-quality-check.yml @@ -0,0 +1,33 @@ +name: PR Quality Check +on: + pull_request: + types: [opened, reopened] + +jobs: + pr_quality_check: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: pip install litellm PyGithub + - name: Run PR quality check agent + env: + # e.g: "claude-sonnet-4-6", "gpt-4o", etc. + MODEL: ${{ secrets.MODEL }} + # Only API key for the chosen model is required + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Obtained automatically by GH Actions + AUTHOR_USERNAME: ${{ github.event.pull_request.user.login }} + AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO_NAME: ${{ github.repository }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + run: python scripts/pr_checker_agent.py diff --git a/.github/workflows/security-review.yml b/.github/workflows/security-review.yml new file mode 100644 index 000000000..08751138b --- /dev/null +++ b/.github/workflows/security-review.yml @@ -0,0 +1,42 @@ +name: Security Review +on: + pull_request: + types: [opened, reopened] + issue_comment: + types: [created] + +jobs: + security-review: + runs-on: ubuntu-latest + # Always runs on PR creation + # Also runs if comment on PR contains "/security-review" + if: > + github.event_name == 'pull_request' || + ( + github.event_name == 'issue_comment' && + github.event.issue.pull_request != null && + contains(github.event.comment.body, '/security-review') + ) + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: pip install litellm PyGithub + - name: Run security review agent + env: + # e.g: "claude-sonnet-4-6", "gpt-4o", etc. + MODEL: ${{ secrets.MODEL }} + # Only API key for the chosen model is required + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Obtained automatically by GH Actions + REPO_NAME: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} + TRIGGER: ${{ github.event_name }} + run: python scripts/security_review_agent.py diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml new file mode 100644 index 000000000..b52bb72a0 --- /dev/null +++ b/.github/workflows/triage.yml @@ -0,0 +1,31 @@ +name: Issue Triage +on: + issues: + types: [opened, reopened] + +jobs: + triage: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: pip install litellm PyGithub + - name: Run triage agent + env: + # e.g: "claude-sonnet-4-6", "gpt-4o", etc. + MODEL: ${{ secrets.MODEL }} + # Only API key for the chosen model is required + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Obtained automatically by GH Actions + ISSUE_NUMBER: ${{ github.event.issue.number }} + REPO_NAME: ${{ github.repository }} + ISSUE_TITLE: ${{ github.event.issue.title }} + ISSUE_BODY: ${{ github.event.issue.body }} + run: python scripts/triage_agent.py diff --git a/scripts/pr_checker_agent.py b/scripts/pr_checker_agent.py new file mode 100644 index 000000000..1b115a5fe --- /dev/null +++ b/scripts/pr_checker_agent.py @@ -0,0 +1,161 @@ +import os +import json +import litellm +from github import Github, Auth + +# Setup + +gh = Github(auth=Auth.Token(os.environ["GITHUB_TOKEN"])) +repo = gh.get_repo(os.environ["REPO_NAME"]) +pr = repo.get_pull(int(os.environ["PR_NUMBER"])) +author = os.environ["AUTHOR_USERNAME"] + +MODEL = os.environ["MODEL"] +for env_var in ["GITHUB_TOKEN", "REPO_NAME", "PR_NUMBER", "AUTHOR_USERNAME", "MODEL"]: + if not os.environ[env_var]: + raise ValueError(f"{env_var} is not set") + +valid_api_keys = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY"] +if not any(os.environ.get(api_key) for api_key in valid_api_keys): + raise ValueError("No API key is set") + + +# Tools + +TOOLS = [ + { + "type": "function", + "function": { + "name": "post_comment", + "description": ( + "Post a comment on the PR. Use this to welcome a first-time contributor, " + "ask for a clearer description, request an issue link, or flag non-compliance " + "with CONTRIBUTING.md. Combine multiple concerns into a single comment where " + "possible rather than posting several separate ones." + ), + "parameters": { + "type": "object", + "properties": { + "body": {"type": "string", "description": "The comment text (markdown supported)."} + }, + "required": ["body"], + }, + }, + }, +] + +# System prompt + +SYSTEM_PROMPT = """You are a PR review assistant for an open-source GitHub repository. +Given a newly opened PR, its author's contribution history, and the repository's CONTRIBUTING.md, +you must check the following - in this order: + +1. FIRST CONTRIBUTION: If this is the author's first contribution to the repo, welcome them warmly. + Acknowledge their effort and point them to any relevant getting-started resources in CONTRIBUTING.md. + +2. DESCRIPTION CLARITY: If the PR description is missing, too vague, or doesn't explain what + the change does and why, ask for a clearer description. + +3. LINKED ISSUE: Check whether the description contains a linked issue using keywords like + "Fixes #N", "Closes #N", "Resolves #N", or "Related to #N". If no issue is linked, + ask the author to either link an existing issue or create a new one. + +4. CONTRIBUTING.md COMPLIANCE: Check whether the PR description follows the structure or + requirements defined in CONTRIBUTING.md. If it doesn't comply, quote the relevant section + and point out specifically what needs to change. + +Important rules: +- If multiple concerns apply, combine them into a single comment, never post more than one. +- If everything looks good, stay silent. Do not post a comment just to say things look fine. +- Be warm and constructive, never demanding. Remember this may be someone's first open-source contribution. +- When referencing CONTRIBUTING.md requirements, be specific: quote or paraphrase the rule, + don't just say "please read the contributing guide". +- Most importantly, be as succinct as possible.""" + +# GitHub helpers + +def get_contributing_md() -> str: + """Fetches CONTRIBUTING.md from the repo root, or returns a notice if absent.""" + try: + contents = repo.get_contents("CONTRIBUTING.md") + return contents.decoded_content.decode("utf-8") + except Exception: + return "(No CONTRIBUTING.md found in this repository.)" + + +def is_first_contribution() -> bool: + """Returns True if the author has no previously merged PRs in this repo.""" + first_contribution_list = ['FIRST_TIMER', 'FIRST_TIME_CONTRIBUTOR', 'NONE'] + return os.environ["AUTHOR_ASSOCIATION"] in first_contribution_list + + +def post_comment(body: str) -> str: + pr.create_issue_comment(body) + return "Comment posted." + +# Tool dispatch + +def handle_tool_call(name: str, inputs: dict) -> str: + if name == "post_comment": + result = post_comment(inputs["body"]) + else: + result = f"Unknown tool: {name}" + + print(f"[tool] {name}: {result}") + return result + +# Agentic loop + +def build_initial_message() -> str: + first_contribution = is_first_contribution() + contributing_md = get_contributing_md() + + return ( + f"Please review this newly opened PR:\n\n" + f"Title: {os.environ['PR_TITLE']}\n" + f"Author: {author} ({'first-time contributor' if first_contribution else 'returning contributor'})\n" + f"Description:\n{os.environ.get('PR_BODY') or '(no description provided)'}\n\n" + f"---\n" + f"CONTRIBUTING.md contents:\n\n" + f"{contributing_md}" + ) + + +def run_pr_review_agent(): + messages = [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": build_initial_message()}, + ] + + while True: + response = litellm.completion( + model=MODEL, + messages=messages, + tools=TOOLS, + ) + + message = response.choices[0].message + + if message.content: + print(f"[agent] {message.content}") + + messages.append(message.model_dump(exclude_none=True)) + + if response.choices[0].finish_reason == "stop" or not message.tool_calls: + break + + tool_results = [] + for tool_call in message.tool_calls: + inputs = json.loads(tool_call.function.arguments) + result = handle_tool_call(tool_call.function.name, inputs) + tool_results.append({ + "role": "tool", + "tool_call_id": tool_call.id, + "content": result, + }) + + messages.extend(tool_results) + + +if __name__ == "__main__": + run_pr_review_agent() diff --git a/scripts/security_review_agent.py b/scripts/security_review_agent.py new file mode 100644 index 000000000..6e1184d6c --- /dev/null +++ b/scripts/security_review_agent.py @@ -0,0 +1,214 @@ +import os +import json +import litellm +from github import Github, Auth + +# Setup + +gh = Github(auth=Auth.Token(os.environ["GITHUB_TOKEN"])) +repo = gh.get_repo(os.environ["REPO_NAME"]) +pr = repo.get_pull(int(os.environ["PR_NUMBER"])) + +MODEL = os.environ["MODEL"] +for env_var in ["GITHUB_TOKEN", "REPO_NAME", "PR_NUMBER", "MODEL"]: + if not os.environ[env_var]: + raise ValueError(f"{env_var} is not set") + +valid_api_keys = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY"] +if not any(os.environ.get(api_key) for api_key in valid_api_keys): + raise ValueError("No API key is set") + + +# Exclude files that are not useful for security analysis +IGNORED_FILENAMES = { + "package-lock.json", + "yarn.lock", + "poetry.lock", + "Gemfile.lock", + "Cargo.lock", + "composer.lock", + "pnpm-lock.yaml", + "pip.lock", +} + +IGNORED_EXTENSIONS = {".lock", ".sum"} + +# Truncate very large diffs like generated files to prevent bloating the prompt +MAX_PATCH_CHARS_PER_FILE = 3000 + +# System prompt + +SYSTEM_PROMPT = """You are a security analysis assistant for a GitHub repository. +You are given the diff of a pull request and must identify potential security issues. + +Focus only on security-relevant concerns such as: +- Hardcoded secrets, tokens, passwords or API keys +- Injection vulnerabilities (SQL, shell, template, etc.) +- Insecure use of cryptography or hashing +- Unsafe deserialization +- Path traversal or directory traversal risks +- Insecure direct object references +- Missing input validation or sanitisation on user-controlled data +- Use of known-vulnerable dependency versions (if visible in the diff) +- Overly permissive file or network access + +Do NOT comment on code style, performance, test coverage, or general best practices +unless they have a direct security implication. + +If you find no issues, say so clearly and briefly — do not invent concerns. +Format your response as a markdown comment suitable for posting directly on a GitHub PR. +Start with a short summary line, then list findings with file references where applicable. +If there are no findings, keep the response to 2-3 sentences maximum.""" + +# GitHub helpers + +def get_pr_diff() -> str: + """ + Fetches changed files and their patches, filtering out lockfiles and + other noise. Returns a formatted string ready to be included in the prompt. + """ + sections = [] + for f in pr.get_files(): + filename = os.path.basename(f.filename) + _, ext = os.path.splitext(filename) + + if filename in IGNORED_FILENAMES or ext in IGNORED_EXTENSIONS: + print(f"[diff] Skipping {f.filename} (ignored file type)") + continue + + if not f.patch: + print(f"[diff] Skipping {f.filename} (no patch — binary or too large)") + continue + + patch = f.patch[:MAX_PATCH_CHARS_PER_FILE] + truncated = len(f.patch) > MAX_PATCH_CHARS_PER_FILE + sections.append( + f"### {f.filename}\n```diff\n{patch}" + + ("\n... (truncated)" if truncated else "") + + "\n```" + ) + + return "\n\n".join(sections) if sections else "(no reviewable changes found)" + + +def find_previous_security_comment() -> object | None: + """ + Looks for an existing security review comment posted by github-actions[bot] + so we can replace it rather than stacking multiple comments on updated reviews. + """ + for comment in pr.get_issue_comments(): + if ( + comment.user.login == "github-actions[bot]" + and "🔒 Automated Security Review" in comment.body + ): + return comment + return None + + +def post_or_update_comment(body: str): + """ + If a previous security review comment exists, edit it in place. + Otherwise post a new one to keep the PR timeline clean. + """ + existing = find_previous_security_comment() + if existing: + existing.edit(body) + print("[comment] Updated existing security review comment.") + else: + pr.create_issue_comment(body) + print("[comment] Posted new security review comment.") + +# Tools + +TOOLS = [ + { + "type": "function", + "function": { + "name": "post_security_review", + "description": ( + "Post the security review findings as a comment on the PR. " + "Call this once when your analysis is complete. " + "If there are no findings, still call this to confirm the review ran." + ), + "parameters": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "The full markdown comment body to post on the PR.", + } + }, + "required": ["body"], + }, + }, + } +] + +# Tool dispatch + +def handle_tool_call(name: str, inputs: dict) -> str: + if name == "post_security_review": + # Prepend a header to identify review comments across runs + body = f"## 🔒 Automated Security Review\n\n{inputs['body']}" + post_or_update_comment(body) + return "Security review comment posted." + return f"Unknown tool: {name}" + +# Agentic loop + +def build_initial_message() -> str: + trigger = os.environ.get("TRIGGER", "pull_request") + trigger_note = ( + "This review was requested manually via `/security-review`." + if trigger == "issue_comment" + else "This review was triggered automatically on PR creation." + ) + + return ( + f"Please perform a security review of this pull request.\n\n" + f"**PR #{pr.number}:** {pr.title}\n" + f"_{trigger_note}_\n\n" + f"---\n\n" + f"{get_pr_diff()}" + ) + + +def run_security_review_agent(): + messages = [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": build_initial_message()}, + ] + + while True: + response = litellm.completion( + model=MODEL, + messages=messages, + tools=TOOLS, + ) + + message = response.choices[0].message + + if message.content: + print(f"[agent] {message.content}") + + messages.append(message.model_dump(exclude_none=True)) + + if response.choices[0].finish_reason == "stop" or not message.tool_calls: + break + + tool_results = [] + for tool_call in message.tool_calls: + inputs = json.loads(tool_call.function.arguments) + result = handle_tool_call(tool_call.function.name, inputs) + print(f"[tool] {tool_call.function.name}: {result}") + tool_results.append({ + "role": "tool", + "tool_call_id": tool_call.id, + "content": result, + }) + + messages.extend(tool_results) + + +if __name__ == "__main__": + run_security_review_agent() diff --git a/scripts/triage_agent.py b/scripts/triage_agent.py new file mode 100644 index 000000000..f5fdbb18d --- /dev/null +++ b/scripts/triage_agent.py @@ -0,0 +1,256 @@ +import os +import json +import litellm +from github import Github, Auth + +# Setup + +gh = Github(auth=Auth.Token(os.environ["GITHUB_TOKEN"])) +repo = gh.get_repo(os.environ["REPO_NAME"]) +issue = repo.get_issue(int(os.environ["ISSUE_NUMBER"])) + +LATEST_ISSUES_LIMIT = 100 +MODEL = os.environ["MODEL"] + +for env_var in ["GITHUB_TOKEN", "REPO_NAME", "ISSUE_NUMBER", "ISSUE_TITLE", "ISSUE_BODY", "MODEL"]: + if not os.environ[env_var]: + raise ValueError(f"{env_var} is not set") + +valid_api_keys = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY"] +if not any(os.environ.get(api_key) for api_key in valid_api_keys): + raise ValueError("No API key is set") + +# Tools + +TOOLS = [ + { + "type": "function", + "function": { + "name": "apply_label", + "description": ( + "Apply one or more labels to the issue. " + "Use labels like: automation, bug, dependencies, " + "documentation, enhancement, good-first-issue, " + "meeting, needs-info, plugins, protocol, question, " + "security, tech-debt, testing." + ), + "parameters": { + "type": "object", + "properties": { + "labels": { + "type": "array", + "items": {"type": "string"}, + "description": "List of labels to apply.", + } + }, + "required": ["labels"], + }, + }, + }, + { + "type": "function", + "function": { + "name": "post_comment", + "description": "Post a comment on the issue, e.g. to ask for clarification or acknowledge receipt.", + "parameters": { + "type": "object", + "properties": { + "body": {"type": "string", "description": "The comment text (markdown supported)."} + }, + "required": ["body"], + }, + }, + }, + { + "type": "function", + "function": { + "name": "mark_duplicate", + "description": ( + "Mark this issue as a duplicate of an existing one. " + "Use this when the issue is clearly asking about the same thing as an open issue. " + "Post a comment pointing to the original issue without closing anything." + ), + "parameters": { + "type": "object", + "properties": { + "original_issue_number": { + "type": "integer", + "description": "The issue number this is a duplicate of.", + }, + "reason": { + "type": "string", + "description": "Brief explanation of why these issues are duplicates.", + }, + }, + "required": ["original_issue_number", "reason"], + }, + }, + }, + { + "type": "function", + "function": { + "name": "suggest_possible_duplicate", + "description": ( + "Use when an existing issue is related but not clearly the same thing. " + "Posts a comment pointing to the similar issue without closing anything." + "Continue triage normally after posting the comment." + ), + "parameters": { + "type": "object", + "properties": { + "related_issue_number": { + "type": "integer", + "description": "The issue number that might be related.", + }, + "reason": { + "type": "string", + "description": "Brief explanation of why these issues seem related.", + }, + }, + "required": ["related_issue_number", "reason"], + }, + }, + }, +] + +# System prompt + +SYSTEM_PROMPT = """You are an issue triage assistant for a GitHub repository. +Given a new issue and a list of existing open issues, you must: + +1. Check whether the new issue is a duplicate of an existing one. + - If it clearly is the same issue, call mark_duplicate and stop — do not label or acknowledge further. + - If it seems related but could be distinct, call suggest_possible_duplicate. That comment + will serve as the acknowledgment too, so do NOT post a separate acknowledgment afterward. +2. Otherwise, classify it by applying appropriate labels + (bug, feature-request, question, documentation, needs-info, good-first-issue). +3. If the issue is missing key info (steps to reproduce for bugs, use case for features, etc.), + post a friendly comment asking for it. +4. If no possible duplicate was flagged, post a short acknowledgment comment so the + author knows their issue was received. Do NOT post comments on administrative issues + such as meeting minutes, roadmaps, etc. + +Keep comments concise and friendly.""" + +# GitHub helpers + +def get_existing_issues(limit: int = LATEST_ISSUES_LIMIT) -> str: + """ + Fetches the most recent open issues (excluding the current one) + and formats them into a string for the prompt. + """ + open_issues = repo.get_issues(state="open") + lines = [] + count = 0 + for existing in open_issues: + if existing.number == issue.number: + continue + lines.append( + f"- #{existing.number}: {existing.title}\n" + f" {(existing.body or '').strip()[:200]}" # truncate long bodies + ) + count += 1 + if count >= limit: + break + return "\n".join(lines) if lines else "(no other open issues)" + + +def apply_label(labels: list[str]) -> str: + existing_label_names = [l.name for l in repo.get_labels()] + for label in labels: + if label not in existing_label_names: + repo.create_label(label, "ededed") + issue.add_to_labels(*labels) + return f"Applied labels: {labels}" + + +def post_comment(body: str) -> str: + issue.create_comment(body) + return "Comment posted." + + +def mark_duplicate(original_issue_number: int, reason: str) -> str: + original = repo.get_issue(original_issue_number) + issue.create_comment( + f"Thanks for the report! This looks like a duplicate of #{original_issue_number} " + f"({original.html_url}).\n\n> {reason}\n\n" + f"Please edit this issue to add any distinguishing details if you believe it's not a duplicate." + ) + issue.add_to_labels("duplicate") + return f"Marked as duplicate of #{original_issue_number}." + + +def suggest_possible_duplicate(related_issue_number: int, reason: str) -> str: + related = repo.get_issue(related_issue_number) + issue.create_comment( + f"Hey! This might be related to #{related_issue_number} " + f"({related.html_url}) — {reason}\n\n" + f"Feel free to check if that one already covers what you're reporting!" + ) + return f"Flagged as possibly related to #{related_issue_number}." + + +# Tool dispatch + +def handle_tool_call(name: str, inputs: dict) -> str: + if name == "apply_label": + result = apply_label(inputs["labels"]) + elif name == "post_comment": + result = post_comment(inputs["body"]) + elif name == "mark_duplicate": + result = mark_duplicate(inputs["original_issue_number"], inputs["reason"]) + elif name == "suggest_possible_duplicate": + result = suggest_possible_duplicate(inputs["related_issue_number"], inputs["reason"]) + else: + result = f"Unknown tool: {name}" + print(f"Tool {name}: {result}") + return result + +# Agentic loop + +def build_initial_message() -> str: + return ( + f"Please triage this new GitHub issue:\n\n" + f"Title: {os.environ['ISSUE_TITLE']}\n" + f"Body:\n{os.environ.get('ISSUE_BODY') or '(no description provided)'}\n\n" + f"---\n" + f"Here are the currently open issues for duplicate detection:\n\n" + f"{get_existing_issues()}" + ) + + +def run_triage_agent(): + messages = [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": build_initial_message()}, + ] + + while True: + response = litellm.completion( + model=MODEL, + messages=messages, + tools=TOOLS, + ) + + message = response.choices[0].message + messages.append(message.model_dump(exclude_none=True)) + + finish_reason = response.choices[0].finish_reason + if finish_reason == "stop" or not message.tool_calls: + break + + tool_results = [] + for tool_call in message.tool_calls: + inputs = json.loads(tool_call.function.arguments) + result = handle_tool_call(tool_call.function.name, inputs) + tool_results.append({ + "role": "tool", + "tool_call_id": tool_call.id, + "content": result, + }) + + messages.extend(tool_results) + + +if __name__ == "__main__": + run_triage_agent() From cdd01cf314ec0694b418f6063ea7374bc3014c9b Mon Sep 17 00:00:00 2001 From: Juan Escalada Date: Wed, 15 Apr 2026 15:57:23 +0900 Subject: [PATCH 2/2] chore: update PR workflows to run on pull_request_target --- .github/workflows/pr-quality-check.yml | 2 +- .github/workflows/security-review.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-quality-check.yml b/.github/workflows/pr-quality-check.yml index 9790283c4..8ccaaa6ea 100644 --- a/.github/workflows/pr-quality-check.yml +++ b/.github/workflows/pr-quality-check.yml @@ -1,6 +1,6 @@ name: PR Quality Check on: - pull_request: + pull_request_target: types: [opened, reopened] jobs: diff --git a/.github/workflows/security-review.yml b/.github/workflows/security-review.yml index 08751138b..38c5194c4 100644 --- a/.github/workflows/security-review.yml +++ b/.github/workflows/security-review.yml @@ -1,6 +1,6 @@ name: Security Review on: - pull_request: + pull_request_target: types: [opened, reopened] issue_comment: types: [created]