testsAndMisc/endurain
Krzysztof kuhy Rudnicki db243fe02e
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
Pre-commit checks / pre-commit (push) Has been cancelled
Python tests / test (push) Has been cancelled
Shell tests / shell-tests (push) Has been cancelled
Shell tests / arch-tests (push) Has been cancelled
Cache prettier's verdict so a push stops paying Node startup
A push took ~13s. prettier is a pre-push hook precisely because Node's
startup dominates its runtime, and pre-commit chunks a large file list into
several invocations -- a 106-file push paid ten cold Node starts. ci-mirror,
the hook that looks expensive, was already 0.02s once its green-tree cache
hit.

A file whose content, prettier version and prettier config are all unchanged
since its last clean check cannot have become unformatted, so the verdict is
now cached and the file never reaches Node. Only clean verdicts are recorded,
so a formatting regression is always re-checked; the key includes
`prettier --version` and every config file, so an upgrade or a config edit
invalidates the cache wholesale. The systemd-run memory cap is unchanged.

Measured on the same push range: 13.0s -> 3.3s total, of which ~0.6s is
hooks and the rest is git's own network negotiation. On a 40-file list the
runner itself goes 0.652s -> 0.057s, starting no prettier process at all.

endurain/README.md and copilot-instructions.md are reflowed: the ~
reorganisation lengthened paths inside them, which changed prettier's
markdown table alignment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
2026-09-11 21:03:57 +02:00
..
scripts Re-point paths for the ~ reorganisation (home-tidy) 2026-09-11 20:50:19 +02:00
systemd Re-point paths for the ~ reorganisation (home-tidy) 2026-09-11 20:50:19 +02:00
docker-compose.yml feat(endurain): self-hosted run viewer fed by RunnerUp WebDAV exports 2026-08-23 13:09:18 +02:00
README.md Cache prettier's verdict so a push stops paying Node startup 2026-09-11 21:03:57 +02:00

Endurain + RunnerUp WebDAV sync

Self-hosted fitness tracker (Endurain) fed by RunnerUp exports from the phone.

Endurain is a viewer. screen-locker's workout gate still reads the phone over adb and is untouched by anything here — a WebDAV drop folder is trivially spoofable by anything that can write to ~/data/cloud, so it must never become an unlock authority.

Paths

What Where
Public URL https://endurain.kuhy.duckdns.org
Local URL http://127.0.0.1:8085 (loopback only)
WebDAV inbox https://kuhy-cloud.duckdns.org/RunnerUp → ~/data/cloud/RunnerUp/
Runtime data /var/opt/endurain/
Ledger ~/.local/state/endurain-import/ledger.json
Importer code ../python_pkg/endurain_import/ (repo requires Python under python_pkg/)
Secrets .env, .api_key (both gitignored, mode 600)

Flow

phone --(WebDAV, manual tap)--> dufs ~/data/cloud/RunnerUp/ --\
                                                          >-- importer --> Endurain
phone --(adb pull, fallback)--> ~/data/cloud/RunnerUp/ -------/

A systemd user timer (endurain-import.timer) runs every 15 min.

Commands

./scripts/setup_endurain.sh          # provision / restart the stack
./scripts/publish_endurain.sh        # install the Caddy site (gated)
systemctl --user start endurain-import.service   # import now
journalctl --user -u endurain-import.service -n 30
cd .. && .venv/bin/python -m pytest python_pkg/endurain_import/tests/ -q  # 52 tests

Things that will bite you

  • RunnerUp has no background sync. No WorkManager, JobScheduler or AlarmManager anywhere in its source. Every upload is a manual tap on the save/upload button after a run. This is not configurable.
  • The WebDAV URL must not end in a slash. RunnerUp builds the target as url + fileBase + fileExt where fileBase already starts with /.
  • MKCOL is not supported by dufs. RunnerUp PROPFINDs the directory and only issues MKCOL on 404, so ~/data/cloud/RunnerUp/ must exist on disk or adding the account fails with an opaque generic error (upstream #1147/#1172).
  • HTTPS is mandatory. RunnerUp targets targetSdk 36 with no usesCleartextTraffic, so Android blocks plain HTTP. Self-signed certs are broken upstream (stock OkHttpClient, system trust store only) — this is why it rides on the existing Let's Encrypt cert.
  • Endurain does not deduplicate. Re-uploading a file creates a second activity. All dedupe is the importer's job, on two axes: the content hash, and an activity key that collapses the .gpx/.tcx pair RunnerUp writes for a single run (different bytes, same run).
  • X-API-Key, not X-Client-Type. Endurain's own OpenAPI schema declares the APIKeyHeader scheme with the wrong name; verified against the server.
  • postgres:18 moved PGDATA to /var/lib/postgresql/18/docker. Upstream's compose example still mounts the pre-18 path, which fails as an unexplained "container is unhealthy".
  • The admin account is seeded admin/admin by an Alembic migration with no env override. publish_endurain.sh refuses to expose the service until that password is changed.