Skip to content
Merged
Show file tree
Hide file tree
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
42 changes: 22 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,39 @@ name: CI
on:
pull_request:
branches: [main]
push:
tags:
- "v*"

jobs:
lint:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install ruff
run: pip install ruff

- name: Ruff format check
run: ruff format --check .

- name: Ruff lint
run: ruff check .
- run: pip install ruff
- run: ruff format --check .
- run: ruff check .

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- uses: astral-sh/setup-uv@v4
- run: uv sync
- run: uv build

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Install dependencies
run: uv sync

- name: Build package
run: uv build
release:
if: startsWith(github.ref, 'refs/tags/')
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: softprops/action-gh-release@v2
with:
files: dist/*
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ wheels/
vectors/
.env.*
logs/stdout.log
.vscode/
*.code-workspace
60 changes: 40 additions & 20 deletions api.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import asyncio
import os
import re
from typing import Any

import dotenv
import requests
from fastapi import BackgroundTasks, FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse

from src.reviewbot.agent.workflow import work_agent
from src.reviewbot.agent.workflow import work_agent # type: ignore
from src.reviewbot.agent.workflow.gitlab_notes import post_mr_note
from src.reviewbot.infra.config.env import load_env

Expand Down Expand Up @@ -40,9 +43,9 @@ def get_pipeline_status(project_id: str, pipeline_id: int) -> str:
return r.json()["status"]


def mr_has_conflicts(mr: dict) -> bool:
def mr_has_conflicts(mr: Any) -> bool:
# GitLab MR payload includes this
return mr.get("detailed_merge_status") == "conflict"
return mr and mr["detailed_merge_status"] == "conflict"


def pipeline_passed(project_id: str, pipeline_id: int) -> bool:
Expand Down Expand Up @@ -81,9 +84,12 @@ async def gitlab_webhook(req: Request, background_tasks: BackgroundTasks):
return JSONResponse({"ignored": "bot note"})

text = note.get("note", "")
# pattern = rf"(?:/review\b.*@{re.escape(BOT_USERNAME)}|@{re.escape(BOT_USERNAME)}.*?/review\b)"
# if not re.search(pattern, text):
# return JSONResponse({"ignored": "no /review command"})
if BOT_USERNAME and text.strip() != "/reviewbot review":
pattern = (
rf"(?:/review\b.*@{re.escape(BOT_USERNAME)}|@{re.escape(BOT_USERNAME)}.*?/review\b)"
)
if not re.search(pattern, text):
return JSONResponse({"ignored": "no /review command"})

if text.strip() != "/reviewbot review":
return JSONResponse({"ignored": "not a review command"})
Expand All @@ -92,15 +98,22 @@ async def gitlab_webhook(req: Request, background_tasks: BackgroundTasks):
if not mr:
return JSONResponse({"ignored": "not an MR note"})

project_id = payload["project"]["id"]
mr_iid = mr["iid"]
project_id = str(payload["project"]["id"])
mr_iid = str(mr["iid"])

config = load_env()
background_tasks.add_task(
work_agent,
config,
project_id,
mr_iid,
thread_id = f"{project_id}:{mr_iid}"
asyncio.create_task(
work_agent.ainvoke( # type: ignore
{
"config": config,
"project_id": project_id,
"mr_iid": mr_iid,
},
config={
"configurable": {"thread_id": thread_id},
},
)
)

return JSONResponse({"status": "manual review triggered"})
Expand All @@ -113,15 +126,15 @@ async def gitlab_webhook(req: Request, background_tasks: BackgroundTasks):
mr = payload.get("merge_request")
detailed_status = attrs.get("detailed_status")

project_id = payload["project"]["id"]
project_id = str(payload["project"]["id"])

if detailed_status not in ["passed", "failed"]:
return JSONResponse({"ignored": "pipeline is not in a final state"})

if not mr:
return JSONResponse({"ignored": "not an MR pipeline"})

mr_iid = mr["iid"]
mr_iid = str(mr["iid"])

if detailed_status != "passed":
post_mr_note(
Expand All @@ -145,11 +158,18 @@ async def gitlab_webhook(req: Request, background_tasks: BackgroundTasks):
return JSONResponse({"ignored": "merge conflicts present"})

config = load_env()
background_tasks.add_task(
work_agent,
config,
project_id,
mr_iid,
thread_id = f"{project_id}:{mr_iid}"
asyncio.create_task(
work_agent.ainvoke( # type: ignore
{
"config": config,
"project_id": project_id,
"mr_iid": mr_iid,
},
config={
"configurable": {"thread_id": thread_id},
},
)
)

return JSONResponse({"status": "auto review triggered"})
Expand Down
11 changes: 11 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,18 @@ services:
2>&1 | tee /logs/stdout.log"
restart: unless-stopped

gitlab_runner:
image: gitlab/gitlab-runner:latest
container_name: gitlab_runner
restart: always
depends_on:
- gitlab
volumes:
- gitlab_runner_config:/etc/gitlab-runner
- /var/run/docker.sock:/var/run/docker.sock

volumes:
gitlab_config:
gitlab_logs:
gitlab_data:
gitlab_runner_config:
22 changes: 13 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
[project]
name = "reviewbot"
version = "0.1.0"
version = "0.3.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"dotenv>=0.9.9",
"faiss-cpu>=1.13.1",
"fastapi>=0.125.0",
"httpx>=0.28.1",
"ido-agents @ git+https://github.com/canefe/ido-agents.git@v0.1.4",
"langchain>=1.2.0",
"langchain-community>=0.4.1",
"langchain-google-genai>=4.1.2",
Expand All @@ -20,30 +22,31 @@ dependencies = [
"transformers>=4.57.3",
"typer>=0.20.0",
"uvicorn>=0.40.0",
"xai-review>=0.48.0",
]
[tool.uv]
package = true

[project.scripts]
reviewbot = "reviewbot.main:app"
[tool.pyright]
typeCheckingMode = "strict"
extraPaths = ["src"]

[tool.ruff]
line-length = 100
target-version = "py313"

[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"UP", # pyupgrade
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"UP", # pyupgrade
]
ignore = [
"E501", # line too long (handled by formatter)
"E501", # line too long (handled by formatter)
]

[tool.ruff.format]
Expand All @@ -52,6 +55,7 @@ indent-style = "space"

[dependency-groups]
dev = [
"pyright>=1.1.408",
"ruff>=0.8.6",
"ty>=0.0.4",
]
Expand Down
53 changes: 0 additions & 53 deletions src/reviewbot/agent/base.py

This file was deleted.

Empty file.
Loading