-
Notifications
You must be signed in to change notification settings - Fork 35
Support multiple GTFS-RT feeds #130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
db58f26
dc11be3
b17a0da
eb5ee5f
478592e
3a709cd
822bbbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,24 @@ on: | |
| pull_request: | ||
|
|
||
| jobs: | ||
| renderer: | ||
| name: Template renderer tests | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Go | ||
| uses: actions/setup-go@v5 | ||
| with: | ||
|
Comment on lines
+16
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find unpinned uses (not @<40-hex-sha>) and checkout steps missing persist-credentials.
rg -n --iglob '.github/workflows/*.y*ml' 'uses:\s*[^[:space:]]+@([^[:space:]]+)$'
rg -n --iglob '.github/workflows/*.y*ml' 'uses:\s*actions/checkout@'
rg -n --iglob '.github/workflows/*.y*ml' 'persist-credentials:\s*false'Repository: OneBusAway/docker Length of output: 508 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Workflow files =="
ls -la .github/workflows || true
fd -e yml -e yaml .github/workflows -a || true
echo
echo "== test.yaml (relevant section) =="
if [ -f .github/workflows/test.yaml ]; then
nl -ba .github/workflows/test.yaml | sed -n '1,120p'
else
echo "MISSING: .github/workflows/test.yaml"
# Try to find similarly named file(s)
fd -a "test.*y*ml" .github/workflows || true
fi
echo
echo "== Search for checkout/setup-go/persist-credentials =="
# Use a broad search path that will actually match existing files
rg -n --hidden --glob '.github/workflows/*.{yml,yaml}' \
'uses:\s*actions/(checkout|setup-go)@' .github/workflows || true
rg -n --hidden --glob '.github/workflows/*.{yml,yaml}' \
'persist-credentials:\s*false' .github/workflows || true
rg -n --hidden --glob '.github/workflows/*.{yml,yaml}' \
'uses:\s*[^[:space:]]+@v[0-9]+' .github/workflows || trueRepository: OneBusAway/docker Length of output: 785 🏁 Script executed: #!/bin/bash
# If .github/workflows doesn't exist, list repository top-level quickly (non-destructive)
ls -la .github || true
ls -la . || trueRepository: OneBusAway/docker Length of output: 1680 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== test.yaml lines 1-80 =="
sed -n '1,80p' .github/workflows/test.yaml | nl -ba 2>/dev/null || sed -n '1,80p' .github/workflows/test.yaml
echo
echo "== test.yaml lines 10-40 (focus) =="
sed -n '10,40p' .github/workflows/test.yaml
echo
echo "== docker.yaml lines 1-120 =="
sed -n '1,120p' .github/workflows/docker.yaml
echo
echo "== rg: actions/checkout and persist-credentials =="
rg -n '.github/workflows/.*' --no-heading 'uses:\s*actions/checkout@' .github/workflows/*.yml .github/workflows/*.yaml 2>/dev/null || true
rg -n 'persist-credentials:\s*false' .github/workflows/*.yml .github/workflows/*.yaml 2>/dev/null || true
echo
echo "== rg: actions/setup-go =="
rg -n 'uses:\s*actions/setup-go@' .github/workflows/*.yml .github/workflows/*.yaml 2>/dev/null || true
echo
echo "== rg: any uses pinned to full commit SHAs? (40 hex) =="
rg -n 'uses:\s*(actions/checkout|actions/setup-go)@[0-9a-f]{40}' .github/workflows/*.yml .github/workflows/*.yaml 2>/dev/null || trueRepository: OneBusAway/docker Length of output: 6470 🏁 Script executed: #!/bin/bash
# fallback: print with awk line numbers if nl missing
awk 'NR>=1 && NR<=80 {printf "%5d %s\n", NR, $0}' .github/workflows/test.yaml | head -n 90Repository: OneBusAway/docker Length of output: 2283 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Any persist-credentials settings in workflows =="
rg -n 'persist-credentials:' .github/workflows/*.yml .github/workflows/*.yaml || true
echo
echo "== All actions/checkout@ usages (with nearby context) =="
rg -n --context 2 'uses:\s*actions/checkout@' .github/workflows/*.yml .github/workflows/*.yaml
echo
echo "== All actions/setup-go@ usages (with nearby context) =="
rg -n --context 2 'uses:\s*actions/setup-go@' .github/workflows/*.yml .github/workflows/*.yamlRepository: OneBusAway/docker Length of output: 2082 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== workflow yaml files =="
ls -la .github/workflows
echo
echo "== persist-credentials settings (yaml only) =="
rg -n 'persist-credentials:' .github/workflows/*.yaml || true
echo
echo "== actions/checkout@ usages (yaml only) =="
rg -n 'uses:\s*actions/checkout@' .github/workflows/*.yaml
echo
echo "== actions/setup-go@ usages (yaml only) =="
rg -n 'uses:\s*actions/setup-go@' .github/workflows/*.yaml || true
echo
echo "== checkout steps: show 'with:' block if present nearby (test.yaml) =="
rg -n --context 6 'uses:\s*actions/checkout@' .github/workflows/test.yamlRepository: OneBusAway/docker Length of output: 2248 Harden GitHub Actions: pin action SHAs and disable checkout credential persistence.
Suggested patch - name: Checkout code
- uses: actions/checkout@v4
+ uses: actions/checkout@<full-commit-sha>
+ with:
+ persist-credentials: false
- name: Set up Go
- uses: actions/setup-go@v5
+ uses: actions/setup-go@<full-commit-sha>
with:
go-version: "1.22"🧰 Tools🪛 zizmor (1.25.2)[warning] 14-15: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) [error] 15-15: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) [error] 18-18: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) 🤖 Prompt for AI Agents
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deferring this one. No workflow in this repo pins actions to commit SHAs or sets There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Would you like me to open a follow-up GitHub issue to track the repo-wide CI hardening work (pinning all action SHAs + |
||
| go-version: "1.22" | ||
|
|
||
| - name: Run renderer tests | ||
| working-directory: oba/config/template_renderer | ||
| run: go test ./... | ||
|
|
||
| image: | ||
| name: Build Docker Image | ||
| runs-on: ubuntu-latest | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| # Multiple GTFS-RT Feeds Example | ||
|
|
||
| This example runs a single OneBusAway server that serves **two transit agencies' | ||
| realtime data at once** — King County Metro and Pierce Transit — using the | ||
| `GTFS_RT_FEEDS` environment variable. | ||
|
|
||
| OneBusAway supports many `GtfsRealtimeSource` beans (one per realtime feed). | ||
| `GTFS_RT_FEEDS` exposes that: it takes a JSON array of feed | ||
| objects and renders one bean per feed. It supersedes the legacy single-feed | ||
| variables (`TRIP_UPDATES_URL`, `VEHICLE_POSITIONS_URL`, `ALERTS_URL`, | ||
| `AGENCY_ID`, …), which still work for a single feed when `GTFS_RT_FEEDS` is unset. | ||
|
|
||
| ## What's configured | ||
|
|
||
| | Agency | `agency_id` | Static GTFS | Realtime | | ||
| |---|---|---|---| | ||
| | King County Metro | `1` | consolidated feed (below) | raw GTFS-RT protobufs from `kcm-alerts-realtime-prod` | | ||
| | Pierce Transit | `3` | consolidated feed (below) | relayed through the Puget Sound OneBusAway GTFS-RT endpoints | | ||
|
|
||
| **Static data** comes from the consolidated Puget Sound GTFS feed | ||
| (`https://gtfs.sound.obaweb.org/prod/gtfs_puget_sound_consolidated.zip`), which | ||
| already contains both agencies (and several others). Because it's a single zip, | ||
| the normal single-feed bundle builder handles it without any changes. | ||
|
|
||
| **Realtime data** is configured with two feeds in `GTFS_RT_FEEDS`. Each feed's | ||
| `agencyIds` tells OneBusAway which bundle agency the feed's entities belong to, | ||
| so the two protobuf streams line up with agencies `1` and `3` in the bundle: | ||
|
|
||
| ```json | ||
| [ | ||
| { | ||
| "tripUpdatesUrl": "https://s3.amazonaws.com/kcm-alerts-realtime-prod/tripupdates.pb", | ||
| "vehiclePositionsUrl": "https://s3.amazonaws.com/kcm-alerts-realtime-prod/vehiclepositions.pb", | ||
| "alertsUrl": "https://s3.amazonaws.com/kcm-alerts-realtime-prod/alerts.pb", | ||
| "refreshInterval": "30", | ||
| "agencyIds": ["1"] | ||
| }, | ||
| { | ||
| "tripUpdatesUrl": "https://api.pugetsound.onebusaway.org/api/gtfs_realtime/trip-updates-for-agency/3.pb?key=org.onebusaway.iphone", | ||
| "vehiclePositionsUrl": "https://api.pugetsound.onebusaway.org/api/gtfs_realtime/vehicle-positions-for-agency/3.pb?key=org.onebusaway.iphone", | ||
| "alertsUrl": "https://api.pugetsound.onebusaway.org/api/gtfs_realtime/alerts-for-agency/3.pb?key=org.onebusaway.iphone", | ||
| "refreshInterval": "30", | ||
| "agencyIds": ["3"] | ||
| } | ||
| ] | ||
| ``` | ||
|
|
||
| > **Note:** `oba_app` is built from this repository's `../../oba` directory, not | ||
| > the published `opentransitsoftwarefoundation/onebusaway-api-webapp:2.7.1-latest` | ||
| > image, because `GTFS_RT_FEEDS` support is not in a published release yet. Once a | ||
| > 2.7.1 image that understands `GTFS_RT_FEEDS` is published, an immutable | ||
| > deployment can bake these env vars into a `Dockerfile` that is `FROM` that | ||
| > image instead — see [`../immutable`](../immutable) for that pattern. | ||
|
|
||
| ## Running it | ||
|
|
||
| From this directory: | ||
|
|
||
| ```bash | ||
| docker compose up -d --build | ||
| ``` | ||
|
|
||
| This: | ||
|
|
||
| 1. Runs `oba_bundler` once to build the consolidated bundle into a shared volume | ||
| (this downloads ~38 MB of GTFS and takes a few minutes). | ||
| 2. Starts PostgreSQL. | ||
| 3. Starts `oba_app` after the bundle finishes, configured with both realtime feeds. | ||
|
|
||
| Watch the bundle build and app startup: | ||
|
|
||
| ```bash | ||
| docker compose logs -f oba_bundler # bundle build | ||
| docker compose logs -f oba_app # config render + Tomcat startup | ||
| ``` | ||
|
|
||
| The API is available at <http://localhost:8080> once `oba_app` is up. | ||
|
|
||
| ## Validating | ||
|
|
||
| Run the repository's validation script (from the repo root) against the running server: | ||
|
|
||
| ```bash | ||
| ./bin/validate.sh | ||
| ``` | ||
|
|
||
| Then confirm **realtime** is flowing for **both** agencies. The most direct | ||
| check is live vehicle positions — each count should be non-zero during service | ||
| hours, and it proves each feed's entities mapped onto the right bundle agency: | ||
|
|
||
| ```bash | ||
| # King County Metro (agency 1) — live vehicles from its realtime feed | ||
| curl -s "http://localhost:8080/api/where/vehicles-for-agency/1.json?key=test" | jq '.data.list | length' | ||
|
|
||
| # Pierce Transit (agency 3) — live vehicles from its realtime feed | ||
| curl -s "http://localhost:8080/api/where/vehicles-for-agency/3.json?key=test" | jq '.data.list | length' | ||
| ``` | ||
|
|
||
| To see the realtime status of a specific trip, pull an active trip from a | ||
| vehicle and inspect its status — look for `"predicted": true`: | ||
|
|
||
| ```bash | ||
| TRIP=$(curl -s "http://localhost:8080/api/where/vehicles-for-agency/1.json?key=test" | jq -r '.data.list[0].tripId') | ||
| curl -s "http://localhost:8080/api/where/trip-details/$TRIP.json?key=test" \ | ||
| | jq '.data.entry.status | {predicted, scheduleDeviation, vehicleId, lastUpdateTime}' | ||
| ``` | ||
|
|
||
| ## Tearing down | ||
|
|
||
| ```bash | ||
| docker compose down -v # -v also removes the bundle and database volumes | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # Multiple GTFS-RT feeds example: King County Metro + Pierce Transit. | ||
| # | ||
| # Demonstrates the GTFS_RT_FEEDS env var, which configures one OR many GTFS-RT | ||
| # realtime feeds (one GtfsRealtimeSource bean per feed). Static data comes from | ||
| # the consolidated Puget Sound GTFS feed, which already contains both agencies | ||
| # (agency_id 1 = King County Metro, agency_id 3 = Pierce Transit), so the | ||
| # single-feed bundle builder handles it as-is. | ||
| # | ||
| # NOTE: oba_app is built from this repo's ../../oba directory rather than the | ||
| # published opentransitsoftwarefoundation/onebusaway-api-webapp:2.7.1-latest | ||
| # image, because GTFS_RT_FEEDS support is not in a published release yet. Once a | ||
| # 2.7.1 image that understands GTFS_RT_FEEDS is published, an immutable | ||
| # deployment can instead bake these env vars into a Dockerfile FROM that image | ||
| # (see ../immutable for that pattern). | ||
|
|
||
| services: | ||
| # One-shot job: builds the consolidated bundle into the shared volume, then exits. | ||
| oba_bundler: | ||
| build: | ||
| context: ../../bundler | ||
| volumes: | ||
| - oba-bundle:/bundle | ||
| environment: | ||
| - GTFS_URL=https://gtfs.sound.obaweb.org/prod/gtfs_puget_sound_consolidated.zip | ||
|
|
||
| oba_database_pg: | ||
| image: postgres:16 | ||
| environment: | ||
| POSTGRES_USER: oba_user | ||
| POSTGRES_PASSWORD: oba_password | ||
| POSTGRES_DB: oba_database | ||
| # No host port mapping: oba_app reaches the database over the compose | ||
| # network, so there's no need to expose 5432 (and it avoids colliding with | ||
| # a Postgres already running on the host). | ||
| volumes: | ||
| - pg-data:/var/lib/postgresql/data | ||
| restart: always | ||
|
|
||
| oba_app: | ||
| depends_on: | ||
| oba_database_pg: | ||
| condition: service_started | ||
| # Wait for the bundle build to finish before starting the app. | ||
| oba_bundler: | ||
| condition: service_completed_successfully | ||
| build: | ||
| context: ../../oba | ||
| environment: | ||
| - JDBC_URL=jdbc:postgresql://oba_database_pg:5432/oba_database | ||
| - JDBC_DRIVER=org.postgresql.Driver | ||
| - JDBC_USER=oba_user | ||
| - JDBC_PASSWORD=oba_password | ||
| - TEST_API_KEY=test # For testing only; remove in production. | ||
| - TZ=America/Los_Angeles | ||
| # Two realtime feeds, one per agency. KCM publishes raw GTFS-RT protobufs; | ||
| # Pierce is relayed through the Puget Sound OneBusAway GTFS-RT endpoints. | ||
| # agencyIds maps each feed's entities onto the matching bundle agency. | ||
| - 'GTFS_RT_FEEDS=[{"tripUpdatesUrl":"https://s3.amazonaws.com/kcm-alerts-realtime-prod/tripupdates.pb","vehiclePositionsUrl":"https://s3.amazonaws.com/kcm-alerts-realtime-prod/vehiclepositions.pb","alertsUrl":"https://s3.amazonaws.com/kcm-alerts-realtime-prod/alerts.pb","refreshInterval":"30","agencyIds":["1"]},{"tripUpdatesUrl":"https://api.pugetsound.onebusaway.org/api/gtfs_realtime/trip-updates-for-agency/3.pb?key=org.onebusaway.iphone","vehiclePositionsUrl":"https://api.pugetsound.onebusaway.org/api/gtfs_realtime/vehicle-positions-for-agency/3.pb?key=org.onebusaway.iphone","alertsUrl":"https://api.pugetsound.onebusaway.org/api/gtfs_realtime/alerts-for-agency/3.pb?key=org.onebusaway.iphone","refreshInterval":"30","agencyIds":["3"]}]' | ||
| volumes: | ||
| - oba-bundle:/bundle | ||
| ports: | ||
| - "8080:8080" | ||
| - "1234:1234" # JMX exporter port | ||
|
|
||
| volumes: | ||
| oba-bundle: | ||
| pg-data: |
Uh oh!
There was an error while loading. Please reload this page.