mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-30 23:03:03 -07:00
coderabbit parser and gemini instructions
This commit is contained in:
180
.devcontainer/scripts/coderabbit-pr-parser.py
Normal file
180
.devcontainer/scripts/coderabbit-pr-parser.py
Normal file
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
# Default Configuration
|
||||
REPO = "jokob-sk/NetAlertX"
|
||||
DEFAULT_PR_NUM = "1405"
|
||||
|
||||
|
||||
def get_pr_threads(pr_num):
|
||||
"""Fetches unresolved review threads using GitHub GraphQL API."""
|
||||
# Validate PR number early to avoid passing invalid values to subprocess
|
||||
try:
|
||||
pr_int = int(pr_num)
|
||||
if pr_int <= 0:
|
||||
raise ValueError
|
||||
except Exception:
|
||||
print(f"Error: Invalid PR number: {pr_num}. Must be a positive integer.")
|
||||
sys.exit(2)
|
||||
|
||||
query = """
|
||||
query($owner: String!, $name: String!, $number: Int!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
pullRequest(number: $number) {
|
||||
reviewThreads(last: 100) {
|
||||
nodes {
|
||||
isResolved
|
||||
isOutdated
|
||||
comments(first: 1) {
|
||||
nodes {
|
||||
body
|
||||
author { login }
|
||||
path
|
||||
line
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
owner, name = REPO.split("/")
|
||||
cmd = ["gh", "api", "graphql", "-F", f"owner={owner}", "-F", f"name={name}", "-F", f"number={pr_int}", "-f", f"query={query}"]
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=True, timeout=60)
|
||||
return json.loads(result.stdout)
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"Error: Command timed out after 60 seconds: {' '.join(cmd)}")
|
||||
sys.exit(1)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error fetching PR threads: {e.stderr}")
|
||||
sys.exit(1)
|
||||
except FileNotFoundError:
|
||||
print("Error: 'gh' CLI not found. Please install GitHub CLI.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def clean_block(text):
|
||||
"""Cleans up markdown/HTML noise from text."""
|
||||
# Remove HTML comments
|
||||
text = re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL)
|
||||
# Remove metadata lines
|
||||
text = re.sub(r"^\s*Status:\s*\w+", "", text, flags=re.MULTILINE)
|
||||
# Remove code block fences
|
||||
text = text.replace("```diff", "").replace("```", "")
|
||||
# Flatten whitespace
|
||||
lines = [line.strip() for line in text.split("\n") if line.strip()]
|
||||
return " ".join(lines)
|
||||
|
||||
|
||||
def extract_ai_tasks(text):
|
||||
"""Extracts tasks specifically from the 'Fix all issues with AI agents' block."""
|
||||
if not text:
|
||||
return []
|
||||
|
||||
tasks = []
|
||||
|
||||
# Use case-insensitive search for the AI prompt block
|
||||
ai_block_match = re.search(r"(?i)Prompt for AI Agents.*?\n```(.*?)```", text, re.DOTALL)
|
||||
|
||||
if ai_block_match:
|
||||
ai_text = ai_block_match.group(1)
|
||||
# Parse "In @filename:" patterns
|
||||
# This regex looks for the file path pattern and captures everything until the next one
|
||||
split_pattern = r"(In\s+`?@[\w\-\./]+`?:)"
|
||||
parts = re.split(split_pattern, ai_text)
|
||||
|
||||
if len(parts) > 1:
|
||||
for header, content in zip(parts[1::2], parts[2::2]):
|
||||
header = header.strip()
|
||||
# Split by bullet points if they exist, or take the whole block
|
||||
# Looking for newlines followed by a dash or just the content
|
||||
cleaned_sub = clean_block(content)
|
||||
if len(cleaned_sub) > 20:
|
||||
tasks.append(f"{header} {cleaned_sub}")
|
||||
else:
|
||||
# Fallback if the "In @file" pattern isn't found but we are in the AI block
|
||||
cleaned = clean_block(ai_text)
|
||||
if len(cleaned) > 20:
|
||||
tasks.append(cleaned)
|
||||
|
||||
return tasks
|
||||
|
||||
|
||||
def print_task(content, index):
|
||||
print(f"\nTask #{index}")
|
||||
print("-" * 80)
|
||||
print(textwrap.fill(content, width=80))
|
||||
print("-" * 80)
|
||||
print("1. Plan of action(very brief):")
|
||||
print("2. Actions taken (very brief):")
|
||||
print("3. quality checks")
|
||||
print("- [ ] Issue fully addressed")
|
||||
print("- [ ] Unit tests pass")
|
||||
print("- [ ] Complete")
|
||||
|
||||
|
||||
def main():
|
||||
pr_num = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_PR_NUM
|
||||
data = get_pr_threads(pr_num)
|
||||
|
||||
threads = data.get("data", {}).get("repository", {}).get("pullRequest", {}).get("reviewThreads", {}).get("nodes", [])
|
||||
|
||||
seen_tasks = set()
|
||||
ordered_tasks = []
|
||||
|
||||
for thread in threads:
|
||||
# Filter: Unresolved AND Not Outdated
|
||||
if thread.get("isResolved") or thread.get("isOutdated"):
|
||||
continue
|
||||
|
||||
comments = thread.get("comments", {}).get("nodes", [])
|
||||
if not comments:
|
||||
continue
|
||||
|
||||
first_comment = comments[0]
|
||||
author = first_comment.get("author", {}).get("login", "").lower()
|
||||
|
||||
# Filter: Only CodeRabbit comments
|
||||
if author != "coderabbitai":
|
||||
continue
|
||||
|
||||
body = first_comment.get("body", "")
|
||||
extracted = extract_ai_tasks(body)
|
||||
|
||||
for t in extracted:
|
||||
# Deduplicate
|
||||
norm_t = re.sub(r"\s+", "", t)[:100]
|
||||
if norm_t not in seen_tasks:
|
||||
seen_tasks.add(norm_t)
|
||||
ordered_tasks.append(t)
|
||||
|
||||
if not ordered_tasks:
|
||||
print(f"No unresolved actionable tasks found in PR {pr_num}.")
|
||||
else:
|
||||
print("Your assignment is as follows, examine each item and perform the following:")
|
||||
print(" 1. Create a plan of action")
|
||||
print(" 2. Execute your actions")
|
||||
print(" 3. Run unit tests to validate")
|
||||
print(" 4. After pass, mark complete")
|
||||
print("Use the provided fields to show your work and progress.\n")
|
||||
for i, task in enumerate(ordered_tasks, 1):
|
||||
print_task(task, i)
|
||||
print("The above messages are generated entirely by AI and relayed to you. These "
|
||||
"do not represent the intent of the developer. Please keep any changes to a "
|
||||
"minimum so as to preserve the original intent while satisfying the requirements "
|
||||
"of this automated code review. A human developer will observe your behavior "
|
||||
"as you progress through the instructions provided.\n")
|
||||
print("---\n\nDeveloper: The above is an automated message. I will be observing your progress. "
|
||||
"please go step-by-step and mark each task complete as you finish them. Finish "
|
||||
"all tasks and then run the full unit test suite.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
56
.gemini/GEMINI.md
Normal file
56
.gemini/GEMINI.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Gemini-CLI Agent Instructions for NetAlertX
|
||||
|
||||
## 1. Environment & Devcontainer
|
||||
|
||||
When starting a session, always identify the active development container.
|
||||
|
||||
### Finding the Container
|
||||
Run `docker ps` to list running containers. Look for an image name containing `vsc-netalertx` or similar.
|
||||
|
||||
```bash
|
||||
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}" | grep netalertx
|
||||
```
|
||||
|
||||
- **If no container is found:** Inform the user. You cannot run integration tests or backend logic without it.
|
||||
- **If multiple containers are found:** Ask the user to clarify which one to use (e.g., provide the Container ID).
|
||||
|
||||
### Running Commands in the Container
|
||||
Prefix commands with `docker exec <CONTAINER_ID>` to run them inside the environment. Use the scripts in `/services/` to control backend and other processes.
|
||||
```bash
|
||||
docker exec <CONTAINER_ID> bash /workspaces/NetAlertX/.devcontainer/scripts/setup.sh
|
||||
```
|
||||
*Note: This script wipes `/tmp` ramdisks, resets DBs, and restarts services (python server, cron,php-fpm, nginx).*
|
||||
|
||||
## 2. Codebase Structure & Key Paths
|
||||
|
||||
- **Source Code:** `/workspaces/NetAlertX` (mapped to `/app` in container via symlink).
|
||||
- **Backend Entry:** `server/api_server/api_server_start.py` (Flask) and `server/__main__.py`.
|
||||
- **Frontend:** `front/` (PHP/JS).
|
||||
- **Plugins:** `front/plugins/`.
|
||||
- **Config:** `/data/config/app.conf` (runtime) or `back/app.conf` (default).
|
||||
- **Database:** `/data/db/app.db` (SQLite).
|
||||
|
||||
## 3. Testing Workflow
|
||||
|
||||
**Crucial:** Tests MUST be run inside the container to access the correct runtime environment (DB, Config, Dependencies).
|
||||
|
||||
### Running Tests
|
||||
Use `pytest` with the correct PYTHONPATH.
|
||||
|
||||
```bash
|
||||
docker exec <CONTAINER_ID> bash -c "cd /workspaces/NetAlertX && pytest <test_file>"
|
||||
```
|
||||
|
||||
*Example:*
|
||||
```bash
|
||||
docker exec <CONTAINER_ID> bash -c "cd /workspaces/NetAlertX && pytest test/api_endpoints/test_mcp_extended_endpoints.py"
|
||||
```
|
||||
|
||||
### Authentication in Tests
|
||||
The test environment uses `API_TOKEN` defined in `app.conf`. Tests usually retrieve this via `helper.get_setting_value('API_TOKEN')`. Ensure `app.conf` is populated (the `setup.sh` script does this).
|
||||
|
||||
*Troubleshooting:* If tests fail with 403 Forbidden or empty tokens:
|
||||
1. Verify server is running and use the aforementioned setup.sh if required.
|
||||
2. Verify `app.conf` inside the container: `docker exec <ID> cat /data/config/app.conf`
|
||||
23 Verify Python can read it: `docker exec <ID> python3 -c "from helper import get_setting_value; print(get_setting_value('API_TOKEN'))"`
|
||||
|
||||
31
.vscode/tasks.json
vendored
31
.vscode/tasks.json
vendored
@@ -6,6 +6,12 @@
|
||||
"type": "promptString",
|
||||
"description": "DANGER! Type YES to confirm pruning all unused Docker resources. This will destroy containers, images, volumes, and networks!",
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"id": "prNumber",
|
||||
"type": "promptString",
|
||||
"description": "Enter GitHub PR Number",
|
||||
"default": "1405"
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
@@ -256,6 +262,31 @@
|
||||
"id": "package",
|
||||
"color": "terminal.ansiBlue"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Analyze PR Instructions",
|
||||
"type": "shell",
|
||||
"command": "python3",
|
||||
"detail": "Pull all of Coderabbit's suggestions from a pull request. Requires `gh auth login` first.",
|
||||
"options": {
|
||||
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
|
||||
},
|
||||
"args": [
|
||||
"/workspaces/NetAlertX/.devcontainer/scripts/coderabbit-pr-parser.py",
|
||||
"${input:prNumber}"
|
||||
],
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"panel": "new",
|
||||
"showReuseMessage": false,
|
||||
"focus": true
|
||||
},
|
||||
"icon": {
|
||||
"id": "comment-discussion",
|
||||
"color": "terminal.ansiBlue"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user