Fix update service: use GitHub API instead of git fetch for checking updates

This commit is contained in:
2025-12-15 13:38:34 +05:00
parent 46fd3333dc
commit 35efcba41d

View File

@@ -83,9 +83,51 @@ def ensure_safe_directory():
pass pass
def configure_git_for_public_access():
"""Настроить git для работы без аутентификации с публичным репозиторием"""
try:
# Отключить запросы пароля
subprocess.run(
["git", "config", "--global", "credential.helper", ""],
cwd=PROJECT_ROOT,
capture_output=True,
timeout=5
)
# Получить текущий remote URL
remote_result = subprocess.run(
["git", "remote", "get-url", "origin"],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
timeout=5
)
if remote_result.returncode == 0:
current_url = remote_result.stdout.strip()
# Если URL использует https с credentials, очистить их
if "https://" in current_url and "@" in current_url:
# Убрать credentials из URL
clean_url = current_url.split("@")[-1]
clean_url = "https://" + clean_url
subprocess.run(
["git", "remote", "set-url", "origin", clean_url],
cwd=PROJECT_ROOT,
capture_output=True,
timeout=5
)
return True
except Exception as e:
logger.warning(f"Could not configure git: {e}")
return False
async def check_for_updates() -> Dict[str, Any]: async def check_for_updates() -> Dict[str, Any]:
""" """
Проверить наличие обновлений Проверить наличие обновлений через GitHub API
Сравнивает локальную версию с удалённой Сравнивает локальную версию с удалённой
""" """
current_version = get_current_version() current_version = get_current_version()
@@ -100,63 +142,62 @@ async def check_for_updates() -> Dict[str, Any]:
"project_mounted": is_project_mounted() "project_mounted": is_project_mounted()
} }
# Проверяем доступность git и проекта # Получаем URL репозитория
if not result["git_available"]: repo_url = None
result["error"] = "Git is not installed in container. Mount project directory with docker-compose volume." if result["project_mounted"]:
return result try:
if not result["project_mounted"]:
result["error"] = "Project directory not mounted. Add volume mount in docker-compose.yml: ./:/project"
return result
# Добавляем safe.directory для git
ensure_safe_directory() ensure_safe_directory()
remote_result = subprocess.run(
["git", "remote", "get-url", "origin"],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
timeout=5
)
if remote_result.returncode == 0:
repo_url = remote_result.stdout.strip()
except Exception as e:
logger.warning(f"Could not get git remote URL: {e}")
# Парсим owner/repo из URL
owner, repo = None, None
if repo_url:
# https://github.com/owner/repo.git -> owner/repo
if "github.com" in repo_url:
parts = repo_url.replace(".git", "").split("github.com/")
if len(parts) == 2:
owner_repo = parts[1].split("/")
if len(owner_repo) >= 2:
owner, repo = owner_repo[0], owner_repo[1]
if not owner or not repo:
# Fallback на хардкод для этого проекта
owner, repo = "markmorado", "pvemanager"
try: try:
# Fetch последних изменений без merge # Используем GitHub API для получения файлов
fetch_result = subprocess.run( async with httpx.AsyncClient(timeout=30.0) as client:
["git", "fetch", "origin", "main"], # Получить VERSION из main ветки
cwd=PROJECT_ROOT, version_url = f"https://raw.githubusercontent.com/{owner}/{repo}/main/VERSION"
capture_output=True, version_response = await client.get(version_url)
text=True,
timeout=30
)
if fetch_result.returncode != 0: if version_response.status_code == 200:
result["error"] = f"Failed to fetch updates: {fetch_result.stderr}" result["latest_version"] = version_response.text.strip()
return result
# Получить версию из удалённого репозитория
remote_version_result = subprocess.run(
["git", "show", "origin/main:VERSION"],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
timeout=10
)
if remote_version_result.returncode == 0:
result["latest_version"] = remote_version_result.stdout.strip()
else: else:
result["error"] = "Failed to get remote version" result["error"] = f"Failed to fetch VERSION file: HTTP {version_response.status_code}"
return result return result
# Сравнить версии # Сравнить версии
if result["latest_version"] != current_version: if result["latest_version"] != current_version:
result["update_available"] = True result["update_available"] = True
# Получить changelog из удалённого репозитория # Получить CHANGELOG.md
changelog_result = subprocess.run( changelog_url = f"https://raw.githubusercontent.com/{owner}/{repo}/main/CHANGELOG.md"
["git", "show", "origin/main:CHANGELOG.md"], changelog_response = await client.get(changelog_url)
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
timeout=10
)
if changelog_result.returncode == 0: if changelog_response.status_code == 200:
# Извлекаем только последнюю версию из changelog # Извлекаем только последнюю версию из changelog
changelog_lines = changelog_result.stdout.split('\n') changelog_lines = changelog_response.text.split('\n')
latest_changelog = [] latest_changelog = []
in_latest_version = False in_latest_version = False
version_count = 0 version_count = 0
@@ -174,20 +215,39 @@ async def check_for_updates() -> Dict[str, Any]:
result["changelog"] = '\n'.join(latest_changelog) result["changelog"] = '\n'.join(latest_changelog)
# Проверить количество коммитов позади # Получить информацию о коммитах через API
behind_result = subprocess.run( if result["project_mounted"]:
["git", "rev-list", "--count", "HEAD..origin/main"], try:
# Получить локальный commit hash
local_commit_result = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=PROJECT_ROOT, cwd=PROJECT_ROOT,
capture_output=True, capture_output=True,
text=True, text=True,
timeout=10 timeout=5
) )
if behind_result.returncode == 0: if local_commit_result.returncode == 0:
result["commits_behind"] = int(behind_result.stdout.strip()) local_commit = local_commit_result.stdout.strip()
except subprocess.TimeoutExpired: # Получить удаленный commit через API
commits_url = f"https://api.github.com/repos/{owner}/{repo}/commits/main"
commits_response = await client.get(commits_url)
if commits_response.status_code == 200:
remote_commit = commits_response.json()["sha"]
# Сравнить хеши
if local_commit != remote_commit:
result["commits_behind"] = 1 # Упрощенно
except Exception as e:
logger.warning(f"Could not check commits: {e}")
except httpx.TimeoutException:
result["error"] = "Timeout while checking for updates" result["error"] = "Timeout while checking for updates"
except httpx.HTTPError as e:
result["error"] = f"HTTP error: {str(e)}"
logger.error(f"HTTP error checking for updates: {e}")
except Exception as e: except Exception as e:
result["error"] = str(e) result["error"] = str(e)
logger.error(f"Error checking for updates: {e}") logger.error(f"Error checking for updates: {e}")
@@ -222,6 +282,9 @@ async def perform_update() -> Dict[str, Any]:
# Добавляем safe.directory для git # Добавляем safe.directory для git
ensure_safe_directory() ensure_safe_directory()
# Настраиваем git для публичного доступа
configure_git_for_public_access()
# Сбросить статус # Сбросить статус
update_status = { update_status = {
"is_updating": True, "is_updating": True,
@@ -237,16 +300,42 @@ async def perform_update() -> Dict[str, Any]:
update_status["stage"] = "pulling" update_status["stage"] = "pulling"
update_status["progress"] = 10 update_status["progress"] = 10
# Пробуем pull с публичным доступом
pull_result = subprocess.run( pull_result = subprocess.run(
["git", "pull", "origin", "main"], ["git", "pull", "origin", "main"],
cwd=PROJECT_ROOT, cwd=PROJECT_ROOT,
capture_output=True, capture_output=True,
text=True, text=True,
timeout=120 timeout=120,
env={**os.environ, "GIT_TERMINAL_PROMPT": "0"} # Отключить запрос пароля
) )
if pull_result.returncode != 0: if pull_result.returncode != 0:
raise Exception(f"Git pull failed: {pull_result.stderr}") # Если не получилось через pull, пробуем через reset
# Fetch с установкой remote для публичного доступа
fetch_result = subprocess.run(
["git", "fetch", "origin", "main"],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
timeout=120,
env={**os.environ, "GIT_TERMINAL_PROMPT": "0"}
)
if fetch_result.returncode != 0:
raise Exception(f"Git fetch failed: {fetch_result.stderr}")
# Reset к удаленной версии
reset_result = subprocess.run(
["git", "reset", "--hard", "origin/main"],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
timeout=30
)
if reset_result.returncode != 0:
raise Exception(f"Git reset failed: {reset_result.stderr}")
# Stage 2: Docker build (50%) # Stage 2: Docker build (50%)
update_status["stage"] = "building" update_status["stage"] = "building"