Files
Frametop--frametop/recorder/common.py
T
DeeJanuzandClaude Opus 5.5 c51aa53902 Recorders: a release records its version, not "unknown"
A release's folder has no .git, so git describe found nothing and every
Hand Recorder session and export from a release said "ft-handrec unknown",
and the live tracker's "frametop" was empty. recorder/common.tree_version
reads the release's .frametop-release (VERSION, COMMIT) when there's no
checkout: "ft-handrec v0.3.0-exp.12 release 789bb44". A checkout keeps its
git describe; a checkout's own BOX-only .frametop-release is ignored. The
eye recorder uses the same helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:59:19 -06:00

115 lines
4.2 KiB
Python

"""What the recorders share that isn't about any one recorder: clocks, the dev container and the
host, small file and text helpers, and the versions a session records.
Standard library only: the recorders run it in the dev container and, standalone, on the host."""
import glob
import json
import os
import shutil
import subprocess
import time
def mono_ns():
return time.clock_gettime_ns(time.CLOCK_MONOTONIC)
def clock_sample():
"""[CLOCK_MONOTONIC ns, CLOCK_MONOTONIC_RAW - CLOCK_MONOTONIC ns], as ft-hands' raw_minus_mono_ns().
sets.bin's capture_ns is on the RAW clock and everything else on MONOTONIC; the two drift
apart with NTP's corrections (0.8 s apart and ~10 ppm on 2026-10-03), so take.json keeps
samples to turn capture_ns into MONOTONIC: capture_ns - offset, interpolated by time."""
a = time.clock_gettime_ns(time.CLOCK_MONOTONIC)
r = time.clock_gettime_ns(time.CLOCK_MONOTONIC_RAW)
b = time.clock_gettime_ns(time.CLOCK_MONOTONIC)
mid = (a + b) // 2
return [mid, r - mid]
def in_container():
return os.path.exists("/run/.containerenv") or os.path.exists("/.dockerenv")
def host_command(*cmd):
"""argv to run a command on the SteamOS host from the dev container (as in
display-settings: distrobox-host-exec needs the user's real session bus). It runs from the
home folder: host-spawn starts the command in the caller's folder, and a container-only
one such as /run/host/tmp doesn't exist on the host (every command then exits 127)."""
exe = shutil.which("distrobox-host-exec")
if not in_container() or not exe:
return list(cmd)
return ["env", "-C", os.path.expanduser("~"), "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%d/bus" % os.getuid(),
exe] + list(cmd)
def host_path(path):
"""A host file, from the container (/run/host) or the host itself."""
for p in ("/run/host" + path, path):
if os.path.exists(p):
return p
return None
def write_json(path, data):
tmp = path + ".tmp"
with open(tmp, "w") as f:
json.dump(data, f, indent=1)
f.write("\n")
os.replace(tmp, path)
def clean_text(s):
"""Text for a panel command: one line; "|" is the panel's line break, so callers use it."""
return " ".join(str(s).replace("\r", " ").replace("\n", " ").split())
def os_version():
path = "/run/host/etc/os-release" if in_container() else "/etc/os-release"
try:
with open(path) as f:
for line in f:
if line.startswith("VERSION_ID="):
return line.split("=", 1)[1].strip().strip('"')
except OSError:
pass
return ""
def tree_version(tree):
"""A Frametop tree's version, for what a session records: a checkout's git describe, else a
release's version and commit from its .frametop-release (pack/install-release.sh writes it: a
release's folder has no .git). "" when neither says."""
if os.path.exists(os.path.join(tree, ".git")):
try:
return subprocess.run(["git", "-C", tree, "describe", "--always", "--dirty", "--tags"],
capture_output=True, text=True, timeout=10).stdout.strip()
except (OSError, subprocess.SubprocessError):
return ""
info = {}
try:
with open(os.path.join(tree, ".frametop-release")) as f:
for line in f:
key, eq, value = line.strip().partition("=")
if eq:
info[key] = value
except OSError:
return ""
if not info.get("VERSION"):
return "" # a checkout's own .frametop-release names only its container (BOX=)
return ("v%s release %s" % (info["VERSION"], info.get("COMMIT", "")[:7])).strip()
def steamvr_version():
for db in ("/usr/lib/holo/pacmandb", "/var/lib/pacman"): # SteamOS keeps it in the image
found = sorted(glob.glob(("/run/host" if in_container() else "") + db + "/local/deckard-steamvr-rel-*"))
if found:
return os.path.basename(found[-1])[len("deckard-steamvr-rel-"):]
p = host_path("/opt/steamvr/bin/version.txt")
if p:
try:
with open(p) as f:
return "build " + f.read().strip()
except OSError:
pass
return ""