Skip to content

Support multiple GTFS-RT feeds#130

Merged
aaronbrethorst merged 7 commits into
mainfrom
multiple-gtfs-rt-feeds
May 24, 2026
Merged

Support multiple GTFS-RT feeds#130
aaronbrethorst merged 7 commits into
mainfrom
multiple-gtfs-rt-feeds

Conversation

@aaronbrethorst
Copy link
Copy Markdown
Member

@aaronbrethorst aaronbrethorst commented May 24, 2026

Summary

Teaches the image to configure many GTFS-RT realtime feeds instead of just one, while remaining fully backward compatible.

  • Template (oba/config/onebusaway-transit-data-federation-webapp-data-sources.xml.hbs): the GTFS-RT block now iterates a FEEDS array ({{#each FEEDS}}) and emits one <bean class="…GtfsRealtimeSource"> per feed, with per-feed tripUpdatesUrl / vehiclePositionsUrl / alertsUrl / refreshInterval / agencyIds / headersMap.
  • oba/bootstrap.sh: builds the FEEDS array — prefers a new GTFS_RT_FEEDS JSON env var, and when it's unset, normalizes the legacy single-feed vars (TRIP_UPDATES_URL, AGENCY_ID_LIST, etc.) into a one-element array. So every already-deployed single-feed Dockerfile keeps building unchanged. Whitespace/empty GTFS_RT_FEEDS falls through to the no-feeds path.
  • CI (.github/workflows/test.yaml): adds a go test ./oba/config/template_renderer/... job (the renderer tests weren't run in CI before).
  • Tests: new Go tests render the real template for multi-feed, empty-feeds, legacy-normalized, and singular-agencyId cases.
  • README: documents GTFS_RT_FEEDS.

Coordination

This is the docker half of a two-repo change (the other half is OBACloud's multi-feed Dockerfile generator). Because 2.7.1-latest is a mutable tag, merge this and cut a release first so 2.7.1-latest understands GTFS_RT_FEEDS before OBACloud starts emitting it. The change is additive, so existing single-feed Dockerfiles keep working before and after.

Test Plan

  • cd oba/config/template_renderer && go test ./... passes (multi-feed, empty, legacy-normalized, singular agencyId)
  • bash -n oba/bootstrap.sh clean; both GTFS_RT_FEEDS and legacy paths smoke-tested through the renderer
  • After merge: cut a 2.7.1-vX.Y.Z release and confirm opentransitsoftwarefoundation/onebusaway-api-webapp:2.7.1-latest is republished

Summary by CodeRabbit

  • New Features

    • Support for multiple GTFS-RT feeds via a new GTFS_RT_FEEDS JSON config, with per‑feed URLs, refresh interval, agency mapping, and optional per‑feed auth; legacy single‑feed vars still normalize/fallback.
  • Documentation

    • README, examples, and design/plan docs added/updated describing multi‑feed configuration and deployment examples.
  • Tests

    • Added renderer tests covering multiple feeds, empty/legacy cases, and agency mapping.
  • Chores

    • CI expanded to run template renderer tests.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 24, 2026

📝 Walkthrough

Walkthrough

This PR adds GTFS_RT_FEEDS (a JSON array) and prefers it in bootstrap, normalizes legacy single-feed env vars into a FEEDS array, updates the Handlebars template to render one GtfsRealtimeSource per feed, adds Go renderer tests, CI coverage, deployment examples, and design/planning docs.

Changes

Multi-Feed GTFS-RT Configuration Support

Layer / File(s) Summary
Configuration and Precedence Documentation
README.md
GTFS_RT_FEEDS documented with expected JSON feed-array shape and per-feed fields; notes it takes precedence over legacy single-feed GTFS-RT env vars.
Feed Configuration Assembly
oba/bootstrap.sh
Builds FEEDS_JSON by preferring GTFS_RT_FEEDS when present, otherwise normalizes TRIP_UPDATES_URL/VEHICLE_POSITIONS_URL/legacy vars into a single-element feed object, or emits []; sets JSON_CONFIG to { "FEEDS": ... }.
Template Rendering for Multi-Feed GTFS-RT
oba/config/onebusaway-transit-data-federation-webapp-data-sources.xml.hbs
Rewrites GTFS-RT bean block to iterate FEEDS, emitting one GtfsRealtimeSource bean per feed with conditional properties: tripUpdatesUrl, vehiclePositionsUrl, alertsUrl, refreshInterval, agencyIds vs agencyId, and per-feed API header map.
Template Rendering Test Suite
oba/config/template_renderer/main_test.go
Adds tests (multiple feeds, no feeds, legacy-normalized single-feed, singular vs plural agencyId) that render the template and assert on generated XML/bean output.
CI Pipeline Configuration
.github/workflows/test.yaml
Adds renderer job that checks out repo, sets up Go 1.22, and runs go test ./... in oba/config/template_renderer.
Deployment examples
deployment-examples/multiple-rt-feeds/*
Adds Docker Compose and README demonstrating running the webapp with two GTFS-RT feeds via GTFS_RT_FEEDS, bundler, DB, app service, and validation steps.
Plans and Specs
docs/superpowers/plans/*, docs/superpowers/specs/*
Adds implementation plan and comprehensive design spec covering contract shape, bootstrap normalization, template rendering, OBACloud model/controller/UI changes, validations, migrations, and tests.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Support multiple GTFS-RT feeds' directly and clearly describes the main change in the changeset: adding support for configuring multiple GTFS-RT realtime feeds.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch multiple-gtfs-rt-feeds

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/test.yaml:
- Around line 10-12: jobs.renderer currently lacks a job-level permissions block
and uses unchecked checkout and tag-pinned actions; add a minimal permissions
section (e.g., permissions: contents: read) to jobs.renderer, change the
actions/checkout step to set persist-credentials: false, and replace tag-only
references (actions/checkout@v4, actions/setup-go@v5) with pinned commit SHAs or
otherwise pin the action versions to specific commits to avoid floating tags.
- Around line 14-19: Replace tag-based GitHub Actions and enable non-persistent
checkout: change uses: actions/checkout@v4 and uses: actions/setup-go@v5 to
pinned commit SHAs for those actions, and add persist-credentials: false to the
checkout step (the actions/checkout usage) to disable credential persistence;
apply the same changes to the other workflow referenced (the actions/checkout
step in the docker workflow). Ensure you update the specific occurrences of
"actions/checkout" and "actions/setup-go" in the workflows so they reference
commit SHAs rather than version tags and add the persist-credentials: false
field to each checkout step.

In `@oba/bootstrap.sh`:
- Around line 40-43: The precedence bug comes from treating
GTFS_RT_FEEDS_TRIMMED == "[]" as unset so legacy vars can override it; change
the logic to first detect whether GTFS_RT_FEEDS_TRIMMED is set at all (use a
"variable is set" test) and, if set, always assign FEEDS_JSON="$GTFS_RT_FEEDS"
(so explicit "[]" is preserved), otherwise fall back to the legacy
TRIP_UPDATES_URL / VEHICLE_POSITIONS_URL branch; update the conditional around
GTFS_RT_FEEDS_TRIMMED and the assignment to FEEDS_JSON (references:
GTFS_RT_FEEDS_TRIMMED and FEEDS_JSON) accordingly so explicit empty lists take
precedence.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7e38627d-0074-474b-8311-07d459541a50

📥 Commits

Reviewing files that changed from the base of the PR and between 18d4636 and eb5ee5f.

📒 Files selected for processing (5)
  • .github/workflows/test.yaml
  • README.md
  • oba/bootstrap.sh
  • oba/config/onebusaway-transit-data-federation-webapp-data-sources.xml.hbs
  • oba/config/template_renderer/main_test.go

Comment thread .github/workflows/test.yaml
Comment on lines +14 to +19
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 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 || true

Repository: 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 . || true

Repository: 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 || true

Repository: 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 90

Repository: 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/*.yaml

Repository: 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.yaml

Repository: OneBusAway/docker

Length of output: 2248


Harden GitHub Actions: pin action SHAs and disable checkout credential persistence.

.github/workflows/test.yaml uses tag-based actions/checkout@v4 and actions/setup-go@v5, and none of the checkout steps set persist-credentials: false (same unpinned checkout pattern exists in .github/workflows/docker.yaml).

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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yaml around lines 14 - 19, Replace tag-based GitHub
Actions and enable non-persistent checkout: change uses: actions/checkout@v4 and
uses: actions/setup-go@v5 to pinned commit SHAs for those actions, and add
persist-credentials: false to the checkout step (the actions/checkout usage) to
disable credential persistence; apply the same changes to the other workflow
referenced (the actions/checkout step in the docker workflow). Ensure you update
the specific occurrences of "actions/checkout" and "actions/setup-go" in the
workflows so they reference commit SHAs rather than version tags and add the
persist-credentials: false field to each checkout step.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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 persist-credentials: falsedocker.yaml has several tag-pinned actions and there is no dependabot config. Applying it to only the new renderer job would create inconsistency without closing the actual exposure, so this is better handled as a separate repo-wide CI-hardening pass (ideally dependabot-managed) rather than scope-creeping this feature PR.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aaronbrethorst, that's a reasonable call — partial hardening of only the new job without addressing the rest of the repo would give a false sense of security without actually reducing the attack surface. A dependabot-managed, repo-wide pass is the right approach.

Would you like me to open a follow-up GitHub issue to track the repo-wide CI hardening work (pinning all action SHAs + persist-credentials: false + adding a Dependabot config for Actions)?

Comment thread oba/bootstrap.sh Outdated
Address CodeRabbit review on PR #130:
- bootstrap.sh: an explicit GTFS_RT_FEEDS='[]' now disables realtime feeds
  instead of silently falling back to the legacy single-feed env vars.
  Existing deployments (legacy vars set, GTFS_RT_FEEDS unset) are unaffected;
  whitespace-only values still fall through. Also corrects the status log to
  read accurately for any feed count (0, 1, or N).
- test.yaml: add least-privilege 'permissions: contents: read' to the
  renderer job.
A runnable deployment example that exercises GTFS_RT_FEEDS with two agencies:
King County Metro (agency 1) and Pierce Transit (agency 3). Static data comes
from the consolidated Puget Sound GTFS feed (single zip, so the existing
single-feed bundle builder handles both agencies). Two GtfsRealtimeSource beans
are configured via GTFS_RT_FEEDS.

oba_app builds from ../../oba rather than the published 2.7.1-latest image,
since GTFS_RT_FEEDS support is not in a published release yet.

Verified locally: bin/validate.sh passes; realtime vehicles and predicted trip
status work for both agencies.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
docs/superpowers/specs/2026-05-23-multi-gtfs-rt-feeds-design.md (1)

9-9: ⚡ Quick win

Avoid machine-local absolute paths in the design spec.

/Users/aaron/repos/... is environment-specific and gets stale quickly; prefer repo-relative naming only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-05-23-multi-gtfs-rt-feeds-design.md` at line 9,
Replace the machine-local absolute path in the spec with a repo-relative
reference so the document is portable; specifically, change occurrences like
`/Users/aaron/repos/onebusaway/docker` to a repo-relative form such as
`onebusaway/docker` (or `./onebusaway/docker`) in the section describing the
change to generate one `GtfsRealtimeSource` per feed driven by `GTFS_RT_FEEDS`,
ensuring the spec references only project-relative locations while keeping
mentions of `GtfsRealtimeSource` and `GTFS_RT_FEEDS` intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-05-23-multi-gtfs-rt-feeds-docker.md`:
- Around line 187-191: The snippet treats explicit GTFS_RT_FEEDS='[]' as falling
through to the legacy branch; change the elif so legacy normalization runs only
when GTFS_RT_FEEDS is unset/empty (not when it's "[]"). Concretely, keep the
first if (check GTFS_RT_FEEDS non-empty and not "[]"), but change the elif to
require GTFS_RT_FEEDS be empty/unset (e.g. use [ -z "$GTFS_RT_FEEDS" ] && ([ -n
"$TRIP_UPDATES_URL" ] || [ -n "$VEHICLE_POSITIONS_URL" ])) so that explicit "[]"
goes to the no-feeds path and only a truly missing GTFS_RT_FEEDS triggers legacy
normalization; update references to GTFS_RT_FEEDS, TRIP_UPDATES_URL,
VEHICLE_POSITIONS_URL and FEEDS_JSON accordingly.

In `@docs/superpowers/specs/2026-05-23-multi-gtfs-rt-feeds-design.md`:
- Around line 84-87: The current shell snippet treats the literal string "[]" as
unset; change the if-condition to test whether GTFS_RT_FEEDS is set at all (not
whether it is non-empty) so any explicit GTFS_RT_FEEDS value — including "[]" —
takes precedence over legacy normalization; update the branch around
GTFS_RT_FEEDS, FEEDS_JSON, and the legacy vars (TRIP_UPDATES_URL,
VEHICLE_POSITIONS_URL) so a set GTFS_RT_FEEDS always wins and prevents falling
back to legacy single-feed normalization.
- Around line 52-68: The fenced code block in the spec is missing a language tag
(MD040); update the opening triple-backtick for that diagram to include a
language identifier such as "text" (e.g., change ``` to ```text) so the linter
recognizes it; make the edit in the fenced block that starts with "OBACloud
accordion editor" in the document so the diagram is fenced as text.

---

Nitpick comments:
In `@docs/superpowers/specs/2026-05-23-multi-gtfs-rt-feeds-design.md`:
- Line 9: Replace the machine-local absolute path in the spec with a
repo-relative reference so the document is portable; specifically, change
occurrences like `/Users/aaron/repos/onebusaway/docker` to a repo-relative form
such as `onebusaway/docker` (or `./onebusaway/docker`) in the section describing
the change to generate one `GtfsRealtimeSource` per feed driven by
`GTFS_RT_FEEDS`, ensuring the spec references only project-relative locations
while keeping mentions of `GtfsRealtimeSource` and `GTFS_RT_FEEDS` intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1284de51-bee7-4d4a-99a9-a83470716021

📥 Commits

Reviewing files that changed from the base of the PR and between eb5ee5f and 822bbbd.

📒 Files selected for processing (6)
  • .github/workflows/test.yaml
  • deployment-examples/multiple-rt-feeds/README.md
  • deployment-examples/multiple-rt-feeds/docker-compose.yaml
  • docs/superpowers/plans/2026-05-23-multi-gtfs-rt-feeds-docker.md
  • docs/superpowers/specs/2026-05-23-multi-gtfs-rt-feeds-design.md
  • oba/bootstrap.sh

Comment on lines +187 to +191
if [ -n "$GTFS_RT_FEEDS" ] && [ "$GTFS_RT_FEEDS" != "[]" ]; then
FEEDS_JSON="$GTFS_RT_FEEDS"
echo "GTFS_RT_FEEDS is set. Rendering multiple GTFS-RT feeds."
elif [ -n "$TRIP_UPDATES_URL" ] || [ -n "$VEHICLE_POSITIONS_URL" ]; then
echo "Legacy single-feed GTFS-RT env vars are set. Normalizing into one feed."
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Explicit GTFS_RT_FEEDS='[]' semantics in this snippet conflict with the stated behavior

This condition allows legacy fallback when GTFS_RT_FEEDS is explicitly [], but the PR summary says [] should disable realtime feeds. Please update this plan snippet so explicit [] routes to the no-feeds path, not legacy normalization.

Suggested doc snippet adjustment
-if [ -n "$GTFS_RT_FEEDS" ] && [ "$GTFS_RT_FEEDS" != "[]" ]; then
+if [ -n "$GTFS_RT_FEEDS" ]; then
+    if [ "$GTFS_RT_FEEDS" = "[]" ]; then
+        FEEDS_JSON="[]"
+        echo "GTFS_RT_FEEDS is explicitly empty. No realtime feeds will be configured."
+    else
+        FEEDS_JSON="$GTFS_RT_FEEDS"
+        echo "GTFS_RT_FEEDS is set. Rendering multiple GTFS-RT feeds."
+    fi
-    FEEDS_JSON="$GTFS_RT_FEEDS"
-    echo "GTFS_RT_FEEDS is set. Rendering multiple GTFS-RT feeds."
 elif [ -n "$TRIP_UPDATES_URL" ] || [ -n "$VEHICLE_POSITIONS_URL" ]; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/plans/2026-05-23-multi-gtfs-rt-feeds-docker.md` around lines
187 - 191, The snippet treats explicit GTFS_RT_FEEDS='[]' as falling through to
the legacy branch; change the elif so legacy normalization runs only when
GTFS_RT_FEEDS is unset/empty (not when it's "[]"). Concretely, keep the first if
(check GTFS_RT_FEEDS non-empty and not "[]"), but change the elif to require
GTFS_RT_FEEDS be empty/unset (e.g. use [ -z "$GTFS_RT_FEEDS" ] && ([ -n
"$TRIP_UPDATES_URL" ] || [ -n "$VEHICLE_POSITIONS_URL" ])) so that explicit "[]"
goes to the no-feeds path and only a truly missing GTFS_RT_FEEDS triggers legacy
normalization; update references to GTFS_RT_FEEDS, TRIP_UPDATES_URL,
VEHICLE_POSITIONS_URL and FEEDS_JSON accordingly.

Comment on lines +52 to +68
```
OBACloud accordion editor
│ (global: base_image, timezone · repeatable feeds)
DockerfileConfig.feeds ── GtfsRtFeed StoreModel array, persisted in existing JSONB column
▼ Dockerfiles::GenerateObaApi
ENV GTFS_RT_FEEDS='[{…},{…}]' ◄── THE CROSS-REPO CONTRACT
│ pushed to OneBusAway/obacloud-dockerfiles → Render builds image
bootstrap.sh ── if $GTFS_RT_FEEDS set → use it; else normalize legacy single-feed vars into a 1-element FEEDS array
│ hands { "FEEDS": [...] } to hbs_renderer (raymond)
data-sources.xml.hbs ── {{#each FEEDS}} → one <bean class="GtfsRealtimeSource"> per feed
OneBusAway runs with N realtime feeds
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language tag to the fenced code block.

This block is missing a fence language (MD040), which will trip markdown linting.

Suggested diff
-```
+```text
 OBACloud accordion editor
   │  (global: base_image, timezone · repeatable feeds)
   ▼
 DockerfileConfig.feeds  ── GtfsRtFeed StoreModel array, persisted in existing JSONB column
   │
   ▼  Dockerfiles::GenerateObaApi
 ENV GTFS_RT_FEEDS='[{…},{…}]'   ◄── THE CROSS-REPO CONTRACT
   │  pushed to OneBusAway/obacloud-dockerfiles → Render builds image
   ▼
 bootstrap.sh   ── if $GTFS_RT_FEEDS set → use it; else normalize legacy single-feed vars into a 1-element FEEDS array
   │  hands { "FEEDS": [...] } to hbs_renderer (raymond)
   ▼
 data-sources.xml.hbs   ── {{`#each` FEEDS}} → one <bean class="GtfsRealtimeSource"> per feed
   ▼
 OneBusAway runs with N realtime feeds
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 52-52: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-05-23-multi-gtfs-rt-feeds-design.md` around lines
52 - 68, The fenced code block in the spec is missing a language tag (MD040);
update the opening triple-backtick for that diagram to include a language
identifier such as "text" (e.g., change ``` to ```text) so the linter recognizes
it; make the edit in the fenced block that starts with "OBACloud accordion
editor" in the document so the diagram is fenced as text.

Comment on lines +84 to +87
if [ -n "$GTFS_RT_FEEDS" ] && [ "$GTFS_RT_FEEDS" != "[]" ]; then
FEEDS_JSON="$GTFS_RT_FEEDS"
elif [ -n "$TRIP_UPDATES_URL" ] || [ -n "$VEHICLE_POSITIONS_URL" ]; then
# Normalize legacy single-feed vars into a one-element FEEDS array.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

GTFS_RT_FEEDS='[]' behavior is documented incorrectly in this snippet.

This condition makes explicit [] fall through to legacy vars when they exist, which contradicts the intended “explicit empty disables realtime feeds” behavior. Update the snippet/decision text so any set GTFS_RT_FEEDS (including []) wins over legacy normalization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-05-23-multi-gtfs-rt-feeds-design.md` around lines
84 - 87, The current shell snippet treats the literal string "[]" as unset;
change the if-condition to test whether GTFS_RT_FEEDS is set at all (not whether
it is non-empty) so any explicit GTFS_RT_FEEDS value — including "[]" — takes
precedence over legacy normalization; update the branch around GTFS_RT_FEEDS,
FEEDS_JSON, and the legacy vars (TRIP_UPDATES_URL, VEHICLE_POSITIONS_URL) so a
set GTFS_RT_FEEDS always wins and prevents falling back to legacy single-feed
normalization.

@aaronbrethorst aaronbrethorst merged commit ca58d6a into main May 24, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant