""" Gitee 仓库管理模块 负责创建仓库、推送代码、查询 Action 状态 """ import os import logging import requests import base64 from typing import Dict, List, Optional, Any from datetime import datetime logger = logging.getLogger(__name__) class GiteeManager: """Gitee 仓库管理器""" def __init__(self): # Gitee 配置 self.gitee_api_url = os.getenv("GITEE_API_URL", "http://gitee.ath.cx:3000/api/v1") self.gitee_token = os.getenv("GITEE_TOKEN", "") self.gitee_username = os.getenv("GITEE_USERNAME", "zhanggangyong") self.gitee_password = os.getenv("GITEE_PASSWORD", "") self.gitee_owner = os.getenv("GITEE_OWNER", "xiaohei") self.gitee_base_url = os.getenv("GITEE_BASE_URL", "http://gitee.ath.cx:3000") # 模板仓库(参考 http://gitee.ath.cx:3000/xiaohei/cicd-AKS) self.template_repo = os.getenv("GITEE_TEMPLATE_REPO", "cicd-AKS") # ACR 配置 self.acr_registry = os.getenv("ACR_REGISTRY", "agnettaiji.azurecr.io") self.acr_namespace = os.getenv("ACR_NAMESPACE", "ai-agents") def _get_headers(self) -> Dict[str, str]: """获取请求头(支持 Token 或 Basic Auth)""" headers = { "Content-Type": "application/json", "Accept": "application/json" } if self.gitee_token: headers["Authorization"] = f"token {self.gitee_token}" elif self.gitee_username and self.gitee_password: # 使用 Basic Auth import base64 credentials = base64.b64encode( f"{self.gitee_username}:{self.gitee_password}".encode() ).decode() headers["Authorization"] = f"Basic {credentials}" return headers def create_repository( self, repo_name: str, description: str = "", private: bool = False ) -> Dict[str, Any]: """ 创建 Gitee 仓库 Args: repo_name: 仓库名称 description: 仓库描述 private: 是否私有 Returns: 仓库信息 """ url = f"{self.gitee_api_url}/user/repos" data = { "name": repo_name, "description": description, "private": private, "auto_init": True, # 自动初始化 "default_branch": "main" } try: response = requests.post( url, json=data, headers=self._get_headers(), timeout=30 ) if response.status_code == 201: repo_info = response.json() owner = repo_info.get("owner", {}).get("login", self.gitee_username) logger.info(f"✅ Gitee 仓库创建成功: {owner}/{repo_name}") return { "success": True, "repo_name": repo_name, "owner": owner, "clone_url": repo_info.get("clone_url"), "html_url": repo_info.get("html_url"), "ssh_url": repo_info.get("ssh_url") } elif response.status_code == 409: # 仓库已存在 owner = self.gitee_username or self.gitee_owner logger.warning(f"仓库已存在: {repo_name}") return { "success": True, "repo_name": repo_name, "owner": owner, "clone_url": f"{self.gitee_base_url}/{owner}/{repo_name}.git", "html_url": f"{self.gitee_base_url}/{owner}/{repo_name}", "exists": True } else: error_msg = response.json().get("message", response.text) logger.error(f"创建仓库失败: {error_msg}") return {"success": False, "error": error_msg} except Exception as e: logger.error(f"创建仓库异常: {e}") return {"success": False, "error": str(e)} def push_files( self, repo_name: str, files: Dict[str, str], commit_message: str = "Initial commit", branch: str = "main", owner: str = None ) -> Dict[str, Any]: """ 推送文件到仓库 Args: repo_name: 仓库名称 files: 文件字典 {path: content} commit_message: 提交信息 branch: 分支名 owner: 仓库所有者(默认使用配置的用户名) Returns: 推送结果 """ import time # 使用当前用户作为所有者 repo_owner = owner or self.gitee_username or self.gitee_owner results = {"success": True, "files": [], "owner": repo_owner} for file_path, content in files.items(): try: # 使用 Contents API 创建/更新文件 url = f"{self.gitee_api_url}/repos/{repo_owner}/{repo_name}/contents/{file_path}" # 检查文件是否存在 check_response = requests.get(url, headers=self._get_headers(), timeout=10) sha = None if check_response.status_code == 200: sha = check_response.json().get("sha") data = { "message": commit_message, "content": base64.b64encode(content.encode()).decode(), "branch": branch } if sha: data["sha"] = sha response = requests.put( url, json=data, headers=self._get_headers(), timeout=30 ) # 添加短暂延迟避免 API 限流 time.sleep(0.5) if response.status_code in [200, 201]: results["files"].append({ "path": file_path, "status": "success" }) logger.info(f"✅ 文件推送成功: {file_path}") else: results["files"].append({ "path": file_path, "status": "failed", "error": response.text }) results["success"] = False except Exception as e: results["files"].append({ "path": file_path, "status": "failed", "error": str(e) }) results["success"] = False return results def get_action_status(self, repo_name: str) -> Dict[str, Any]: """ 获取 Gitee Action 运行状态 Args: repo_name: 仓库名称 Returns: Action 运行状态 """ # Gitea Actions API url = f"{self.gitee_api_url}/repos/{self.gitee_owner}/{repo_name}/actions/runs" try: response = requests.get( url, headers=self._get_headers(), timeout=30 ) if response.status_code == 200: data = response.json() runs = data.get("workflow_runs", []) if not runs: return { "success": True, "status": "no_runs", "message": "暂无 Action 运行记录" } # 获取最新的运行 latest_run = runs[0] if runs else None if latest_run: return { "success": True, "status": latest_run.get("status"), "conclusion": latest_run.get("conclusion"), "run_id": latest_run.get("id"), "created_at": latest_run.get("created_at"), "updated_at": latest_run.get("updated_at"), "html_url": latest_run.get("html_url") } elif response.status_code == 404: return { "success": True, "status": "not_configured", "message": "Actions 未配置或仓库不存在" } else: return { "success": False, "error": response.text } except Exception as e: logger.error(f"获取 Action 状态失败: {e}") return {"success": False, "error": str(e)} def get_latest_workflow_run(self, repo_name: str, workflow_name: str = None) -> Dict[str, Any]: """ 获取最新的 Workflow 运行信息 Args: repo_name: 仓库名称 workflow_name: 工作流名称(可选) Returns: Workflow 运行信息 """ url = f"{self.gitee_api_url}/repos/{self.gitee_owner}/{repo_name}/actions/runs" try: response = requests.get( url, headers=self._get_headers(), params={"per_page": 10}, timeout=30 ) if response.status_code == 200: data = response.json() runs = data.get("workflow_runs", []) # 如果指定了工作流名称,过滤 if workflow_name and runs: runs = [r for r in runs if r.get("name") == workflow_name] if runs: latest = runs[0] return { "success": True, "run": { "id": latest.get("id"), "name": latest.get("name"), "status": latest.get("status"), "conclusion": latest.get("conclusion"), "created_at": latest.get("created_at"), "updated_at": latest.get("updated_at"), "run_number": latest.get("run_number") } } else: return { "success": True, "run": None, "message": "暂无运行记录" } else: return {"success": False, "error": response.text} except Exception as e: return {"success": False, "error": str(e)} def delete_repository(self, repo_name: str) -> Dict[str, Any]: """删除仓库""" url = f"{self.gitee_api_url}/repos/{self.gitee_owner}/{repo_name}" try: response = requests.delete( url, headers=self._get_headers(), timeout=30 ) if response.status_code == 204: logger.info(f"✅ 仓库删除成功: {repo_name}") return {"success": True} else: return {"success": False, "error": response.text} except Exception as e: return {"success": False, "error": str(e)} def set_repo_secret( self, repo_name: str, secret_name: str, secret_value: str, owner: str = None ) -> Dict[str, Any]: """ 设置仓库 Action Secret Args: repo_name: 仓库名称 secret_name: Secret 名称 secret_value: Secret 值 owner: 仓库所有者 Returns: 设置结果 """ repo_owner = owner or self.gitee_username or self.gitee_owner url = f"{self.gitee_api_url}/repos/{repo_owner}/{repo_name}/actions/secrets/{secret_name}" try: response = requests.put( url, json={"data": secret_value}, headers=self._get_headers(), timeout=30 ) if response.status_code in [200, 201, 204]: logger.info(f"✅ Secret 设置成功: {secret_name}") return {"success": True, "secret_name": secret_name} else: return {"success": False, "error": response.text} except Exception as e: logger.error(f"设置 Secret 失败: {e}") return {"success": False, "error": str(e)} def set_repo_secrets( self, repo_name: str, secrets: Dict[str, str], owner: str = None ) -> Dict[str, Any]: """ 批量设置仓库 Action Secrets Args: repo_name: 仓库名称 secrets: Secret 字典 {name: value} owner: 仓库所有者 Returns: 设置结果 """ import time results = {"success": True, "secrets": []} for name, value in secrets.items(): result = self.set_repo_secret(repo_name, name, value, owner) results["secrets"].append({ "name": name, "status": "success" if result["success"] else "failed", "error": result.get("error") }) if not result["success"]: results["success"] = False # 避免 API 限流 time.sleep(0.3) return results # 全局实例 gitee_manager = GiteeManager()