cicaddy-github 0.7.1__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/.agents/skills/cicaddy-action/SKILL.md +8 -2
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/.github/workflows/pr-review.yml +6 -1
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/AGENTS.md +5 -1
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/PKG-INFO +41 -4
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/README.md +39 -2
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/action.yml +8 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/entrypoint.sh +2 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/pyproject.toml +2 -2
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/__init__.py +1 -1
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/config/settings.py +10 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/github_integration/agents.py +194 -4
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/github_integration/analyzer.py +77 -4
- cicaddy_github-0.8.0/tests/unit/test_inline_review.py +1369 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/.github/dependabot.yml +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/.github/workflows/changelog.yml +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/.github/workflows/ci.yml +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/.github/workflows/release.yml +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/.gitignore +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/.pre-commit-config.yaml +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/CLAUDE.md +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/CODE_OF_CONDUCT.md +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/CONTRIBUTING.md +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/Dockerfile +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/LICENSE +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/docs/delegation.md +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/docs/providers.md +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/config/__init__.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/github_integration/__init__.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/github_integration/detector.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/github_integration/go_dep_review_tools.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/github_integration/tools.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/plugin.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/security/__init__.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/security/leak_detector.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/validation.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tasks/changelog_report.yml +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tasks/go_dep_impact_review.yml +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tasks/pr_review.yml +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/templates/report_template.html +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/__init__.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/conftest.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/__init__.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/test_agents.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/test_analyzer.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/test_detector.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/test_go_dep_review.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/test_leak_detector.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/test_plugin.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/test_review_delegation_hooks.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/test_settings.py +0 -0
- {cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/tests/unit/test_tools.py +0 -0
|
@@ -127,10 +127,12 @@ can reference them as bash variables (`INPUT_AI_PROVIDER`, `INPUT_AI_API_KEY`, e
|
|
|
127
127
|
| `task_prompt` | No | Inline task prompt |
|
|
128
128
|
| `post_pr_comment` | No | Post results as PR comment (default: `false`) |
|
|
129
129
|
| `submit_review` | No | Submit formal PR review with APPROVE/REQUEST_CHANGES (default: `false`) |
|
|
130
|
+
| `inline_review_comments` | No | Post AI findings as inline comments on PR diff lines (default: `false`) |
|
|
130
131
|
| `run_govulncheck` | No | Run govulncheck for vulnerability reachability analysis (default: `false`) |
|
|
131
132
|
| `dep_review_severity_threshold` | No | Minimum semver bump to analyze: `minor` or `major` (default: `minor`) |
|
|
132
133
|
| `delegation_mode` | No | `none` (default) or `auto` for sub-agent delegation |
|
|
133
134
|
| `max_sub_agents` | No | Max concurrent sub-agents, 1-10 (default: `3`) |
|
|
135
|
+
| `delegation_verify_findings` | No | Verify sub-agent findings against codebase (default: `false`) |
|
|
134
136
|
| `github_token` | No | GitHub token (default: `${{ github.token }}`) |
|
|
135
137
|
| `mcp_servers_config` | No | JSON array of MCP server configs |
|
|
136
138
|
| `slack_webhook_url` | No | Slack webhook URL |
|
|
@@ -196,6 +198,8 @@ github = "cicaddy_github.plugin:validate"
|
|
|
196
198
|
- `github_token`, `github_repository`, `github_ref`, `github_event_name`
|
|
197
199
|
- `github_sha`, `github_run_id`, `github_pr_number`
|
|
198
200
|
- `post_pr_comment` (bool)
|
|
201
|
+
- `submit_review` (bool)
|
|
202
|
+
- `inline_review_comments` (bool)
|
|
199
203
|
|
|
200
204
|
All loaded from environment variables via `load_settings()`.
|
|
201
205
|
|
|
@@ -233,7 +237,7 @@ the `safe-to-review` label. The label is auto-removed on new pushes to prevent
|
|
|
233
237
|
TOCTOU bypasses.
|
|
234
238
|
|
|
235
239
|
```yaml
|
|
236
|
-
- uses: redhat-community-ai-tools/cicaddy-action@v0.
|
|
240
|
+
- uses: redhat-community-ai-tools/cicaddy-action@v0.8.0
|
|
237
241
|
with:
|
|
238
242
|
ai_provider: gemini
|
|
239
243
|
ai_model: gemini-3.5-flash
|
|
@@ -329,11 +333,13 @@ cicaddy-action v0.5.0+ supports AI-powered sub-agent delegation via cicaddy>=0.8
|
|
|
329
333
|
**Action Inputs:**
|
|
330
334
|
- `delegation_mode`: `none` (default) or `auto`
|
|
331
335
|
- `max_sub_agents`: 1-10 (default: `3`)
|
|
336
|
+
- `delegation_verify_findings`: `false` (default) — verify findings against codebase
|
|
332
337
|
|
|
333
338
|
**Environment Variables:**
|
|
334
339
|
- `DELEGATION_MODE`: `none` or `auto`
|
|
335
340
|
- `MAX_SUB_AGENTS`: 1-10 (default: `3`)
|
|
336
|
-
- `SUB_AGENT_MAX_ITERS`: 1-15 (default: `
|
|
341
|
+
- `SUB_AGENT_MAX_ITERS`: 1-15 (default: `5`)
|
|
342
|
+
- `DELEGATION_VERIFY_FINDINGS`: verify findings against codebase (`true`/`false`)
|
|
337
343
|
- `DELEGATION_AGENTS_DIR`: `.agents/delegation` (custom agent YAML directory)
|
|
338
344
|
- `DELEGATION_AGENTS`: JSON array for inline custom agents
|
|
339
345
|
- `TRIAGE_PROMPT`: Custom triage instructions
|
|
@@ -49,7 +49,7 @@ jobs:
|
|
|
49
49
|
fi
|
|
50
50
|
|
|
51
51
|
- name: AI Code Review
|
|
52
|
-
uses: redhat-community-ai-tools/cicaddy-action@v0.
|
|
52
|
+
uses: redhat-community-ai-tools/cicaddy-action@v0.8.0
|
|
53
53
|
id: review
|
|
54
54
|
with:
|
|
55
55
|
ai_provider: gemini
|
|
@@ -57,7 +57,12 @@ jobs:
|
|
|
57
57
|
ai_api_key: ${{ secrets.AI_API_KEY }}
|
|
58
58
|
task_file: tasks/pr_review.yml
|
|
59
59
|
post_pr_comment: 'true'
|
|
60
|
+
# submit_review and inline_review_comments intentionally omitted —
|
|
61
|
+
# this workflow uses pull_request_target for fork PR support, and
|
|
62
|
+
# formal reviews should not be combined with pull_request_target.
|
|
63
|
+
# See docs/providers.md#submit_review-and-fork-pull-requests
|
|
60
64
|
delegation_mode: auto
|
|
65
|
+
delegation_verify_findings: 'true'
|
|
61
66
|
mcp_servers_config: ${{ steps.mcp.outputs.config }}
|
|
62
67
|
env:
|
|
63
68
|
ANALYSIS_FOCUS: "general"
|
|
@@ -102,7 +102,7 @@ The cicaddy-github plugin provides:
|
|
|
102
102
|
| `DELEGATION_AGENTS` | (empty) | JSON config for inline custom sub-agent definitions |
|
|
103
103
|
| `TRIAGE_PROMPT` | (empty) | Custom triage instructions |
|
|
104
104
|
|
|
105
|
-
Action inputs: `delegation_mode`, `max_sub_agents`
|
|
105
|
+
Action inputs: `delegation_mode`, `max_sub_agents`, `delegation_verify_findings`
|
|
106
106
|
CLI flags: `--delegation-mode auto --max-sub-agents 2`
|
|
107
107
|
|
|
108
108
|
See cicaddy's [sub-agent delegation docs](https://github.com/waynesun09/cicaddy/blob/main/docs/sub-agent-delegation.md) for built-in agents, custom YAML format, and tool filtering.
|
|
@@ -124,12 +124,16 @@ All inputs use **underscores** (not hyphens) for Docker container compatibility:
|
|
|
124
124
|
| `task_prompt` | No | Inline task prompt |
|
|
125
125
|
| `post_pr_comment` | No | Post results as PR comment (default: `false`) |
|
|
126
126
|
| `submit_review` | No | Submit formal PR review with APPROVE/REQUEST_CHANGES (default: `false`) |
|
|
127
|
+
| `inline_review_comments` | No | Post AI findings as inline comments on PR diff lines (default: `false`) |
|
|
127
128
|
| `github_token` | No | GitHub token (default: `${{ github.token }}`) |
|
|
128
129
|
| `mcp_servers_config` | No | JSON array of MCP server configs |
|
|
129
130
|
| `slack_webhook_url` | No | Slack webhook URL |
|
|
130
131
|
| `report_template` | No | Custom HTML report template path |
|
|
132
|
+
| `run_govulncheck` | No | Run govulncheck for vulnerability reachability analysis (default: `false`) |
|
|
133
|
+
| `dep_review_severity_threshold` | No | Minimum semver bump to analyze: `minor` or `major` (default: `minor`) |
|
|
131
134
|
| `delegation_mode` | No | `none` (default) or `auto` for sub-agent delegation |
|
|
132
135
|
| `max_sub_agents` | No | Max concurrent sub-agents, 1-10 (default: `3`) |
|
|
136
|
+
| `delegation_verify_findings` | No | Verify sub-agent findings against codebase (default: `false`) |
|
|
133
137
|
|
|
134
138
|
*Not required if provider-specific key is set via `env:`.
|
|
135
139
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cicaddy-github
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: GitHub Actions plugin for cicaddy AI agent framework
|
|
5
5
|
Project-URL: Homepage, https://github.com/redhat-community-ai-tools/cicaddy-action
|
|
6
6
|
Project-URL: Repository, https://github.com/redhat-community-ai-tools/cicaddy-action.git
|
|
7
7
|
Project-URL: Issues, https://github.com/redhat-community-ai-tools/cicaddy-action/issues
|
|
8
8
|
Author: Wayne Sun
|
|
9
|
-
License: Apache-2.0
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Requires-Python: >=3.11
|
|
12
12
|
Requires-Dist: cicaddy>=0.11.0
|
|
@@ -95,8 +95,10 @@ jobs:
|
|
|
95
95
|
google_cloud_project: ${{ vars.GCP_PROJECT_ID }}
|
|
96
96
|
task_file: tasks/pr_review.yml
|
|
97
97
|
post_pr_comment: 'true'
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
submit_review: 'true'
|
|
99
|
+
inline_review_comments: 'true'
|
|
100
|
+
delegation_mode: auto
|
|
101
|
+
delegation_verify_findings: 'true'
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
> **Fork PRs**: The `pull_request` event cannot mint OIDC tokens for PRs
|
|
@@ -215,12 +217,42 @@ SA key fallback, and an authentication method comparison table.
|
|
|
215
217
|
| `slack_webhook_url` | No | Slack webhook URL for notifications |
|
|
216
218
|
| `post_pr_comment` | No | Post results as PR comment (default: `false`) |
|
|
217
219
|
| `submit_review` | No | Submit formal PR review with APPROVE/REQUEST_CHANGES (default: `false`) |
|
|
220
|
+
| `inline_review_comments` | No | Post AI findings as inline comments on PR diff lines (default: `false`) |
|
|
218
221
|
| `run_govulncheck` | No | Run govulncheck for vulnerability reachability analysis (default: `false`) |
|
|
219
222
|
| `dep_review_severity_threshold` | No | Minimum semver bump to analyze: `minor` or `major` (default: `minor`) |
|
|
220
223
|
| `delegation_mode` | No | Enable AI-powered sub-agent delegation: `none` (default) or `auto` |
|
|
221
224
|
| `max_sub_agents` | No | Maximum concurrent sub-agents, 1-10 (default: `3`) |
|
|
225
|
+
| `delegation_verify_findings` | No | Verify sub-agent findings against codebase to reduce false positives (default: `false`) |
|
|
222
226
|
| `github_token` | No | GitHub token (default: `${{ github.token }}`) |
|
|
223
227
|
|
|
228
|
+
### Review Output Options
|
|
229
|
+
|
|
230
|
+
The action supports three independent review output options that control how results appear on a PR. All three default to `false` and can be combined:
|
|
231
|
+
|
|
232
|
+
| Option | What it does |
|
|
233
|
+
|--------|-------------|
|
|
234
|
+
| `post_pr_comment` | Posts (or updates) a bot comment on the PR with the full review summary. On subsequent runs the same comment is updated in place. |
|
|
235
|
+
| `submit_review` | Submits a formal GitHub review (APPROVE / REQUEST_CHANGES / COMMENT) based on the AI analysis. |
|
|
236
|
+
| `inline_review_comments` | Attaches AI findings as inline comments on the exact diff lines. Requires `submit_review: 'true'` since inline comments are part of the GitHub review. |
|
|
237
|
+
|
|
238
|
+
**Recommended combinations:**
|
|
239
|
+
|
|
240
|
+
```yaml
|
|
241
|
+
# Summary comment only (simplest)
|
|
242
|
+
post_pr_comment: 'true'
|
|
243
|
+
|
|
244
|
+
# Summary comment + inline diff comments (recommended for most teams)
|
|
245
|
+
post_pr_comment: 'true'
|
|
246
|
+
submit_review: 'true'
|
|
247
|
+
inline_review_comments: 'true'
|
|
248
|
+
|
|
249
|
+
# Formal review with inline comments, no standalone comment
|
|
250
|
+
submit_review: 'true'
|
|
251
|
+
inline_review_comments: 'true'
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
> **Note:** `inline_review_comments` works best with `submit_review: 'true'` and `delegation_mode: auto`. Delegation produces structured findings with file/line info that inline comments need. Without `submit_review`, inline comments are posted as a `COMMENT` review (no approval/rejection verdict).
|
|
255
|
+
|
|
224
256
|
## Outputs
|
|
225
257
|
|
|
226
258
|
| Output | Description |
|
|
@@ -271,6 +303,8 @@ GITHUB_PR_NUMBER=42
|
|
|
271
303
|
|
|
272
304
|
# Agent Settings
|
|
273
305
|
POST_PR_COMMENT=true
|
|
306
|
+
SUBMIT_REVIEW=true
|
|
307
|
+
INLINE_REVIEW_COMMENTS=true
|
|
274
308
|
ENABLE_LOCAL_TOOLS=true
|
|
275
309
|
LOCAL_TOOLS_WORKING_DIR=.
|
|
276
310
|
|
|
@@ -326,9 +360,12 @@ uv run cicaddy validate --env-file .env.my-review
|
|
|
326
360
|
| `GITHUB_EVENT_NAME` | No | Set to `pull_request` for auto-detection (optional if `GITHUB_PR_NUMBER` is set) |
|
|
327
361
|
| `GITHUB_PR_NUMBER` | Yes | PR number to review |
|
|
328
362
|
| `POST_PR_COMMENT` | No | Post results as PR comment (`true`/`false`) |
|
|
363
|
+
| `SUBMIT_REVIEW` | No | Submit formal PR review with APPROVE/REQUEST_CHANGES (`true`/`false`) |
|
|
364
|
+
| `INLINE_REVIEW_COMMENTS` | No | Post AI findings as inline comments on PR diff lines (`true`/`false`) |
|
|
329
365
|
| `AGENT_TASKS` | No | Agent task type (e.g. `go_dep_review` for Go dependency analysis) |
|
|
330
366
|
| `DELEGATION_MODE` | No | `auto` for AI-powered sub-agent delegation, `none` for single-agent (default: `none`) |
|
|
331
367
|
| `MAX_SUB_AGENTS` | No | Max concurrent sub-agents for delegation, 1-10 (default: `3`) |
|
|
368
|
+
| `DELEGATION_VERIFY_FINDINGS` | No | Verify sub-agent findings against codebase to reduce false positives (`true`/`false`) |
|
|
332
369
|
| `SUB_AGENT_MAX_ITERS` | No | Max iterations per sub-agent, 1-15 (default: `5`) |
|
|
333
370
|
| `AI_TASK_FILE` | No | Path to DSPy YAML task file for custom workflows |
|
|
334
371
|
| `RUN_GOVULNCHECK` | No | Run govulncheck for reachability analysis (`true`/`false`) |
|
|
@@ -75,8 +75,10 @@ jobs:
|
|
|
75
75
|
google_cloud_project: ${{ vars.GCP_PROJECT_ID }}
|
|
76
76
|
task_file: tasks/pr_review.yml
|
|
77
77
|
post_pr_comment: 'true'
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
submit_review: 'true'
|
|
79
|
+
inline_review_comments: 'true'
|
|
80
|
+
delegation_mode: auto
|
|
81
|
+
delegation_verify_findings: 'true'
|
|
80
82
|
```
|
|
81
83
|
|
|
82
84
|
> **Fork PRs**: The `pull_request` event cannot mint OIDC tokens for PRs
|
|
@@ -195,12 +197,42 @@ SA key fallback, and an authentication method comparison table.
|
|
|
195
197
|
| `slack_webhook_url` | No | Slack webhook URL for notifications |
|
|
196
198
|
| `post_pr_comment` | No | Post results as PR comment (default: `false`) |
|
|
197
199
|
| `submit_review` | No | Submit formal PR review with APPROVE/REQUEST_CHANGES (default: `false`) |
|
|
200
|
+
| `inline_review_comments` | No | Post AI findings as inline comments on PR diff lines (default: `false`) |
|
|
198
201
|
| `run_govulncheck` | No | Run govulncheck for vulnerability reachability analysis (default: `false`) |
|
|
199
202
|
| `dep_review_severity_threshold` | No | Minimum semver bump to analyze: `minor` or `major` (default: `minor`) |
|
|
200
203
|
| `delegation_mode` | No | Enable AI-powered sub-agent delegation: `none` (default) or `auto` |
|
|
201
204
|
| `max_sub_agents` | No | Maximum concurrent sub-agents, 1-10 (default: `3`) |
|
|
205
|
+
| `delegation_verify_findings` | No | Verify sub-agent findings against codebase to reduce false positives (default: `false`) |
|
|
202
206
|
| `github_token` | No | GitHub token (default: `${{ github.token }}`) |
|
|
203
207
|
|
|
208
|
+
### Review Output Options
|
|
209
|
+
|
|
210
|
+
The action supports three independent review output options that control how results appear on a PR. All three default to `false` and can be combined:
|
|
211
|
+
|
|
212
|
+
| Option | What it does |
|
|
213
|
+
|--------|-------------|
|
|
214
|
+
| `post_pr_comment` | Posts (or updates) a bot comment on the PR with the full review summary. On subsequent runs the same comment is updated in place. |
|
|
215
|
+
| `submit_review` | Submits a formal GitHub review (APPROVE / REQUEST_CHANGES / COMMENT) based on the AI analysis. |
|
|
216
|
+
| `inline_review_comments` | Attaches AI findings as inline comments on the exact diff lines. Requires `submit_review: 'true'` since inline comments are part of the GitHub review. |
|
|
217
|
+
|
|
218
|
+
**Recommended combinations:**
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
# Summary comment only (simplest)
|
|
222
|
+
post_pr_comment: 'true'
|
|
223
|
+
|
|
224
|
+
# Summary comment + inline diff comments (recommended for most teams)
|
|
225
|
+
post_pr_comment: 'true'
|
|
226
|
+
submit_review: 'true'
|
|
227
|
+
inline_review_comments: 'true'
|
|
228
|
+
|
|
229
|
+
# Formal review with inline comments, no standalone comment
|
|
230
|
+
submit_review: 'true'
|
|
231
|
+
inline_review_comments: 'true'
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
> **Note:** `inline_review_comments` works best with `submit_review: 'true'` and `delegation_mode: auto`. Delegation produces structured findings with file/line info that inline comments need. Without `submit_review`, inline comments are posted as a `COMMENT` review (no approval/rejection verdict).
|
|
235
|
+
|
|
204
236
|
## Outputs
|
|
205
237
|
|
|
206
238
|
| Output | Description |
|
|
@@ -251,6 +283,8 @@ GITHUB_PR_NUMBER=42
|
|
|
251
283
|
|
|
252
284
|
# Agent Settings
|
|
253
285
|
POST_PR_COMMENT=true
|
|
286
|
+
SUBMIT_REVIEW=true
|
|
287
|
+
INLINE_REVIEW_COMMENTS=true
|
|
254
288
|
ENABLE_LOCAL_TOOLS=true
|
|
255
289
|
LOCAL_TOOLS_WORKING_DIR=.
|
|
256
290
|
|
|
@@ -306,9 +340,12 @@ uv run cicaddy validate --env-file .env.my-review
|
|
|
306
340
|
| `GITHUB_EVENT_NAME` | No | Set to `pull_request` for auto-detection (optional if `GITHUB_PR_NUMBER` is set) |
|
|
307
341
|
| `GITHUB_PR_NUMBER` | Yes | PR number to review |
|
|
308
342
|
| `POST_PR_COMMENT` | No | Post results as PR comment (`true`/`false`) |
|
|
343
|
+
| `SUBMIT_REVIEW` | No | Submit formal PR review with APPROVE/REQUEST_CHANGES (`true`/`false`) |
|
|
344
|
+
| `INLINE_REVIEW_COMMENTS` | No | Post AI findings as inline comments on PR diff lines (`true`/`false`) |
|
|
309
345
|
| `AGENT_TASKS` | No | Agent task type (e.g. `go_dep_review` for Go dependency analysis) |
|
|
310
346
|
| `DELEGATION_MODE` | No | `auto` for AI-powered sub-agent delegation, `none` for single-agent (default: `none`) |
|
|
311
347
|
| `MAX_SUB_AGENTS` | No | Max concurrent sub-agents for delegation, 1-10 (default: `3`) |
|
|
348
|
+
| `DELEGATION_VERIFY_FINDINGS` | No | Verify sub-agent findings against codebase to reduce false positives (`true`/`false`) |
|
|
312
349
|
| `SUB_AGENT_MAX_ITERS` | No | Max iterations per sub-agent, 1-15 (default: `5`) |
|
|
313
350
|
| `AI_TASK_FILE` | No | Path to DSPy YAML task file for custom workflows |
|
|
314
351
|
| `RUN_GOVULNCHECK` | No | Run govulncheck for reachability analysis (`true`/`false`) |
|
|
@@ -50,6 +50,10 @@ inputs:
|
|
|
50
50
|
description: 'Submit a formal PR review with APPROVE or REQUEST_CHANGES based on AI analysis (requires github-token with pull-requests: write)'
|
|
51
51
|
required: false
|
|
52
52
|
default: 'false'
|
|
53
|
+
inline_review_comments:
|
|
54
|
+
description: 'Post AI findings as inline comments on PR diff lines. Best used with submit_review and delegation_mode=auto for structured findings. (requires github-token with pull-requests: write)'
|
|
55
|
+
required: false
|
|
56
|
+
default: 'false'
|
|
53
57
|
run_govulncheck:
|
|
54
58
|
description: 'Run govulncheck for vulnerability reachability analysis (requires Go and govulncheck installed)'
|
|
55
59
|
required: false
|
|
@@ -66,6 +70,10 @@ inputs:
|
|
|
66
70
|
description: 'Maximum concurrent sub-agents for delegation (1-10, default: 3)'
|
|
67
71
|
required: false
|
|
68
72
|
default: '3'
|
|
73
|
+
delegation_verify_findings:
|
|
74
|
+
description: 'Verify each sub-agent finding against the actual codebase via a lightweight AI check to reduce false positives (default: false)'
|
|
75
|
+
required: false
|
|
76
|
+
default: 'false'
|
|
69
77
|
github_token:
|
|
70
78
|
description: 'GitHub token for API access'
|
|
71
79
|
required: false
|
|
@@ -109,10 +109,12 @@ export SLACK_WEBHOOK_URL="${INPUT_SLACK_WEBHOOK_URL}"
|
|
|
109
109
|
export GITHUB_TOKEN="${INPUT_GITHUB_TOKEN:-$GITHUB_TOKEN}"
|
|
110
110
|
export POST_PR_COMMENT="${INPUT_POST_PR_COMMENT:-false}"
|
|
111
111
|
export SUBMIT_REVIEW="${INPUT_SUBMIT_REVIEW:-false}"
|
|
112
|
+
export INLINE_REVIEW_COMMENTS="${INPUT_INLINE_REVIEW_COMMENTS:-false}"
|
|
112
113
|
export RUN_GOVULNCHECK="${INPUT_RUN_GOVULNCHECK:-false}"
|
|
113
114
|
export DEP_REVIEW_SEVERITY_THRESHOLD="${INPUT_DEP_REVIEW_SEVERITY_THRESHOLD:-minor}"
|
|
114
115
|
export DELEGATION_MODE="${INPUT_DELEGATION_MODE:-none}"
|
|
115
116
|
export MAX_SUB_AGENTS="${INPUT_MAX_SUB_AGENTS:-3}"
|
|
117
|
+
export DELEGATION_VERIFY_FINDINGS="${INPUT_DELEGATION_VERIFY_FINDINGS:-false}"
|
|
116
118
|
|
|
117
119
|
# Validate MAX_SUB_AGENTS is a number
|
|
118
120
|
if [[ -n "${MAX_SUB_AGENTS}" ]] && ! [[ "${MAX_SUB_AGENTS}" =~ ^[0-9]+$ ]]; then
|
|
@@ -4,11 +4,11 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cicaddy-github"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "GitHub Actions plugin for cicaddy AI agent framework"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
|
-
license =
|
|
11
|
+
license = "Apache-2.0"
|
|
12
12
|
authors = [{name = "Wayne Sun"}]
|
|
13
13
|
dependencies = [
|
|
14
14
|
"cicaddy>=0.11.0",
|
|
@@ -66,6 +66,11 @@ class Settings(CoreSettings):
|
|
|
66
66
|
validation_alias=AliasChoices("SUBMIT_REVIEW"),
|
|
67
67
|
description="Whether to submit a formal PR review (APPROVE or REQUEST_CHANGES)",
|
|
68
68
|
)
|
|
69
|
+
inline_review_comments: bool = Field(
|
|
70
|
+
default=False,
|
|
71
|
+
validation_alias=AliasChoices("INLINE_REVIEW_COMMENTS"),
|
|
72
|
+
description="Post findings as inline comments on PR diff lines.",
|
|
73
|
+
)
|
|
69
74
|
run_govulncheck: bool = Field(
|
|
70
75
|
default=False,
|
|
71
76
|
validation_alias=AliasChoices("RUN_GOVULNCHECK"),
|
|
@@ -115,6 +120,11 @@ def load_settings() -> Settings:
|
|
|
115
120
|
if submit_review:
|
|
116
121
|
env_data["submit_review"] = submit_review.lower() in ("true", "1", "yes")
|
|
117
122
|
|
|
123
|
+
# Inline review comments flag
|
|
124
|
+
inline_review = os.getenv("INLINE_REVIEW_COMMENTS", "").strip()
|
|
125
|
+
if inline_review:
|
|
126
|
+
env_data["inline_review_comments"] = inline_review.lower() in ("true", "1", "yes")
|
|
127
|
+
|
|
118
128
|
# Dep review configuration
|
|
119
129
|
run_govulncheck = os.getenv("RUN_GOVULNCHECK", "").strip()
|
|
120
130
|
if run_govulncheck:
|
{cicaddy_github-0.7.1 → cicaddy_github-0.8.0}/src/cicaddy_github/github_integration/agents.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""GitHub AI Agents for PR review and task execution."""
|
|
2
2
|
|
|
3
|
+
import html
|
|
3
4
|
import os
|
|
4
5
|
import re
|
|
5
6
|
import textwrap
|
|
@@ -346,7 +347,7 @@ Please provide your comprehensive analysis in markdown format.
|
|
|
346
347
|
)
|
|
347
348
|
|
|
348
349
|
# Post PR comment if enabled (updates existing bot comment in-place)
|
|
349
|
-
post_comment =
|
|
350
|
+
post_comment = self.settings.post_pr_comment
|
|
350
351
|
if post_comment and self.platform_analyzer and self.pr_number:
|
|
351
352
|
try:
|
|
352
353
|
comment = self._format_pr_comment(analysis_result)
|
|
@@ -360,9 +361,17 @@ Please provide your comprehensive analysis in markdown format.
|
|
|
360
361
|
)
|
|
361
362
|
logger.debug("PR comment post traceback:", exc_info=True)
|
|
362
363
|
|
|
363
|
-
#
|
|
364
|
-
|
|
365
|
-
|
|
364
|
+
# Check if inline review comments will handle the formal review
|
|
365
|
+
inline_enabled = self.settings.inline_review_comments
|
|
366
|
+
findings = analysis_result.get("findings") or []
|
|
367
|
+
inline_findings = [f for f in findings if f.get("line") and f.get("file")]
|
|
368
|
+
inline_will_post = (
|
|
369
|
+
inline_enabled and inline_findings and self.platform_analyzer and self.pr_number
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
# Submit formal PR review if enabled — skip when inline review will handle it
|
|
373
|
+
submit_review = self.settings.submit_review
|
|
374
|
+
if submit_review and self.platform_analyzer and self.pr_number and not inline_will_post:
|
|
366
375
|
try:
|
|
367
376
|
ai_text = analysis_result.get("ai_analysis", "")
|
|
368
377
|
verdict = extract_review_verdict(ai_text)
|
|
@@ -377,9 +386,190 @@ Please provide your comprehensive analysis in markdown format.
|
|
|
377
386
|
)
|
|
378
387
|
logger.debug("PR review submit traceback:", exc_info=True)
|
|
379
388
|
|
|
389
|
+
# Post inline review comments if enabled
|
|
390
|
+
if inline_enabled:
|
|
391
|
+
if not findings:
|
|
392
|
+
logger.warning(
|
|
393
|
+
"INLINE_REVIEW_COMMENTS is enabled but no structured findings "
|
|
394
|
+
"were produced. Inline comments require DELEGATION_MODE=auto."
|
|
395
|
+
)
|
|
396
|
+
skipped = len(findings) - len(inline_findings)
|
|
397
|
+
logger.info(
|
|
398
|
+
f"Inline review for PR #{self.pr_number}: "
|
|
399
|
+
f"{len(inline_findings)} findings with line+file, "
|
|
400
|
+
f"{skipped} without (total {len(findings)})"
|
|
401
|
+
)
|
|
402
|
+
if inline_will_post:
|
|
403
|
+
try:
|
|
404
|
+
await self._post_inline_review(analysis_result, inline_findings)
|
|
405
|
+
except Exception as e:
|
|
406
|
+
logger.error(
|
|
407
|
+
f"Failed to post inline review: {self.leak_detector.sanitize_text(str(e))}"
|
|
408
|
+
)
|
|
409
|
+
logger.debug("Inline review post traceback:", exc_info=True)
|
|
410
|
+
# Fallback: submit standalone review since inline failed
|
|
411
|
+
if submit_review and self.platform_analyzer:
|
|
412
|
+
try:
|
|
413
|
+
ai_text = analysis_result.get("ai_analysis", "")
|
|
414
|
+
verdict = extract_review_verdict(ai_text)
|
|
415
|
+
review_body = self._format_review_body(analysis_result)
|
|
416
|
+
await self.platform_analyzer.submit_pr_review(
|
|
417
|
+
int(self.pr_number), review_body, event=verdict
|
|
418
|
+
)
|
|
419
|
+
logger.info(f"Submitted fallback {verdict} review on PR #{self.pr_number}")
|
|
420
|
+
except Exception as fallback_e:
|
|
421
|
+
logger.error(
|
|
422
|
+
f"Fallback review also failed: "
|
|
423
|
+
f"{self.leak_detector.sanitize_text(str(fallback_e))}"
|
|
424
|
+
)
|
|
425
|
+
|
|
380
426
|
# Send Slack notification using parent class
|
|
381
427
|
await super().send_notifications(report, analysis_result)
|
|
382
428
|
|
|
429
|
+
@staticmethod
|
|
430
|
+
def _format_inline_comment(finding: dict, leak_detector: Any = None) -> str:
|
|
431
|
+
"""Format a finding dict into a markdown inline comment body.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
finding: Finding dict with severity, message, and optional
|
|
435
|
+
suggestion and agent_source fields.
|
|
436
|
+
leak_detector: Optional leak detector to sanitize secrets.
|
|
437
|
+
|
|
438
|
+
Returns:
|
|
439
|
+
Formatted markdown string for the inline comment.
|
|
440
|
+
"""
|
|
441
|
+
severity = finding.get("severity", "info").upper()
|
|
442
|
+
emoji_map = {
|
|
443
|
+
"CRITICAL": "\U0001f534",
|
|
444
|
+
"MAJOR": "\U0001f7e0",
|
|
445
|
+
"MINOR": "\U0001f7e1",
|
|
446
|
+
"NIT": "\U0001f535",
|
|
447
|
+
}
|
|
448
|
+
emoji = emoji_map.get(severity, "ℹ️")
|
|
449
|
+
message = finding.get("message", "No description provided")
|
|
450
|
+
suggestion = finding.get("suggestion", "")
|
|
451
|
+
if leak_detector:
|
|
452
|
+
message = leak_detector.sanitize_text(message)
|
|
453
|
+
if suggestion:
|
|
454
|
+
suggestion = leak_detector.sanitize_text(suggestion)
|
|
455
|
+
body = f"{emoji} **{severity}**: {message}"
|
|
456
|
+
if suggestion:
|
|
457
|
+
body += f"\n\n**Suggestion**: {suggestion}"
|
|
458
|
+
if finding.get("agent_source"):
|
|
459
|
+
agent_source = finding["agent_source"]
|
|
460
|
+
if leak_detector:
|
|
461
|
+
agent_source = leak_detector.sanitize_text(agent_source)
|
|
462
|
+
body += f"\n\n<sub>Source: {html.escape(agent_source)}</sub>"
|
|
463
|
+
return body
|
|
464
|
+
|
|
465
|
+
MAX_INLINE_COMMENTS = 25
|
|
466
|
+
|
|
467
|
+
async def _post_inline_review(
|
|
468
|
+
self, analysis_result: dict[str, Any], findings: list[dict]
|
|
469
|
+
) -> None:
|
|
470
|
+
"""Post findings as inline review comments on the PR diff.
|
|
471
|
+
|
|
472
|
+
Args:
|
|
473
|
+
analysis_result: Full analysis result dict.
|
|
474
|
+
findings: Pre-filtered findings that have both file and line.
|
|
475
|
+
"""
|
|
476
|
+
pr_files = await self.platform_analyzer.get_pr_changed_files(int(self.pr_number))
|
|
477
|
+
|
|
478
|
+
# Build patch lookup: file path -> patch string
|
|
479
|
+
patch_map: dict[str, str | None] = {}
|
|
480
|
+
for f in pr_files:
|
|
481
|
+
patch_map[f.filename] = f.patch
|
|
482
|
+
|
|
483
|
+
severity_order = {"critical": 0, "major": 1, "minor": 2, "nit": 3}
|
|
484
|
+
sorted_findings = sorted(
|
|
485
|
+
findings,
|
|
486
|
+
key=lambda f: severity_order.get(f.get("severity", "nit").lower(), 4),
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
# Build inline comments, validating each line is in the diff
|
|
490
|
+
comments: list[dict] = []
|
|
491
|
+
skipped = 0
|
|
492
|
+
for finding in sorted_findings:
|
|
493
|
+
file_path = finding["file"]
|
|
494
|
+
try:
|
|
495
|
+
line = int(finding["line"])
|
|
496
|
+
except (ValueError, TypeError):
|
|
497
|
+
skipped += 1
|
|
498
|
+
continue
|
|
499
|
+
patch = patch_map.get(file_path)
|
|
500
|
+
|
|
501
|
+
if patch is None or not GitHubAnalyzer._is_line_in_diff_range(patch, line):
|
|
502
|
+
skipped += 1
|
|
503
|
+
continue
|
|
504
|
+
|
|
505
|
+
comment: dict[str, Any] = {
|
|
506
|
+
"path": file_path,
|
|
507
|
+
"body": self._format_inline_comment(finding, leak_detector=self.leak_detector),
|
|
508
|
+
"line": line,
|
|
509
|
+
"side": "RIGHT",
|
|
510
|
+
}
|
|
511
|
+
start_line = finding.get("start_line")
|
|
512
|
+
try:
|
|
513
|
+
start_line = int(start_line) if start_line else None
|
|
514
|
+
except (ValueError, TypeError):
|
|
515
|
+
start_line = None
|
|
516
|
+
if (
|
|
517
|
+
start_line
|
|
518
|
+
and start_line < line
|
|
519
|
+
and GitHubAnalyzer._is_line_in_diff_range(patch, start_line)
|
|
520
|
+
):
|
|
521
|
+
comment["start_line"] = start_line
|
|
522
|
+
comment["start_side"] = "RIGHT"
|
|
523
|
+
|
|
524
|
+
comments.append(comment)
|
|
525
|
+
if len(comments) >= self.MAX_INLINE_COMMENTS:
|
|
526
|
+
truncated = len(sorted_findings) - skipped - self.MAX_INLINE_COMMENTS
|
|
527
|
+
if truncated > 0:
|
|
528
|
+
logger.warning(
|
|
529
|
+
f"Capped inline comments at {self.MAX_INLINE_COMMENTS}, "
|
|
530
|
+
f"{truncated} lower-severity finding(s) omitted"
|
|
531
|
+
)
|
|
532
|
+
break
|
|
533
|
+
|
|
534
|
+
if not comments:
|
|
535
|
+
logger.info(
|
|
536
|
+
f"No findings survived diff validation on PR #{self.pr_number}; "
|
|
537
|
+
"skipping inline review submission"
|
|
538
|
+
)
|
|
539
|
+
if self.settings.submit_review and self.platform_analyzer:
|
|
540
|
+
ai_text = analysis_result.get("ai_analysis", "")
|
|
541
|
+
verdict = extract_review_verdict(ai_text)
|
|
542
|
+
review_body = self._format_review_body(analysis_result)
|
|
543
|
+
await self.platform_analyzer.submit_pr_review(
|
|
544
|
+
int(self.pr_number), review_body, event=verdict
|
|
545
|
+
)
|
|
546
|
+
logger.info(f"Submitted standalone {verdict} review (no inline comments)")
|
|
547
|
+
return
|
|
548
|
+
|
|
549
|
+
# Determine review event
|
|
550
|
+
submit_review = self.settings.submit_review
|
|
551
|
+
if submit_review:
|
|
552
|
+
ai_text = analysis_result.get("ai_analysis", "")
|
|
553
|
+
event = extract_review_verdict(ai_text)
|
|
554
|
+
else:
|
|
555
|
+
event = "COMMENT"
|
|
556
|
+
|
|
557
|
+
# Build review body
|
|
558
|
+
review_body = self._format_review_body(analysis_result)
|
|
559
|
+
|
|
560
|
+
await self.platform_analyzer.submit_review_with_comments(
|
|
561
|
+
pr_number=int(self.pr_number),
|
|
562
|
+
body=review_body,
|
|
563
|
+
comments=comments,
|
|
564
|
+
event=event,
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
posted = len(comments)
|
|
568
|
+
logger.info(
|
|
569
|
+
f"Inline review: {posted} comment(s) posted, {skipped} skipped "
|
|
570
|
+
f"(not in diff range) on PR #{self.pr_number}"
|
|
571
|
+
)
|
|
572
|
+
|
|
383
573
|
def _format_review_body(self, analysis_result: dict[str, Any]) -> str:
|
|
384
574
|
"""Format analysis results as a PR review body.
|
|
385
575
|
|