changelog
¶
Fetches and parses mujoco-mojo's CHANGELOG.md into structured version entries, for Dojo's "What's Changed" footer button and for scripts/verify_changelog_by_tag.py's pre-push check - one parser, so the two can't drift apart.
The changelog is always read live from GitHub; there is no local-file fallback, so a fetch failure is just an error the caller surfaces, the same way any other failed request already is.
LAST_SEEN_VERSION_FILE
module-attribute
¶
LAST_SEEN_VERSION_FILE = (
MUJOCO_MOJO_DIR / "last_seen_changelog_version.txt"
)
The latest changelog version the user has explicitly acknowledged (by closing the What's Changed modal on it). Read fresh on every GET /changelog/data request, not cached in memory, so a mark-seen write takes effect immediately - both for a later page load in the same Dojo process and across a full restart of it.
ChangelogEntry
¶
Bases: BaseModel
One ## Version X.Y.Z (YYYY-MM-DD) section of the changelog.
-
Code Reference
mujoco_mojoutilschangelog
-
Code Reference
mujoco_mojoutilslayersdojorouterschangelogChangelogEntryOut
version_tuple
¶
The leading X.Y.Z as an int triple, for > comparison. A dev-build suffix (e.g. 2.6.11.dev3+g1234abc) is ignored past the prefix; a string with no such prefix degrades to (0, 0, 0) rather than raising, since this never needs to be exact, only ordered.
Source code in src/mujoco_mojo/utils/changelog.py
parse_changelog
¶
parse_changelog(text: str) -> list[ChangelogEntry]
Splits text into one ChangelogEntry per ## Version X.Y.Z (date) heading, newest first (the file's own order). A malformed date (this repo's own CHANGELOG.md has one, a typo'd 5-digit year) does not fail the entry - it becomes date=None, with the version and bullets unaffected.
Source code in src/mujoco_mojo/utils/changelog.py
fetch_and_parse_changelog
¶
fetch_and_parse_changelog(
*, force_refresh: bool = False
) -> list[ChangelogEntry]
Fetches CHANGELOG_URL and parses it, reusing a successful result for _CACHE_TTL_SECONDS.
Synchronous by design, so it and everything it calls stay plain-function testable with no async test infra (this repo has none); call it from an async endpoint via starlette.concurrency.run_in_threadpool.
Raises on any failure - a network error, a timeout, a non-200 response, or a 200 that parses to zero entries (a captive-portal page would otherwise look like an empty changelog instead of a failure). A failure is not cached, so the next call retries.
Source code in src/mujoco_mojo/utils/changelog.py
read_last_seen_version
¶
read_last_seen_version() -> str | None
The version last written by write_last_seen_version, or None if the user has never acknowledged one (a first-ever launch of this feature, or a fresh ~/.mujoco-mojo).
Source code in src/mujoco_mojo/utils/changelog.py
write_last_seen_version
¶
write_last_seen_version(version: str) -> None
Persists version as acknowledged, so it (and everything older) stops being flagged is_new and stops triggering the modal's auto-open, on this and every future launch, until a genuinely newer version appears.