fix: проверка готовности всегда врала, установщик показывал зашитую версию

Три дефекта, найденные при установке владельцем на две машины.

1. Linux: лаунчер писал «Web server failed to respond», хотя сервер
   поднимался нормально — в логе старт API, планировщик квот и прогрев
   кэша без ошибок. Причина в самой проверке: sys импортировался ТОЛЬКО
   внутри except, а использовался в успешной ветке. На здоровом ответе
   возникал NameError, его ловил тот же except, и проверка всегда
   возвращала отказ.
   Доказано исполнением на живом сервере: старая логика -> код 1,
   новая -> код 0.

2. Windows: при нечитаемом манифесте мастер показывал зашитые
   InstalledVersion = "0.1.0" и InstalledDate = "19.08.2026" как факт.
   Владелец видел «старую версию» на свежей установке, хотя проверка
   показала правильный путь и версию 0.1.1. Заглушки заменены на
   «не определена» — выдуманный факт хуже отсутствующего.

3. Манифест писался одним File.WriteAllText: прерванная запись оставляла
   пустой файл, и разбор версии падал на первом символе — ровно это и
   случилось у владельца (JSONDecodeError, char 0). Запись переведена на
   временный файл с переносом.

Попутно: git_commit в манифесте был зашит как "8cddc9f", то есть манифест
сообщал неправду о происхождении сборки. Теперь сборщик проставляет
фактический коммит.

Тесты: 373 passed, ruff чисто. Установщик пересобран.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hermes Team 2026-08-24 09:47:00 +07:00
parent 36b449bc6c
commit 57e7ca2edd
5 changed files with 37 additions and 9 deletions

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
@ -15,6 +15,10 @@ namespace HermesHubSetup
public class SetupEngine
{
public const string HUB_VERSION = "0.1.1";
// Подставляется сборщиком из фактического git-коммита. Раньше здесь
// жил зашитый "8cddc9f", то есть манифест сообщал неправду о том, из
// какого кода собран установщик.
public const string BuildCommit = "36b449b";
public const string MIN_HERMES_VERSION = "0.20.0";
public const string MAX_TESTED_HERMES = "0.20.4";
@ -89,8 +93,12 @@ namespace HermesHubSetup
string installedExe = Path.Combine(TargetInstallDir, "HermesHub.exe");
string pluginManifest = Path.Combine(HermesHome, @"plugins\antigravity-provider\deployment_manifest.json");
IsInstalled = File.Exists(installedExe) || File.Exists(pluginManifest);
InstalledVersion = "0.1.0";
InstalledDate = "19.08.2026";
// Пока манифест не прочитан, версия НЕ известна. Раньше здесь стояли
// зашитые "0.1.0" и "19.08.2026", и при нечитаемом манифесте мастер
// показывал их как установленную версию — владелец видел «старую
// версию» на свежей установке.
InstalledVersion = "не определена";
InstalledDate = "дата неизвестна";
if (File.Exists(pluginManifest))
{
@ -336,9 +344,14 @@ namespace HermesHubSetup
"{{\n \"version\": \"{0}\",\n \"deployed_at\": \"{1}\",\n \"git_commit\": \"{2}\"\n}}",
HUB_VERSION,
DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"),
"8cddc9f"
BuildCommit
);
File.WriteAllText(manifestFile, manifestJson, Encoding.UTF8);
// Запись через временный файл: прерванная запись оставляла
// пустой манифест, и разбор версии падал на первом символе.
string manifestTmp = manifestFile + ".tmp";
File.WriteAllText(manifestTmp, manifestJson, Encoding.UTF8);
if (File.Exists(manifestFile)) File.Delete(manifestFile);
File.Move(manifestTmp, manifestFile);
}
catch { }

View file

@ -29,6 +29,16 @@ if (Test-Path $HubWebCs) {
# Собираем полезную нагрузку: всё, что нужно PerformInstall на целевой машине.
# Она вшивается в exe ресурсом, чтобы установщик был одним файлом и не требовал
# копировать репозиторий.
# Проставляем фактический коммит в исходник установщика, чтобы манифест
# развёртывания не сообщал неправду о происхождении сборки.
$GitSha = (git -C $RepoRoot rev-parse --short HEAD 2>$null)
if ($GitSha) {
$csText = Get-Content $SourceFile -Raw -Encoding UTF8
$csText = [regex]::Replace($csText, 'public const string BuildCommit = "[^"]*";', "public const string BuildCommit = `"$GitSha`";")
Set-Content $SourceFile -Value $csText -Encoding UTF8 -NoNewline
Write-Host "Build commit: $GitSha" -ForegroundColor Gray
}
Write-Host "Packing payload..." -ForegroundColor Cyan
$PayloadDir = Join-Path $env:TEMP ("hubpayload_" + [guid]::NewGuid().ToString("N").Substring(0,8))
New-Item -ItemType Directory -Force $PayloadDir | Out-Null

Binary file not shown.

Binary file not shown.

View file

@ -48,14 +48,19 @@ HEALTH_URL="http://$HOST:$PORT/api/health"
# Function to check server health
check_health() {
# sys импортируется здесь, а не внутри except: раньше на УСПЕШНОМ ответе
# возникал NameError, его ловил тот же except, и проверка всегда сообщала
# об отказе — сервер работал, а лаунчер писал «failed to respond».
"$PYTHON_BIN" -c "
import urllib.request, json
import json
import sys
import urllib.request
try:
req = urllib.request.urlopen('$HEALTH_URL', timeout=0.8)
data = json.loads(req.read().decode('utf-8'))
with urllib.request.urlopen('$HEALTH_URL', timeout=0.8) as resp:
data = json.loads(resp.read().decode('utf-8'))
sys.exit(0 if data.get('ok') is True else 1)
except Exception:
import sys
sys.exit(1)
" >/dev/null 2>&1
}