No description
  • Python 99.1%
  • Dockerfile 0.6%
  • Mako 0.2%
  • Makefile 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Max Katz-Christy da0fb12d2f
All checks were successful
Build / build (push) Successful in 1m11s
chore: add gitleaks config
2026-09-30 20:41:50 +02:00
.forgejo/workflows feat: scaffold the geometry-car repo 2026-09-22 15:03:14 +02:00
src/geometry_car feat(feeds): partial state, catalog-first names and search.json 2026-09-30 02:51:30 +02:00
tests feat(feeds): partial state, catalog-first names and search.json 2026-09-30 02:51:30 +02:00
.dockerignore build: ship alembic.ini in the image 2026-09-22 22:57:37 +02:00
.env.example feat(heartbeat): push a Gatus heartbeat after each successful publish 2026-09-24 11:06:02 +02:00
.gitignore feat: scaffold the geometry-car repo 2026-09-22 15:03:14 +02:00
.gitleaks.toml chore: add gitleaks config 2026-09-30 20:41:50 +02:00
.pre-commit-config.yaml feat: scaffold the geometry-car repo 2026-09-22 15:03:14 +02:00
alembic.ini feat: the catalog pipeline 2026-09-22 22:43:51 +02:00
CLAUDE.md feat(feeds): partial state, catalog-first names and search.json 2026-09-30 02:51:30 +02:00
Dockerfile build: ship alembic.ini in the image 2026-09-22 22:57:37 +02:00
LICENSE.txt feat: scaffold the geometry-car repo 2026-09-22 15:03:14 +02:00
Makefile feat: scaffold the geometry-car repo 2026-09-22 15:03:14 +02:00
pyproject.toml build(deps): install railroad-club from GitHub 2026-09-30 19:27:19 +02:00
README.md feat(feeds): partial state, catalog-first names and search.json 2026-09-30 02:51:30 +02:00
uv.lock build(deps): install railroad-club from GitHub 2026-09-30 19:27:19 +02:00

Geometry Car

Dagster pipeline that inventories the world of GTFS. It ingests the Transitland Atlas DMFR corpus and the Mobility Database daily, checks whether each feed endpoint still answers, keeps the history in Postgres, and publishes public JSON artifacts to object storage at data.gtfs.zone.

Overview

Every consumer used to ship its own copy of a build-time feed catalog with no liveness signal. This replaces that: one pipeline produces sources.json, feeds.json, search.json, status.json, summary.json and a manifest.json consumers poll to decide whether to refetch, plus deduplicated dated snapshots.

Reachability is checked with a HEAD request, falling back to a ranged GET when the origin rejects HEAD. No feed bodies are downloaded or stored.

globe-of-contents (list.gtfs.zone) is the frontend for these artifacts.

Running Locally

make env             # creates .env from .env.example
uv sync
uv run dagster dev

Fill MOBILITY_DB_REFRESH_TOKEN in .env, and point TRANSITLAND_ATLAS_PATH at a sibling checkout of transitland/transitland-atlas so runs read the DMFR corpus from disk rather than the GitHub API.

Without DATABASE_URL the run still works and still publishes; it just keeps no history. Without MOBILITY_DB_REFRESH_TOKEN it runs on Transitland and cafe-car's catalog alone. Neither is an error, so a first local run needs neither.

Trigger the first run from the Dagster UI rather than waiting for the schedule.

Migrations

The history tables are this repo's, with this repo's Alembic. Dagster's own run and event storage lives in the same database and creates itself, and env.py filters autogenerate down to this repo's tables so a revision here cannot propose dropping any of Dagster's. Dagster migrates itself with Alembic too, so this repo's revision is recorded in alembic_version_geometry_car rather than the default alembic_version, which is Dagster's.

DATABASE_URL=postgresql://... uv run alembic upgrade head
DATABASE_URL=postgresql://... uv run alembic revision --autogenerate -m "what changed"

Development

uv sync              # install dependencies
ruff check .         # lint
ruff format .        # format
uv run pytest        # tests
pre-commit install   # install git hooks (run once after clone)

Tests run against no services: respx for the catalog APIs and the endpoint checks, moto for the bucket, SQLite for the history tables.