.
This commit is contained in:
parent
3dcf947c68
commit
bbac9918e1
@ -44,6 +44,8 @@ python -m uvicorn main:app --host 0.0.0.0 --port 8080 --reload --env-file .env
|
|||||||
- `AUTO_ASSIGNED_LABEL` - необязательно, по умолчанию `auto-assigned`
|
- `AUTO_ASSIGNED_LABEL` - необязательно, по умолчанию `auto-assigned`
|
||||||
- `AUTO_ASSIGN_REVIEWERS_COUNT` - необязательно, по умолчанию `2` (сколько ревьюеров назначать автоматически)
|
- `AUTO_ASSIGN_REVIEWERS_COUNT` - необязательно, по умолчанию `2` (сколько ревьюеров назначать автоматически)
|
||||||
- `AUTO_ASSIGN_EXCLUDED_AUTHORS` - необязательно, CSV логинов Gitea; для этих авторов автоназначение и auto-label на `PR opened` не выполняются
|
- `AUTO_ASSIGN_EXCLUDED_AUTHORS` - необязательно, CSV логинов Gitea; для этих авторов автоназначение и auto-label на `PR opened` не выполняются
|
||||||
|
- `BACKEND_USERS` - необязательно, CSV логинов Gitea backend-команды; если автор PR входит в этот список, ревьюеры выбираются только из этого же списка
|
||||||
|
- `FRONTEND_USERS` - необязательно, CSV логинов Gitea frontend-команды; если автор PR входит в этот список, ревьюеры выбираются только из этого же списка
|
||||||
- `APPROVALS_REQUIRED_FOR_MERGE` - необязательно, по умолчанию `2` (порог approve для сообщения "можно мерджить")
|
- `APPROVALS_REQUIRED_FOR_MERGE` - необязательно, по умолчанию `2` (порог approve для сообщения "можно мерджить")
|
||||||
- `NOTIFY_ON_WEEKENDS` - необязательно, по умолчанию `true`; если `false`, бот не отправляет ЛС в субботу и воскресенье (МСК)
|
- `NOTIFY_ON_WEEKENDS` - необязательно, по умолчанию `true`; если `false`, бот не отправляет ЛС в субботу и воскресенье (МСК)
|
||||||
- `ALLOW_SELF_ASSIGN` - необязательно, по умолчанию `false` (для локального теста можно `true`)
|
- `ALLOW_SELF_ASSIGN` - необязательно, по умолчанию `false` (для локального теста можно `true`)
|
||||||
@ -52,6 +54,8 @@ python -m uvicorn main:app --host 0.0.0.0 --port 8080 --reload --env-file .env
|
|||||||
- `GITEA_COUNT_REPOS` - необязательно, CSV репозиториев `org/repo` для расчета нагрузки (исторический счетчик назначений)
|
- `GITEA_COUNT_REPOS` - необязательно, CSV репозиториев `org/repo` для расчета нагрузки (исторический счетчик назначений)
|
||||||
- `BOT_DB_PATH` - необязательно, по умолчанию `botreviewer.sqlite3`
|
- `BOT_DB_PATH` - необязательно, по умолчанию `botreviewer.sqlite3`
|
||||||
|
|
||||||
|
Если автор PR не входит ни в `BACKEND_USERS`, ни в `FRONTEND_USERS`, бот использует общий пул доступных пользователей (как раньше).
|
||||||
|
|
||||||
## Webhook-эндпоинты
|
## Webhook-эндпоинты
|
||||||
|
|
||||||
- Обновления Telegram: `POST /telegram/webhook`
|
- Обновления Telegram: `POST /telegram/webhook`
|
||||||
|
|||||||
41
main.py
41
main.py
@ -51,6 +51,16 @@ AUTO_ASSIGN_EXCLUDED_AUTHORS = {
|
|||||||
for v in os.getenv("AUTO_ASSIGN_EXCLUDED_AUTHORS", "").split(",")
|
for v in os.getenv("AUTO_ASSIGN_EXCLUDED_AUTHORS", "").split(",")
|
||||||
if v.strip()
|
if v.strip()
|
||||||
}
|
}
|
||||||
|
BACKEND_USERS = {
|
||||||
|
v.strip().lower()
|
||||||
|
for v in os.getenv("BACKEND_USERS", "").split(",")
|
||||||
|
if v.strip()
|
||||||
|
}
|
||||||
|
FRONTEND_USERS = {
|
||||||
|
v.strip().lower()
|
||||||
|
for v in os.getenv("FRONTEND_USERS", "").split(",")
|
||||||
|
if v.strip()
|
||||||
|
}
|
||||||
NOTIFY_ON_WEEKENDS = os.getenv("NOTIFY_ON_WEEKENDS", "true").lower() == "true"
|
NOTIFY_ON_WEEKENDS = os.getenv("NOTIFY_ON_WEEKENDS", "true").lower() == "true"
|
||||||
MSK_TZ = timezone(timedelta(hours=3))
|
MSK_TZ = timezone(timedelta(hours=3))
|
||||||
|
|
||||||
@ -529,6 +539,23 @@ def choose_reviewer(candidates: list[RegisteredUser]) -> RegisteredUser:
|
|||||||
return random.choice(candidates)
|
return random.choice(candidates)
|
||||||
|
|
||||||
|
|
||||||
|
def get_reviewer_group_for_author(author_login: str) -> tuple[set[str] | None, str]:
|
||||||
|
author_login_norm = author_login.strip().lower()
|
||||||
|
if author_login_norm in BACKEND_USERS:
|
||||||
|
return BACKEND_USERS, "backend"
|
||||||
|
if author_login_norm in FRONTEND_USERS:
|
||||||
|
return FRONTEND_USERS, "frontend"
|
||||||
|
return None, ""
|
||||||
|
|
||||||
|
|
||||||
|
def filter_candidates_by_author_group(author_login: str, candidates: list[RegisteredUser]) -> tuple[list[RegisteredUser], str]:
|
||||||
|
configured_group, group_name = get_reviewer_group_for_author(author_login)
|
||||||
|
if configured_group is None:
|
||||||
|
return candidates, ""
|
||||||
|
filtered = [candidate for candidate in candidates if candidate.gitea_login.strip().lower() in configured_group]
|
||||||
|
return filtered, group_name
|
||||||
|
|
||||||
|
|
||||||
async def build_reviewer_load_counts(candidates: list[RegisteredUser], current_repo_full_name: str) -> dict[str, int]:
|
async def build_reviewer_load_counts(candidates: list[RegisteredUser], current_repo_full_name: str) -> dict[str, int]:
|
||||||
_ = current_repo_full_name
|
_ = current_repo_full_name
|
||||||
return {
|
return {
|
||||||
@ -835,13 +862,20 @@ async def handle_pr_opened(payload: dict[str, Any]) -> None:
|
|||||||
log_warn("[gitea] Клиент не настроен, автоназначение пропущено.")
|
log_warn("[gitea] Клиент не настроен, автоназначение пропущено.")
|
||||||
return
|
return
|
||||||
|
|
||||||
candidates = storage.get_available_candidates(
|
all_candidates = storage.get_available_candidates(
|
||||||
author_login=author_login,
|
author_login=author_login,
|
||||||
today=msk_now().date(),
|
today=msk_now().date(),
|
||||||
allow_self_assign=ALLOW_SELF_ASSIGN,
|
allow_self_assign=ALLOW_SELF_ASSIGN,
|
||||||
)
|
)
|
||||||
|
candidates, assigned_group_name = filter_candidates_by_author_group(author_login, all_candidates)
|
||||||
if not candidates:
|
if not candidates:
|
||||||
log_warn(f"[assign] Нет доступных кандидатов для {repo_full_name}#{pr_number}.")
|
if assigned_group_name:
|
||||||
|
log_warn(
|
||||||
|
f"[assign] Нет доступных кандидатов для группы '{assigned_group_name}' "
|
||||||
|
f"({repo_full_name}#{pr_number}, author={author_login})."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
log_warn(f"[assign] Нет доступных кандидатов для {repo_full_name}#{pr_number}.")
|
||||||
return
|
return
|
||||||
|
|
||||||
selected_reviewers, counts = await choose_reviewers_with_remote_load(
|
selected_reviewers, counts = await choose_reviewers_with_remote_load(
|
||||||
@ -856,7 +890,8 @@ async def handle_pr_opened(payload: dict[str, Any]) -> None:
|
|||||||
|
|
||||||
log_info(
|
log_info(
|
||||||
f"[assign] Автоназначение для {repo_full_name}#{pr_number}. "
|
f"[assign] Автоназначение для {repo_full_name}#{pr_number}. "
|
||||||
f"Кандидаты={len(candidates)}, выбраны={selected_logins}, нагрузки={dict(counts)}"
|
f"Кандидаты={len(candidates)} из {len(all_candidates)}, "
|
||||||
|
f"группа={assigned_group_name or 'all'}, выбраны={selected_logins}, нагрузки={dict(counts)}"
|
||||||
)
|
)
|
||||||
await gitea_client.assign_reviewers(repo_full_name, int(pr_number), selected_logins)
|
await gitea_client.assign_reviewers(repo_full_name, int(pr_number), selected_logins)
|
||||||
await gitea_client.add_label(repo_full_name, int(pr_number), AUTO_ASSIGNED_LABEL)
|
await gitea_client.add_label(repo_full_name, int(pr_number), AUTO_ASSIGNED_LABEL)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user