Fix update service: use GitHub API instead of git fetch for checking updates
This commit is contained in:
@@ -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,94 +142,112 @@ 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:
|
||||||
|
ensure_safe_directory()
|
||||||
if not result["project_mounted"]:
|
remote_result = subprocess.run(
|
||||||
result["error"] = "Project directory not mounted. Add volume mount in docker-compose.yml: ./:/project"
|
["git", "remote", "get-url", "origin"],
|
||||||
return result
|
|
||||||
|
|
||||||
# Добавляем safe.directory для git
|
|
||||||
ensure_safe_directory()
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Fetch последних изменений без merge
|
|
||||||
fetch_result = subprocess.run(
|
|
||||||
["git", "fetch", "origin", "main"],
|
|
||||||
cwd=PROJECT_ROOT,
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
timeout=30
|
|
||||||
)
|
|
||||||
|
|
||||||
if fetch_result.returncode != 0:
|
|
||||||
result["error"] = f"Failed to fetch updates: {fetch_result.stderr}"
|
|
||||||
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:
|
|
||||||
result["error"] = "Failed to get remote version"
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Сравнить версии
|
|
||||||
if result["latest_version"] != current_version:
|
|
||||||
result["update_available"] = True
|
|
||||||
|
|
||||||
# Получить changelog из удалённого репозитория
|
|
||||||
changelog_result = subprocess.run(
|
|
||||||
["git", "show", "origin/main:CHANGELOG.md"],
|
|
||||||
cwd=PROJECT_ROOT,
|
cwd=PROJECT_ROOT,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=10
|
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:
|
||||||
|
# Используем GitHub API для получения файлов
|
||||||
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||||
|
# Получить VERSION из main ветки
|
||||||
|
version_url = f"https://raw.githubusercontent.com/{owner}/{repo}/main/VERSION"
|
||||||
|
version_response = await client.get(version_url)
|
||||||
|
|
||||||
if changelog_result.returncode == 0:
|
if version_response.status_code == 200:
|
||||||
# Извлекаем только последнюю версию из changelog
|
result["latest_version"] = version_response.text.strip()
|
||||||
changelog_lines = changelog_result.stdout.split('\n')
|
else:
|
||||||
latest_changelog = []
|
result["error"] = f"Failed to fetch VERSION file: HTTP {version_response.status_code}"
|
||||||
in_latest_version = False
|
return result
|
||||||
version_count = 0
|
|
||||||
|
# Сравнить версии
|
||||||
|
if result["latest_version"] != current_version:
|
||||||
|
result["update_available"] = True
|
||||||
|
|
||||||
for line in changelog_lines:
|
# Получить CHANGELOG.md
|
||||||
if line.startswith('## [v'):
|
changelog_url = f"https://raw.githubusercontent.com/{owner}/{repo}/main/CHANGELOG.md"
|
||||||
version_count += 1
|
changelog_response = await client.get(changelog_url)
|
||||||
if version_count == 1:
|
|
||||||
in_latest_version = True
|
if changelog_response.status_code == 200:
|
||||||
elif version_count == 2:
|
# Извлекаем только последнюю версию из changelog
|
||||||
break
|
changelog_lines = changelog_response.text.split('\n')
|
||||||
|
latest_changelog = []
|
||||||
|
in_latest_version = False
|
||||||
|
version_count = 0
|
||||||
|
|
||||||
if in_latest_version:
|
for line in changelog_lines:
|
||||||
latest_changelog.append(line)
|
if line.startswith('## [v'):
|
||||||
|
version_count += 1
|
||||||
result["changelog"] = '\n'.join(latest_changelog)
|
if version_count == 1:
|
||||||
|
in_latest_version = True
|
||||||
|
elif version_count == 2:
|
||||||
|
break
|
||||||
|
|
||||||
|
if in_latest_version:
|
||||||
|
latest_changelog.append(line)
|
||||||
|
|
||||||
|
result["changelog"] = '\n'.join(latest_changelog)
|
||||||
|
|
||||||
|
# Получить информацию о коммитах через API
|
||||||
|
if result["project_mounted"]:
|
||||||
|
try:
|
||||||
|
# Получить локальный commit hash
|
||||||
|
local_commit_result = subprocess.run(
|
||||||
|
["git", "rev-parse", "HEAD"],
|
||||||
|
cwd=PROJECT_ROOT,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
|
||||||
|
if local_commit_result.returncode == 0:
|
||||||
|
local_commit = local_commit_result.stdout.strip()
|
||||||
|
|
||||||
|
# Получить удаленный 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:
|
||||||
behind_result = subprocess.run(
|
|
||||||
["git", "rev-list", "--count", "HEAD..origin/main"],
|
|
||||||
cwd=PROJECT_ROOT,
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
timeout=10
|
|
||||||
)
|
|
||||||
|
|
||||||
if behind_result.returncode == 0:
|
|
||||||
result["commits_behind"] = int(behind_result.stdout.strip())
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user