fix(update): a release-tag fetch failure is reported, and never crashes the update

The tag's rev-parse sat outside the try, so an OSError (a missing git, or a
custody refusal) escaped _cmd_update_impl, and a failed tag fetch was
swallowed silently, leaving every version reader on the previous release,
the symptom the fetch exists to prevent. Both are inside the best-effort
block now, and a miss logs a warning and prints one line naming the tag.
(itsflownium review on #144894, #3.)
This commit is contained in:
Teknium committed 2026-10-27 13:23:31 -07:00
1 parent 268f161d08
commit 2434ee787e
2 files changed
+34 -5

No files matched your search

+18 -5
View File
@@ -7,11 +7,14 @@ per function, so test patches on ``hermes_cli.update_cmd`` and the origin module
from __future__ import annotations
import logging
import subprocess
import sys
from pathlib import Path
from typing import Any
logger = logging.getLogger(__name__)
def _git(git_cmd: list[str], root: Path, args: list[str], **kwargs: Any) -> subprocess.CompletedProcess[str]:
from hermes_cli._subprocess_compat import windows_hide_flags
@@ -284,13 +287,23 @@ def fetch_release_tag(git_cmd: list[str], root: Path, request: dict) -> None:
from hermes_cli.update_cmd import NETWORK_GIT_TIMEOUT_SECONDS
tag = request.get("release_tag")
if not tag or _git(git_cmd, root, ["rev-parse", "--verify", "--quiet", f"refs/tags/{tag}"]).returncode == 0:
if not tag:
return
try:
_git(git_cmd, root, ["fetch", "--no-tags", "origin", f"refs/tags/{tag}:refs/tags/{tag}"],
timeout=NETWORK_GIT_TIMEOUT_SECONDS, **no_prompt_git_kwargs())
except (OSError, subprocess.SubprocessError):
pass
if _git(git_cmd, root, ["rev-parse", "--verify", "--quiet", f"refs/tags/{tag}"]).returncode == 0:
return
fetched = _git(git_cmd, root, ["fetch", "--no-tags", "origin", f"refs/tags/{tag}:refs/tags/{tag}"],
timeout=NETWORK_GIT_TIMEOUT_SECONDS, **no_prompt_git_kwargs())
detail = (fetched.stderr or "").strip().splitlines()[-1:] if fetched.returncode else None
except (OSError, subprocess.SubprocessError) as exc: # custody refusals are OSErrors too
detail = [str(exc)]
if detail is not None:
# Not fatal (a mirror may lack the tag), but say so: without it every version reader keeps
# naming the previous release, the symptom this fetch exists to prevent.
reason = detail[0] if detail else "git fetch failed"
logger.warning("Could not fetch release tag %s: %s", tag, reason)
print(f"⚠ Could not fetch the {tag} tag ({reason}); the version shown may name the previous "
"release until the next update fetches it.")
def hold_channel_release_behind_runtime(git_cmd, target_ref: str, channel: str) -> str | None:
@@ -6,8 +6,11 @@ the install stamp and the receipt still named the previous release.
"""
import subprocess
import sys
from types import SimpleNamespace
import pytest
from hermes_cli import update_cmd_check
from hermes_cli.source_releases import SourceTarget
from hermes_cli.version_info import _git_version_info
@@ -47,3 +50,16 @@ def test_stable_update_fetch_brings_the_release_tag(tmp_path, monkeypatch):
info = _git_version_info(checkout)
assert (info.commit, info.base_version, info.display_version) == (release, "0.22.3", "0.22.3")
@pytest.mark.parametrize("git_cmd", [["git"], [sys.executable + "-missing-git"]])
def test_an_unfetchable_release_tag_is_reported_never_fatal(tmp_path, capsys, git_cmd):
"""Best effort means the update goes on, but not silently: without the tag every version
reader keeps naming the previous release. A git that cannot even run (missing binary, a
custody refusal: both OSErrors) is the same non-fatal miss, not a crash of the update."""
_git(tmp_path, "init", "-q", "-b", "main") # no origin: the fetch itself fails
update_cmd_check.fetch_release_tag(git_cmd, tmp_path, {"release_tag": "v9.9.9"})
assert "Could not fetch the v9.9.9 tag" in capsys.readouterr().out