Skip to content

perf: cut redundant work from the steady cross-session rebuild (#90) #38

perf: cut redundant work from the steady cross-session rebuild (#90)

perf: cut redundant work from the steady cross-session rebuild (#90) #38

name: Install checks
# Installs the backend on clean macOS, Linux, and Windows hosts, checks the
# running service, then uninstalls; on Linux it also runs a real update. Tray
# companions need a desktop session that hosted runners do not reliably
# provide, so they are not covered here.
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: read
# Superseded pull-request runs are cancelled. Every other run gets its own
# group (keyed by commit), so each commit on main is checked to completion.
concurrency:
group: install-checks-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
linux:
name: Linux install
runs-on: ubuntu-latest
timeout-minutes: 20
env:
TOKEN_METER_READINESS_TIMEOUT_SECONDS: "600"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
# Hosted runners have no logged-in user session; lingering starts the
# systemd user manager the installer expects from a desktop login.
- name: Start a systemd user session
run: |
sudo loginctl enable-linger "$USER"
runtime_dir="/run/user/$(id -u)"
export XDG_RUNTIME_DIR="$runtime_dir"
export DBUS_SESSION_BUS_ADDRESS="unix:path=$runtime_dir/bus"
for _ in $(seq 1 30); do
systemctl --user show-environment >/dev/null 2>&1 && break
sleep 1
done
systemctl --user show-environment >/dev/null
echo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" >> "$GITHUB_ENV"
echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" >> "$GITHUB_ENV"
- name: Add a sample session
run: python3 .github/scripts/write_sample_trace.py
- name: Install
run: ./scripts/install --backend-only
- name: Check the installation
run: |
runtime="${XDG_DATA_HOME:-$HOME/.local/share}/token-meter/runtime"
systemctl --user is-active --quiet token-meter-server.service
test "$(cat "$runtime/INSTALL_MODE")" = "backend-only"
# The installer waits for state_ready; the session inventory can
# finish a moment later, so allow it up to a minute.
python3 - <<'PY'
import json, time, urllib.request
deadline = time.monotonic() + 60
while True:
with urllib.request.urlopen("http://127.0.0.1:8722/health", timeout=10) as response:
health = json.load(response)
if health.get("inventory_ready") or time.monotonic() > deadline:
break
time.sleep(1)
assert health.get("ok") and health.get("state_ready"), health
assert health.get("inventory_ready"), health
assert (health.get("sources") or 0) >= 1, health
assert not health.get("runtime_adapter_failures"), health
print("health ok:", health["sources"], "session(s) indexed")
PY
curl -fsS --max-time 10 http://127.0.0.1:8722/menubar | python3 -c 'import json, sys; json.load(sys.stdin); print("menubar ok")'
- name: Uninstall
run: |
runtime="${XDG_DATA_HOME:-$HOME/.local/share}/token-meter/runtime"
"$runtime/scripts/uninstall-systemd-user"
if systemctl --user is-active --quiet token-meter-server.service; then
echo "token-meter-server.service is still running" >&2
exit 1
fi
if curl -fsS --max-time 3 http://127.0.0.1:8722/health >/dev/null 2>&1; then
echo "the server still answers after uninstall" >&2
exit 1
fi
echo "uninstall ok"
- name: Show server logs
if: failure()
run: journalctl --user -u token-meter-server.service -n 200 --no-pager || true
# Installs from a local clone whose origin is a local bare remote, publishes a
# newer commit there, then drives Install through the dashboard API. The
# installer restarts the server mid-update, so this proves the helper
# outlives that restart and the server comes back on the new revision.
linux-update:
name: Linux update
runs-on: ubuntu-latest
timeout-minutes: 25
env:
TOKEN_METER_READINESS_TIMEOUT_SECONDS: "600"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
- name: Start a systemd user session
run: |
sudo loginctl enable-linger "$USER"
runtime_dir="/run/user/$(id -u)"
export XDG_RUNTIME_DIR="$runtime_dir"
export DBUS_SESSION_BUS_ADDRESS="unix:path=$runtime_dir/bus"
for _ in $(seq 1 30); do
systemctl --user show-environment >/dev/null 2>&1 && break
sleep 1
done
systemctl --user show-environment >/dev/null
echo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" >> "$GITHUB_ENV"
echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" >> "$GITHUB_ENV"
- name: Publish this revision to a local remote
run: |
remote="$RUNNER_TEMP/update-remote.git"
source="$RUNNER_TEMP/update-source"
git init --quiet --bare --initial-branch=main "$remote"
git push --quiet "$remote" HEAD:refs/heads/main
git clone --quiet "$remote" "$source"
echo "UPDATE_SOURCE=$source" >> "$GITHUB_ENV"
- name: Add a sample session and turn off automatic installs
run: |
python3 .github/scripts/write_sample_trace.py
mkdir -p "$HOME/.token-meter"
printf '%s\n' '{"updates": {"enabled": true, "auto_install": false}}' \
> "$HOME/.token-meter/settings.json"
- name: Install
run: |
cd "$UPDATE_SOURCE"
./scripts/install --backend-only
systemctl --user show -p MainPID --value token-meter-server.service \
> "$RUNNER_TEMP/server-pid-before"
- name: Publish a newer revision
run: |
cd "$UPDATE_SOURCE"
git -c user.name="Token Meter CI" -c user.email="ci@token-meter.invalid" \
commit --quiet --allow-empty -m "ci: update bump"
git push --quiet origin main
echo "UPDATE_TARGET=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- name: Check for and install the update
run: |
python3 - <<'PY'
import json, os, time, urllib.request
base = "http://127.0.0.1:8722"
target = os.environ["UPDATE_TARGET"]
def get(path):
with urllib.request.urlopen(base + path, timeout=10) as response:
return json.load(response)
def post(path, token):
request = urllib.request.Request(
base + path, data=b"{}", method="POST",
headers={"Content-Type": "application/json", "X-Token-Meter-Action": token},
)
with urllib.request.urlopen(request, timeout=10) as response:
return response.status, json.load(response)
token = get("/updates/status")["actions"]["token"]
deadline = time.monotonic() + 180
# A check already running from server startup ignores a new request,
# so keep asking until one sees the published revision.
while True:
status = get("/updates/status")
if status.get("can_update") and target.startswith(status.get("latest_revision") or "-"):
break
assert time.monotonic() < deadline, status
if not status.get("checking"):
post("/updates/check", token)
time.sleep(2)
code, result = post("/updates/install", token)
assert code == 202 and result.get("ok"), (code, result)
print("update started for", target[:12])
PY
- name: Wait for the update to finish
run: |
status_file="$HOME/.token-meter/update-status.json"
deadline=$((SECONDS + 720))
while (( SECONDS < deadline )); do
phase="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("phase",""))' "$status_file" 2>/dev/null || true)"
case "$phase" in
complete) echo "update complete"; break ;;
failed) cat "$status_file"; exit 1 ;;
esac
sleep 2
done
test "$phase" = complete || { echo "update stuck in phase: $phase" >&2; cat "$status_file"; exit 1; }
- name: Check the updated installation
run: |
runtime="${XDG_DATA_HOME:-$HOME/.local/share}/token-meter/runtime"
installed="$(cat "$runtime/INSTALLED_REVISION")"
case "$UPDATE_TARGET" in
"$installed"*) echo "installed revision ok: $installed" ;;
*) echo "installed $installed, expected $UPDATE_TARGET" >&2; exit 1 ;;
esac
systemctl --user is-active --quiet token-meter-server.service
before="$(cat "$RUNNER_TEMP/server-pid-before")"
after="$(systemctl --user show -p MainPID --value token-meter-server.service)"
test "$before" != "$after" || { echo "the server was not restarted" >&2; exit 1; }
curl -fsS --max-time 10 http://127.0.0.1:8722/health | python3 -c 'import json, sys; h = json.load(sys.stdin); assert h.get("ok"), h; print("health ok")'
- name: Show update and server logs
if: failure()
run: |
cat "$HOME/.token-meter/update-status.json" || true
journalctl --user -u token-meter-update.service -n 200 --no-pager || true
journalctl --user -u token-meter-server.service -n 200 --no-pager || true
macos:
name: macOS install
runs-on: macos-latest
timeout-minutes: 20
env:
TOKEN_METER_READINESS_TIMEOUT_SECONDS: "600"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
- name: Add a sample session
run: python3 .github/scripts/write_sample_trace.py
- name: Install
run: ./scripts/install --backend-only
- name: Check the installation
run: |
runtime="$HOME/Library/Application Support/Token Meter/runtime"
launchctl print "gui/$UID/com.token-meter.server" >/dev/null
test "$(cat "$runtime/INSTALL_MODE")" = "backend-only"
python3 - <<'PY'
import json, time, urllib.request
deadline = time.monotonic() + 60
while True:
with urllib.request.urlopen("http://127.0.0.1:8722/health", timeout=10) as response:
health = json.load(response)
if health.get("inventory_ready") or time.monotonic() > deadline:
break
time.sleep(1)
assert health.get("ok") and health.get("state_ready"), health
assert health.get("inventory_ready"), health
assert (health.get("sources") or 0) >= 1, health
assert not health.get("runtime_adapter_failures"), health
print("health ok:", health["sources"], "session(s) indexed")
PY
curl -fsS --max-time 10 http://127.0.0.1:8722/menubar | python3 -c 'import json, sys; json.load(sys.stdin); print("menubar ok")'
- name: Uninstall
run: |
runtime="$HOME/Library/Application Support/Token Meter/runtime"
"$runtime/scripts/uninstall-launch-agent"
if launchctl print "gui/$UID/com.token-meter.server" >/dev/null 2>&1; then
echo "com.token-meter.server is still loaded" >&2
exit 1
fi
for _ in $(seq 1 10); do
curl -fsS --max-time 3 http://127.0.0.1:8722/health >/dev/null 2>&1 || { echo "uninstall ok"; exit 0; }
sleep 1
done
echo "the server still answers after uninstall" >&2
exit 1
- name: Show server logs
if: failure()
run: |
runtime="$HOME/Library/Application Support/Token Meter/runtime"
for name in meter.err.log meter.log; do
[[ -f "$runtime/$name" ]] && { echo "== $name"; tail -n 200 "$runtime/$name"; }
done
true
windows:
name: Windows install
runs-on: windows-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: Add a sample session
run: python .github/scripts/write_sample_trace.py
- name: Install
shell: powershell
run: |
& powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass `
-File scripts\install-windows.ps1 -BackendOnly -ReadinessTimeoutSeconds 600
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Check the installation
shell: powershell
run: |
$Runtime = Join-Path $env:LOCALAPPDATA "Token Meter\runtime"
# The session inventory can finish a moment after state_ready.
$Deadline = (Get-Date).AddSeconds(60)
do {
$Health = Invoke-RestMethod -Uri "http://127.0.0.1:8722/health" -TimeoutSec 10
if ($Health.inventory_ready) { break }
Start-Sleep -Seconds 1
} while ((Get-Date) -lt $Deadline)
if (-not $Health.ok -or -not $Health.state_ready -or -not $Health.inventory_ready) { throw "server is not ready: $($Health | ConvertTo-Json -Compress)" }
if ([int]$Health.sources -lt 1) { throw "no sessions were indexed: $($Health | ConvertTo-Json -Compress)" }
if (@($Health.runtime_adapter_failures).Count -gt 0) { throw "runtime adapters failed: $($Health | ConvertTo-Json -Compress)" }
Write-Host "health ok: $($Health.sources) session(s) indexed"
$null = Invoke-RestMethod -Uri "http://127.0.0.1:8722/menubar" -TimeoutSec 10
Write-Host "menubar ok"
$Mode = (Get-Content -LiteralPath (Join-Path $Runtime "INSTALL_MODE") -Raw).Trim()
if ($Mode -ne "backend-only") { throw "unexpected install mode: $Mode" }
$RunKey = "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run"
if (-not (Get-ItemProperty -LiteralPath $RunKey -Name "Token Meter" -ErrorAction SilentlyContinue)) {
throw "the login startup entry is missing"
}
Write-Host "startup entry ok"
- name: Uninstall
shell: powershell
run: |
$Runtime = Join-Path $env:LOCALAPPDATA "Token Meter\runtime"
& powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Runtime "scripts\uninstall-windows.ps1")
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$RunKey = "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run"
if (Get-ItemProperty -LiteralPath $RunKey -Name "Token Meter" -ErrorAction SilentlyContinue) {
throw "the login startup entry is still present"
}
$StillServing = $true
try { $null = Invoke-RestMethod -Uri "http://127.0.0.1:8722/health" -TimeoutSec 3 } catch { $StillServing = $false }
if ($StillServing) { throw "the server still answers after uninstall" }
Write-Host "uninstall ok"
- name: Show server logs
if: failure()
shell: powershell
run: |
$Runtime = Join-Path $env:LOCALAPPDATA "Token Meter\runtime"
foreach ($Name in @("meter.err.log", "meter.log")) {
$Path = Join-Path $Runtime $Name
if (Test-Path -LiteralPath $Path) { Write-Host "== $Name"; Get-Content -LiteralPath $Path -Tail 200 }
}