#!/usr/bin/env zsh # ============================================================================== # Gitea Client Wrapper (gitea-client.zsh) - FULLY FEATURED # # PURPOSE: # Высокоуровневая, отказоустойчивая обертка над 'tea' для # использования AI-агентами. Абстрагирует сложность управления # контекстом, предоставляет атомарные операции и прозрачный вывод. # # VERSION: 1.4 # CHANGE LOG: # - v1.4: Добавлены критически важные функции 'create-task' и 'create-pr', # чтобы полностью соответствовать архитектуре системы # 'AI_Dev_System_Ivanov_Gemini_v1.1'. # - v1.3: Исправлена критическая ошибка парсинга remote URL, # когда в SSH адресе присутствует номер порта. # Логика `initialize` полностью переработана для надежности. # ============================================================================== # --- ОБРАБОТКА ОШИБОК И ВЫХОД --- set -e set -u set -o pipefail # --- ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ И НАСТРОЙКИ --- ROLE_NAME="" REPO_SLUG="" VERBOSE="true" # --- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ --- log_info() { if [[ "$VERBOSE" == "true" ]]; then echo -e "\033[34m[INFO]\033[0m $1" >&2 fi } error_exit() { echo -e "\033[31m[ОШИБКА ЛОГИКИ]\033[0m $1" >&2 exit 1 } # --- ЦЕНТРАЛИЗОВАННЫЙ ИСПОЛНИТЕЛЬ КОМАНД --- run_command() { log_info "Выполняется команда:" echo -e "\033[33m[CMD]\033[0m $@" >&2 local output local exit_code output=$("$@" 2>&1) exit_code=$? if [[ $exit_code -ne 0 ]]; then echo -e "\033[31m[ОШИБКА ВЫПОЛНЕНИЯ]\033[0m Команда завершилась с кодом $exit_code." >&2 echo "------------------------- ВЫВОД КОМАНДЫ -------------------------" >&2 echo "$output" >&2 echo "-----------------------------------------------------------------" >&2 exit $exit_code fi echo "$output" } # --- ИНИЦИАЛИЗАЦИЯ И ПРОВЕРКИ --- initialize() { log_info "Инициализация клиента..." for cmd in git tea jq; do command -v "$cmd" >/dev/null 2>&1 || error_exit "Зависимость не найдена: $cmd. Пожалуйста, установите ее." done local git_remote_url git_remote_url=$(run_command git remote get-url origin) || error_exit "Не удалось определить remote URL." # Надежная логика парсинга для SSH и HTTPS URL local path_part=$(echo "$git_remote_url" | sed -n 's|.*//[^/]*/\(.*\)|\1|p') if [[ -z "$path_part" ]]; then path_part=$(echo "$git_remote_url" | sed -n 's|.*:\(.*\)| \1|p') fi REPO_SLUG=$(echo "$path_part" | sed 's/\.git$//') [[ -z "$REPO_SLUG" ]] && error_exit "Не удалось извлечь 'owner/repo' из URL: $git_remote_url" log_info "Репозиторий определен как: $REPO_SLUG" [[ -z "$ROLE_NAME" ]] && error_exit "Имя роли (ROLE_NAME) не было предоставлено." log_info "Роль установлена: $ROLE_NAME" } # --- ФУНКЦИИ КОМАНД --- create_task() { local title="" body="" assignee="" labels="" while [[ $# -gt 0 ]]; do case $1 in --title) title="$2"; shift 2;; --body) body="$2"; shift 2;; --assignee) assignee="$2"; shift 2;; --labels) labels="$2"; shift 2;; *) shift;; esac; done [[ -z "$title" || -z "$body" || -z "$assignee" || -z "$labels" ]] && error_exit "Для 'create-task' требуются флаги --title, --body, --assignee, --labels." log_info "Создание задачи для '$assignee'..." run_command tea issues create --repo "$REPO_SLUG" \ --title "$title" \ --description "$body" \ --assignees "$assignee" \ --labels "$labels" log_info "Задача успешно создана." } create_pr() { local title="" body="" head="" base="main" while [[ $# -gt 0 ]]; do case $1 in --title) title="$2"; shift 2;; --body) body="$2"; shift 2;; --head) head="$2"; shift 2;; --base) base="$2"; shift 2;; *) shift;; esac; done [[ -z "$title" || -z "$body" || -z "$head" ]] && error_exit "Для 'create-pr' требуются флаги --title, --body, --head." log_info "Создание Pull Request из ветки '$head' в '$base' ભા" run_command tea pr create --repo "$REPO_SLUG" \ --title "$title" \ --description "$body" \ --head "$head" \ --base "$base" log_info "Pull Request успешно создан." } find_tasks() { local task_type="" while [[ $# -gt 0 ]]; do case $1 in --type) task_type="$2"; shift 2;; *) shift;; esac; done [[ -z "$task_type" ]] && error_exit "Для 'find-tasks' требуется флаг --type." local issues_json issues_json=$(run_command tea issues list --output json --repo "$REPO_SLUG" --labels "$task_type" --state "open") if [[ -z "$issues_json" || "$issues_json" == "[]" ]]; then echo "[]" else echo "$issues_json" | jq -c '.[] | select(.labels[]?.name == "status::pending")' fi } update_task_status() { local issue_id="" old_status="" new_status="" while [[ $# -gt 0 ]]; do case $1 in --issue-id) issue_id="$2"; shift 2;; --old) old_status="$2"; shift 2;; --new) new_status="$2"; shift 2;; *) shift;; esac; done [[ -z "$issue_id" || -z "$old_status" || -z "$new_status" ]] && error_exit "Для 'update-task-status' требуются флаги --issue-id, --old, --new." run_command tea issues edit "$issue_id" --repo "$REPO_SLUG" --remove-labels "$old_status" --add-labels "$new_status" log_info "Статус задачи #$issue_id успешно обновлен на '$new_status'." } merge_and_complete() { local issue_id="" pr_id="" branch_name="" while [[ $# -gt 0 ]]; do case $1 in --issue-id) issue_id="$2"; shift 2;; --pr-id) pr_id="$2"; shift 2;; --branch) branch_name="$2"; shift 2;; *) shift;; esac; done [[ -z "$issue_id" || -z "$pr_id" || -z "$branch_name" ]] && error_exit "Для 'merge-and-complete' требуются флаги --issue-id, --pr-id, --branch." log_info "Слияние PR #$pr_id..." run_command tea pr merge "$pr_id" --repo "$REPO_SLUG" log_info "Удаление ветки '$branch_name'..." run_command git push origin --delete "$branch_name" log_info "Закрытие задачи #$issue_id..." run_command tea issues close "$issue_id" --repo "$REPO_SLUG" log_info "Процесс успешно завершен." } return_to_dev() { local issue_id="" pr_id="" report="" while [[ $# -gt 0 ]]; do case $1 in --issue-id) issue_id="$2"; shift 2;; --pr-id) pr_id="$2"; shift 2;; --report) report="$2"; shift 2;; *) shift;; esac; done [[ -z "$issue_id" || -z "$pr_id" || -z "$report" ]] && error_exit "Для 'return-to-dev' требуются флаги --issue-id, --pr-id, --report." log_info "Отклонение PR #$pr_id..." run_command tea pr close "$pr_id" --repo "$REPO_SLUG" log_info "Добавление отчета о дефектах в задачу #$issue_id..." run_command tea issues comment create "$issue_id" --repo "$REPO_SLUG" --comment "$report" log_info "Возврат задачи #$issue_id разработчику..." run_command tea issues edit "$issue_id" --repo "$REPO_SLUG" \ --title "[QA -> DEV] FAILED: Fix Defects in PR #$pr_id" \ --assignee "agent-developer" \ --remove-labels "status::in-progress,type::quality-assurance" \ --add-labels "status::failed,type::development" log_info "Задача возвращена." } # --- ГЛАВНЫЙ ДИСПЕТЧЕР КОМАНД --- main() { [[ $# -lt 2 ]] && error_exit "Неверное использование. Требуется: ./gitea-client.zsh [OPTIONS]" ROLE_NAME="$1"; shift local COMMAND="$1"; shift initialize case "$COMMAND" in create-task) create_task "$@";; create-pr) create_pr "$@";; find-tasks) find_tasks "$@";; update-task-status) update_task_status "$@";; merge-and-complete) merge_and_complete "$@";; return-to-dev) return_to_dev "$@";; *) error_exit "Неизвестная команда: '$COMMAND'";; esac } # --- ТОЧКА ВХОДА --- main "$@"