feat: support CI artifact context injection and workflow_run trigger#2494
feat: support CI artifact context injection and workflow_run trigger#2494MahmoudHaouachi wants to merge 3 commits into
Conversation
Adds a registry-based artifact parser system that reads a CI-produced file (e.g. terraform plan output, test report) and injects its content into tool extra_instructions as extra review context. New files: - pr_agent/algo/artifacts.py: core module with resolve_artifact_path, load_artifact_content, and three built-in parsers (generic, terraform_plan, test_report) - tests/unittest/test_artifacts.py: full unit test suite Configuration (off by default, opt-in via artifact_path action input): [artifacts] enable = false artifact_path = "" artifact_type = "generic" target_tools = ["pr_reviewer", "pr_description", "pr_code_suggestions"] max_artifact_size = 50000
Two additions to github_action_runner.py:
1. Artifact injection: reads ARTIFACT_PATH / ARTIFACT_TYPE env vars
before event dispatch and appends parsed artifact content to
extra_instructions for pr_description, pr_code_suggestions, and
pr_reviewer. Delegates to pr_agent/algo/artifacts.py. No-op when
ARTIFACT_PATH is not set.
2. workflow_run handler: new elif branch that handles
GITHUB_EVENT_NAME=workflow_run. Extracts the PR API URL from
workflow_run.pull_requests[0].url and runs the same auto tools
(PRDescription, PRReviewer, PRCodeSuggestions) as the pull_request
handler. Guards: skips non-pull_request origins and empty
pull_requests arrays (fork PRs).
This enables the common pattern of downloading cross-workflow artifacts
before running PR-Agent:
on:
workflow_run:
workflows: ["CI"]
types: [completed]
steps:
- uses: actions/download-artifact@v4
with:
run-id: ${{ github.event.workflow_run.id }}
name: terraform-plan
- uses: The-PR-Agent/pr-agent@main
with:
artifact_path: plan.txt
artifact_type: terraform_plan
Exposes two optional inputs for the GitHub Action: - artifact_path: path to a CI artifact file (relative to GITHUB_WORKSPACE or absolute). When set, artifact injection is automatically enabled. - artifact_type: parser to apply — generic (default), terraform_plan, or test_report. Both default to empty/generic so existing users see no behavior change. Also adds `set -e` to entrypoint.sh for fail-fast behavior.
PR Summary by QodoSupport workflow_run trigger and inject CI artifact context into PR tools
AI Description
Diagram
High-Level Assessment
Files changed (7)
|
Code Review by Qodo
Context used 1. Artifact path can escape workspace
|
| def resolve_artifact_path(path: str) -> Optional[Path]: | ||
| if not path: | ||
| return None | ||
|
|
||
| artifact_path = Path(path) | ||
| if artifact_path.is_absolute(): | ||
| return artifact_path if artifact_path.is_file() else None | ||
|
|
||
| workspace = os.environ.get("GITHUB_WORKSPACE", "") | ||
| if workspace: | ||
| resolved = Path(workspace) / artifact_path | ||
| if resolved.is_file(): | ||
| return resolved | ||
|
|
||
| resolved = artifact_path.resolve() | ||
| if resolved.is_file(): | ||
| return resolved | ||
|
|
There was a problem hiding this comment.
1. Artifact path can escape workspace 📎 Requirement gap ⛨ Security
resolve_artifact_path() accepts absolute paths and falls back to resolving relative paths against the current working directory, allowing ARTIFACT_PATH to escape GITHUB_WORKSPACE (including .. traversal) and read arbitrary local files instead of workspace-local artifacts. Because the resolved file content is injected into tool extra_instructions and rendered into LLM prompts, this violates the workspace-relative artifact requirement and can leak unintended sensitive files to the model provider.
Agent Prompt
## Issue description
`resolve_artifact_path()` currently permits resolving artifact paths outside `GITHUB_WORKSPACE` by allowing absolute paths and by resolving relative paths against the container’s current working directory. Because the resolved file contents are injected into tool `extra_instructions` and rendered into prompts sent to the model, this breaks the workspace-relative artifact contract and creates an arbitrary local file read → prompt exfiltration risk.
## Issue Context
Compliance requires artifact reading to be `GITHUB_WORKSPACE`-relative and effectively confined to that directory before injecting content into `extra_instructions`. Today, `ARTIFACT_PATH` is sourced from environment variables in the GitHub Action runner, the artifact text is loaded and injected into `extra_instructions` for tools, and those instructions are rendered into prompts and sent to the model via `chat_completion`; the runner also has access to sensitive data (e.g., `GITHUB_TOKEN`/`OPENAI_KEY`), so allowing artifacts to point outside the workspace can leak unintended local files.
## Fix Focus Areas
- pr_agent/algo/artifacts.py[60-78]
- pr_agent/servers/github_action_runner.py[110-139]
- pr_agent/settings/configuration.toml[371-383]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Hey @MahmoudHaouachi, |
| @@ -0,0 +1,176 @@ | |||
| import os | |||
| import tempfile | |||
| @@ -0,0 +1,176 @@ | |||
| import os | |||
| import tempfile | |||
| from pathlib import Path | |||
| from pathlib import Path | ||
| from unittest.mock import patch, MagicMock | ||
|
|
||
| import pytest |
Closes #2493
Summary
Adds two related enhancements that together enable a common CI/CD pattern: running PR-Agent after a prior workflow completes, with that workflow's artifacts (e.g. terraform plan, test report) injected as extra review context.
Changes
1.
pr_agent/algo/artifacts.py(new)Registry-based artifact parser that reads a local file and formats its content for injection into tool prompts. Three built-in parsers:
generic— plain context, useful for any CI outputterraform_plan— instructs the AI to verify infrastructure changes match the code diff and flag risky deletionstest_report— instructs the AI to correlate failures with the code changesKey functions:
resolve_artifact_path(handles relative/absolute, respectsGITHUB_WORKSPACE),load_artifact_content(reads, truncates, formats; no-op unless enabled).2.
pr_agent/servers/github_action_runner.pyArtifact injection block: reads
ARTIFACT_PATH/ARTIFACT_TYPEenv vars before event dispatch, callsload_artifact_content(), and appends the result toextra_instructionsforpr_description,pr_code_suggestions, andpr_reviewer. Fully wrapped in try/except — safe no-op whenARTIFACT_PATHis not set.workflow_runhandler: newelif GITHUB_EVENT_NAME == "workflow_run":branch. Extracts the PR URL fromworkflow_run.pull_requests[0].urland runs the same auto tools as thepull_requesthandler. Guards: skips non-pull_requestorigins and emptypull_requestsarrays (fork PRs).3.
action.yamlTwo new optional inputs with safe defaults:
artifact_path(default"") — path to artifact relative toGITHUB_WORKSPACEartifact_type(default"generic") — parser type4.
pr_agent/settings/configuration.tomlNew
[artifacts]section documenting all options (enable,artifact_path,artifact_type,artifact_label,target_tools,max_artifact_size).5. Tests
tests/unittest/test_artifacts.py(new, 176 lines) — full coverage of the artifacts moduletests/unittest/test_github_action_runner_core.py— 3 new tests forworkflow_run: runs tools, skips non-PR origin, skips emptypull_requests6.
github_action/entrypoint.shAdded
set -efor fail-fast behavior.Example: terraform plan review via workflow_run
Toggle / opt-out
artifact_pathdefaults to"", artifact injection never runsartifact_pathin action inputs, or[artifacts] enable = true+artifact_pathin.pr_agent.tomlartifact_pathfrom action inputs, or[artifacts] enable = falseworkflow_runhandler only fires whenGITHUB_EVENT_NAME=workflow_run— existingpull_requestandissue_commentflows are completely unchangedTesting
The implementation has been validated end-to-end against real terraform plan artifacts using Bedrock Claude Haiku.