feat(code): show last update check time in dcode doctor (#4307)

The Updates section of `dcode doctor` reported status but not *when* the
last PyPI update check happened. This adds a "Last checked" item (e.g.
`2h ago`, or `never`), read from the offline cache so the command stays
network-free.

Made by [Open
SWE](https://openswe.vercel.app/agents/5810207b-bdbb-6bee-54cf-3b4bcf31a3e4)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
Mason Daugherty
2026-06-26 14:16:14 -04:00
committed by GitHub
parent e323d0c7d9
commit b669f37fb0
4 changed files with 171 additions and 2 deletions
+25 -1
View File
@@ -218,11 +218,35 @@ def _collect_updates() -> DiagnosticSection:
update_status = f"v{latest} available"
else:
update_status = "up to date"
items.append(DiagnosticItem("Latest version", update_status))
items.extend(
(
DiagnosticItem("Latest version", update_status),
DiagnosticItem("Last checked", _format_last_checked()),
)
)
return DiagnosticSection(title="Updates", items=items)
def _format_last_checked() -> str:
"""Return a relative description of the last update check, or `never`.
`never` covers both the no-check-recorded case and, defensively, a stamp
that cannot be formatted. `get_last_update_check_time` only returns finite,
in-range epochs, so the formatting path does not raise here.
"""
from datetime import UTC, datetime
from deepagents_code.sessions import format_relative_timestamp
from deepagents_code.update_check import get_last_update_check_time
checked_at = get_last_update_check_time()
if checked_at is None:
return "never"
iso = datetime.fromtimestamp(checked_at, tz=UTC).isoformat()
return format_relative_timestamp(iso) or "never"
def _sanitize_endpoint(endpoint: str) -> str:
"""Return a paste-safe custom endpoint identifier.
+39 -1
View File
@@ -14,6 +14,7 @@ from __future__ import annotations
import asyncio
import json
import logging
import math
import operator
import os
import re
@@ -240,7 +241,8 @@ def get_cached_update_available() -> tuple[bool, str | None]:
if not isinstance(data, dict):
return False, None
checked_at = data.get("checked_at")
if not isinstance(checked_at, (int, float)):
checked_at = _coerce_checked_at(checked_at)
if checked_at is None:
return False, None
if time.time() - checked_at >= CACHE_TTL:
return False, None
@@ -253,6 +255,42 @@ def get_cached_update_available() -> tuple[bool, str | None]:
return False, None
def _coerce_checked_at(value: object) -> float | None:
"""Return a valid epoch timestamp from cached state, or `None`."""
if not isinstance(value, (int, float)) or isinstance(value, bool):
return None
checked_at = float(value)
if not math.isfinite(checked_at):
return None
try:
datetime.fromtimestamp(checked_at, tz=UTC)
except (OverflowError, OSError, ValueError):
return None
return checked_at
def get_last_update_check_time() -> float | None:
"""Return the epoch time of the last PyPI update check, or `None`.
Reads the `checked_at` stamp recorded in `CACHE_FILE` when the update cache
is written (primarily by `get_latest_version`; also seeded by
`_write_release_requires_prereleases`). Missing, corrupt, or non-numeric
data fail-soft to `None` so callers can render an "unknown" state without
contacting the network.
"""
try:
if not CACHE_FILE.exists():
return None
data = json.loads(CACHE_FILE.read_text(encoding="utf-8"))
if not isinstance(data, dict):
return None
checked_at = data.get("checked_at")
except (OSError, json.JSONDecodeError, TypeError):
logger.debug("Failed to read last update check time", exc_info=True)
return None
return _coerce_checked_at(checked_at)
def _requires_prerelease_dependency(requirements: Sequence[object] | None) -> bool:
"""Return whether any requirement specifier names a pre-release version.
+59
View File
@@ -4,6 +4,7 @@ import argparse
import io
import json
import sys
import time
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
@@ -194,6 +195,64 @@ class TestCollectTracing:
assert labels["Replica project"] == "replica"
class TestCollectUpdates:
"""Tests for the Updates diagnostic section."""
def _labels(self, cache_file: Path) -> dict[str, str]:
"""Collect the Updates labels, reading `checked_at` from `cache_file`.
Patches `CACHE_FILE` rather than `get_last_update_check_time` so the
section flows through the genuine reader and the epoch -> ISO ->
relative-time conversion, not a stub.
"""
from deepagents_code.doctor import _collect_updates
with (
patch("deepagents_code.config._is_editable_install", return_value=False),
patch(
"deepagents_code.update_check.is_update_check_enabled",
return_value=True,
),
patch(
"deepagents_code.update_check.is_auto_update_enabled",
return_value=True,
),
patch(
"deepagents_code.update_check.get_cached_update_available",
return_value=(False, "1.0.0"),
),
patch("deepagents_code.update_check.CACHE_FILE", cache_file),
):
section = _collect_updates()
return {item.label: item.value for item in section.items}
def test_last_checked_shows_relative_time(self, tmp_path: Path) -> None:
"""A check stamped an hour ago renders as `1h ago` via the real read."""
cache = tmp_path / "latest_version.json"
cache.write_text(
json.dumps({"checked_at": time.time() - 3600}), encoding="utf-8"
)
assert self._labels(cache)["Last checked"] == "1h ago"
def test_last_checked_just_now_on_future_stamp(self, tmp_path: Path) -> None:
"""A future stamp (clock skew) renders as `just now`, not a crash."""
cache = tmp_path / "latest_version.json"
cache.write_text(
json.dumps({"checked_at": time.time() + 3600}), encoding="utf-8"
)
assert self._labels(cache)["Last checked"] == "just now"
def test_last_checked_never_without_cache(self, tmp_path: Path) -> None:
"""An absent cache reports `never` rather than crashing."""
assert self._labels(tmp_path / "latest_version.json")["Last checked"] == "never"
def test_last_checked_never_on_corrupt_stamp(self, tmp_path: Path) -> None:
"""A non-finite stamp fails soft to `never` instead of crashing doctor."""
cache = tmp_path / "latest_version.json"
cache.write_text(json.dumps({"checked_at": float("nan")}), encoding="utf-8")
assert self._labels(cache)["Last checked"] == "never"
class TestCommitHash:
"""Tests for git commit hash detection."""
@@ -52,6 +52,7 @@ from deepagents_code.update_check import (
format_shadowed_dcode_fix_command,
format_shadowed_dcode_warning,
get_cached_update_available,
get_last_update_check_time,
get_latest_version,
get_release_time,
get_sdk_release_time,
@@ -273,6 +274,21 @@ class TestCachedUpdateAvailable:
mock_get.assert_not_called()
@pytest.mark.parametrize("checked_at", [float("nan"), float("inf"), 1e100])
def test_invalid_numeric_checked_at_returns_no_answer_without_http(
self, cache_file, checked_at: float
) -> None:
"""Corrupt numeric timestamps must not be treated as fresh cache."""
cache_file.write_text(
json.dumps({"version": "99.0.0", "checked_at": checked_at}),
encoding="utf-8",
)
with patch("requests.get") as mock_get:
assert get_cached_update_available() == (False, None)
mock_get.assert_not_called()
def test_missing_cache_returns_no_answer_without_http(self, cache_file) -> None:
"""Missing cache should not block startup on a network request."""
assert not cache_file.exists()
@@ -291,6 +307,38 @@ class TestCachedUpdateAvailable:
assert get_cached_update_available() == (False, __version__)
class TestGetLastUpdateCheckTime:
def test_reads_checked_at(self, cache_file) -> None:
"""The stored `checked_at` epoch is returned as a float."""
now = time.time()
cache_file.write_text(json.dumps({"checked_at": now}), encoding="utf-8")
assert get_last_update_check_time() == pytest.approx(now)
def test_missing_cache_returns_none(self, cache_file) -> None: # noqa: ARG002
"""An absent cache yields `None`."""
assert get_last_update_check_time() is None
def test_corrupt_cache_returns_none(self, cache_file) -> None:
"""Unparseable cache data fails soft to `None`."""
cache_file.write_text("{not valid json", encoding="utf-8")
assert get_last_update_check_time() is None
def test_non_numeric_checked_at_returns_none(self, cache_file) -> None:
"""A non-numeric (or boolean) `checked_at` is ignored."""
cache_file.write_text(json.dumps({"checked_at": "soon"}), encoding="utf-8")
assert get_last_update_check_time() is None
cache_file.write_text(json.dumps({"checked_at": True}), encoding="utf-8")
assert get_last_update_check_time() is None
@pytest.mark.parametrize("checked_at", [float("nan"), float("inf"), 1e100])
def test_invalid_numeric_checked_at_returns_none(
self, cache_file, checked_at: float
) -> None:
"""Invalid numeric `checked_at` values are ignored."""
cache_file.write_text(json.dumps({"checked_at": checked_at}), encoding="utf-8")
assert get_last_update_check_time() is None
class TestGetLatestVersion:
def test_fresh_fetch(self, cache_file) -> None:
"""Successful PyPI fetch writes cache and returns version."""