data/plugins/p4n2gpx/ (outside git, see .gitignore) now holds a
plugin.json + run.rb wrapper around ../p4n2gpx/ruby/p4n2gpx.rb, so it
runs through the same --south/--west/--north/--east-in,
GeoJSON-on-stdout-out contract as the built-in overpass-pois plugin.
run.rb converts the requested bbox to the center+radius p4n2gpx.rb
itself expects, then reuses lib/maptool/gpx.rb to turn its .gpx output
into GeoJSON.
p4n2gpx.rb needs faraday-cookie_jar, which wasn't in the Gemfile yet
(faraday itself already was, transitively via oauth2) — added and
locked. Also wires P4N_USER/P4N_PASSWORD through docker-compose.yaml
and the deploy workflow's "Write .env" step, the same optional-secret
pattern as LIVETRACK_WEBHOOK_SECRET, since the container has no
browser for p4n2gpx.rb's Chrome-cookie auth fallback.
Sources now sync into /srv/podman/maptool/build/ (wiped and fully
replaced every deploy, nothing in it needs preserving) instead of
directly into /srv/podman/maptool/ itself, where data/ and .env also
live — the old sync step had to specifically skip data/ by name to
avoid wiping it; this way there's nothing to skip.
docker-compose.yaml is symlinked (ln -sf, relative target) from
/srv/podman/maptool/ up to build/ — both for a human to still find it
at the top, and because that symlink's own location, not build/, is
what compose resolves ./data and .env's directory against. No changes
to docker-compose.yaml or the podman-compose@.service systemd unit's
WorkingDirectory needed.
Verified against a full local dry run of the new sync step (copy into
a fake build/, symlink up, confirm pre-existing data/ survives) and
`docker-compose config` against the real docker-compose.yaml through
that symlink — both volume mounts (./data, the kiwix ZIM path)
resolved to the sibling data/ directory as intended, not build/data.
/api/health now reports REVISION alongside status, and the CI wait
step checks for this deploy's own short SHA specifically - a stray
old container that never got torn down (or a build that silently
kept serving stale code) would still 200 on a plain health check but
now won't match.
systemctl restart (Type=simple, no readiness protocol) returns as
soon as podman compose up --build forks, before the build or the
container start have actually finished - a build failure or crash
loop would still show as a green deploy job. Poll /api/health for up
to 5 minutes (comfortably above the ~3.5min worst-case cold rebuild)
and fail the job if it never answers.
podman-compose@.service was missing the --build flag that
docker-compose@.service already has (added on cube.local:
/etc/systemd/system/podman-compose@.service), so removing
maptool:latest before every restart (my own earlier fix, meant to
force a rebuild) also destroyed the build cache along with it - every
deploy was redoing apt-get/bundle install from scratch (~3 min
measured, vs ~20s before). --build lets a plain systemctl restart
rebuild while still reusing unchanged layers, same as docker did, so
the ci.yml deploy step collapses back to one line.
docker.io/library/maptool:latest and maptool:latest resolve to the
same local image (verified on cube.local) - the docker.io/library
prefix is just podman's normalization of the unqualified name, not an
actual registry reference. Short form matches what docker-compose.yaml
itself uses and avoids implying a Docker Hub push/pull that never
happens.
The service's own ExecStart deliberately runs a plain podman compose
up (no --build) since ExecStartPre already tears down the previous
containers/networks/volumes on every start — but that leaves the
built image alone, so the deploy job now removes it directly first
(what forces a fresh build) and drives start/stop through systemctl
rather than calling podman/docker compose by hand. Also switches
/srv/docker/maptool to /srv/podman/maptool per the symlink now in
place, and updates deployment.adoc to match.
bin/gnss_simulator.rb speaks gpsd's own JSON wire protocol directly
on its default port -- GpsdClient connects to it exactly as it would
to real gpsd, no app changes needed, and no gpsd install/daemon
required either (unlike the existing fake-NMEA-source approach in
docs/development.adoc, which still needs gpsd itself in front of it).
Drives a loop around a center point at a driving-ish pace rather than
sitting still, since a fixed position isn't much of a "GNSS position"
to look at.
Verified end to end: started it, pointed a dev server at it, and
confirmed /api/gnss/position reports a moving fix across two polls.
ASSET_VERSION only proves *a* deploy happened recently, not *which*
commit -- not enough to answer "did my push actually deploy, or am I
looking at a stale browser cache" (today's actual answer: neither --
deploy was current, a GeoJSON cache predating a code change was just
never invalidated, since it only checks the source .gpx's mtime).
The CI deploy job now writes REVISION (short SHA) before dropping
.git; app.rb reads it at boot, falling back to "dev" locally/in CI's
own check job where the file never exists.
`bundle exec rake test` (minitest, a default gem but pinned in
Gemfile for reproducibility) now covers what ad-hoc scripts kept
re-verifying by hand during development: Gpx/Fit/TrackStats parsing,
GpxStore/LiveTrackStore file/TTL logic, LiveTrackClient's payload
parsing, and app.rb's routes end to end via Rack::MockRequest.
MAPTOOL_DATA_DIR (new, app.rb) lets the suite point GPX_STORE/
LIVETRACK_STORE at a throwaway directory instead of real data/ —
without it every run would leave uploaded test files behind. Each
lib file's old `if __FILE__ == $0` self-check is now redundant with
real tests covering the same ground, so removed rather than kept as
a second copy of the same assertions. Wired into ci.yml so it isn't
just a local convenience.
A Gmail-watching Apps Script (documented in docs/deployment.adoc) posts
the session ID from each LiveTrack email to a new webhook, which stores
it in data/livetrack.json; a new sidebar panel lists what's been
received.
Runs renovate/renovate against this Gitea instance on the native
deploy runner, opening PRs for outdated Bundler dependencies instead
of relying on manually noticing (as happened with puma/mustermann
earlier).
Upload/delete/move/create-folder now require logging in via Gitea
OAuth2 (only MAPTOOL_ALLOWED_GITEA_USER is granted write access);
reading stays public. Deploy secrets (session secret, OAuth2 client
credentials, allowed user) are written to .env from Gitea Actions
repo secrets on every deploy instead of living on the server by hand.
Also switch the container's bind address to "[::]" (dual-stack) so
the app is reachable over IPv6, not just IPv4.
The Docker-based runner's own container is too minimal for host-mode
jobs (no rsync, docker CLI, or Node.js) and has no reliable path to
the host filesystem or systemctl otherwise. Register a separate,
native runner (label deploy:host) just for deployment, and point the
deploy job at it.
Host-mode jobs run directly in the runner container instead of a
spawned per-job container, so JS actions like actions/checkout@v4
have no Node.js runtime to execute with. Replace it with a plain git
fetch/checkout in the deploy job.
Containerize the app (Dockerfile, docker-compose.yaml) and add a CI
deploy job that syncs the checkout to /srv/docker/maptool and runs
docker compose directly, using the runner's new "host" execution
label to reach the host's Docker daemon and filesystem.
Omitting ruby-version entirely triggers its actual auto-detection from
.ruby-version — "file" was a mistaken guess and failed with "Unknown
engine file".
Ruby syntax check, a require_relative "app" smoke load, and JS syntax
check on every push/PR. No test suite exists yet, so these are cheap
syntax/load checks rather than behavioral tests.