Skip to content

Bundle inputs mode for build_bundle.sh + GTFS-RT stop-remap wiring#132

Merged
aaronbrethorst merged 13 commits into
mainfrom
stop-consolidation
Jul 6, 2026
Merged

Bundle inputs mode for build_bundle.sh + GTFS-RT stop-remap wiring#132
aaronbrethorst merged 13 commits into
mainfrom
stop-consolidation

Conversation

@aaronbrethorst

@aaronbrethorst aaronbrethorst commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

Multi-input ("bundle inputs") mode for build_bundle.sh, standalone stop-consolidation support, and GTFS-RT stop-remap wiring. Companion to OneBusAway/obacloud's stop-consolidation PR (which generates the BUNDLE_INPUTS_URL Dockerfiles and publishes the manifest) and OneBusAway/gtfs-merge-service (which uploads the per-feed zips + manifest).

Why: the OBA server must build its bundle from per-feed zips with per-zip defaultAgencyId — a single merged zip stamps every stop with the first agency in agency.txt, collapsing multi-agency regions onto one prefix.

What's included

  • build_bundle.sh refactor (both oba/ and bundler/ copies, byte-identical, now CI-enforced): functions + set -euo pipefail, one-line ERROR: diagnostics. Classic single-zip mode is byte-for-byte compatible (env vars, messages, artifacts, gtfstidy) — pinned by the 48-test unit harness.
  • Multi-input mode (BUNDLE_INPUTS_URL, mutually exclusive with GTFS_URL/GTFS_ZIP_FILENAME/STOP_CONSOLIDATION_URL): downloads bundle-inputs.json (version 1), sha256-verifies each per-feed zip (hard fail — no partial bundles), generates a Spring bundle-context.xml (gtfs-bundles bean with per-feed defaultAgencyId in manifest order; entityReplacementStrategy beans when the manifest carries stopConsolidationUrl), and runs the real transit-data-federation builder with no gtfstidy (legacy parity). Mapping lands at <bundle>/StopConsolidation.txt (hardcoded runtime filename).
  • Standalone STOP_CONSOLIDATION_URL in classic single-zip mode (replacement-beans-only context XML).
  • GTFS-RT remap wiring: each templated GtfsRealtimeSource bean now receives a ConsolidatedStopsModificationStrategy (explicit agencyId = feed's first agency; consolidatedStopsService autowired) — without this, consolidated keepers silently lose member-agency realtime. Verified two ways: Go renderer tests against the real template, and a live docker-compose federation boot (bean graph loads; "missing Stop Consolidation File; skipping" no-op confirmed; zero BeanCreation errors).
  • Tooling/CI: jq added to the bundler image (wget verified already present in the tomcat base); new bundle_script CI job (unit harness + byte-identical-copies guard) and bundle_inputs_integration job — a real-JAR (2.7.1) end-to-end test on fixture feeds that also pins the keeper-missing failure mode: a mapping line whose keeper doesn't exist leaves the replaced stop GONE from the serialized TransitGraph.obj (byte-level check) with a dangling-stopTime ERROR — the serialized-bundle proof of why obacloud excludes keeper-missing rows at publish.
  • README documents the new env vars.

Verification

  • Unit harness: 48/48. Renderer suite green. Integration test run locally end-to-end against the real builder JAR (9/9 assertions).
  • Whole-branch final review traced the full main → validate → download → XML → java seam and cross-checked every manifest field name against the Go producer and the Rails consumer: no drift.

Follow-ups (non-blocking)

  • Contrastive assertion in the valid-mapping integration build (assert the keeper-missing harm log is absent).
  • GTFS_ZIP_FILENAME + STOP_CONSOLIDATION_URL combination test.
  • Decide whether to rm -rf /bundle/inputs after a successful multi-input build (currently baked into the image layer, consistent with single mode's leftover zips).
  • CI image-build dedup between the image and bundle_inputs_integration jobs.
  • Manifest pre-checks (jq empty, non-empty id/defaultAgencyId) for uniform ERROR diagnostics on malformed manifests.

Summary by CodeRabbit

  • New Features
    • Added multi-input bundle building driven by a bundle inputs configuration, including optional stop consolidation mapping.
    • Enhanced template rendering to support stop modification strategies when consolidation data is present.
  • Bug Fixes
    • Added stricter validation to prevent invalid environment combinations and unsupported bundle input configurations.
    • Hardened bundle build execution with safer defaults and clearer failure behavior.
  • Tests
    • Expanded CI with bundle script equivalence checks and end-to-end integration tests using local fixture servers.
    • Added new fixtures and golden outputs to verify generated bundle context, escaping, and stop consolidation behavior.
  • Documentation
    • Documented new bundle input and stop consolidation environment variables and their mutual exclusivity.

No behavior change for single-mode use: same env vars, messages, and
artifacts. Adds set -euo pipefail so a failed wget/gtfstidy/zip/java now
aborts the build instead of silently continuing, and BUNDLE_DIR (default
/bundle) so tests don't need to touch /bundle. Lays the function
structure (validate_mode_env, bundle_mode, fetch_url, run_single_mode,
main) that later multi-input mode tasks build on.
Runs the real transit-data-federation builder jar inside the built oba
image against two 3-stop fixture GTFS feeds in multi-input mode, proving
the manifest/sha256/entity-replacement path end-to-end rather than
through the stubbed unit harness alone. Also pins the keeper-missing
mapping-row failure mode: the replaced stop is dropped from the bundle
either way, but with no valid keeper the builder leaves a dangling
stopTime pointing at a null stop (StopTimeEntriesFactory ERROR) instead
of cleanly remapping it — the concrete harm that justifies obacloud
excluding keeper-missing rows before publish.

Assertions were adjusted from the plan's draft based on what the real
jar actually does: the hypothesized "error replacing entity ...
replacement not found" log line does not appear against the 2.7.1
builder, and an anchored `strings | grep '^ID$'` probe against the
serialized TransitGraph.obj is flaky because the Java serialization
stream has no line terminators. Both were replaced with directly
verified, reproducible checks.
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@aaronbrethorst, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 53 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f902a8c9-c590-4955-ac9f-2fd53606f18d

📥 Commits

Reviewing files that changed from the base of the PR and between 1b92aaa and 320355f.

📒 Files selected for processing (2)
  • bundler/build_bundle.sh
  • oba/build_bundle.sh
📝 Walkthrough

Walkthrough

Refactors the bundle build scripts to support single-input and multi-input manifest-driven builds with stop consolidation mapping, adds template wiring for stop remapping, and expands unit, integration, CI, and documentation coverage.

Changes

Multi-input bundle building

Layer / File(s) Summary
Bundle script refactor
bundler/build_bundle.sh, oba/build_bundle.sh
Adds strict mode, mode validation, manifest handling, XML generation, single/multi dispatch, and guarded entrypoints for both build scripts.
Stop consolidation template wiring
oba/config/onebusaway-transit-data-federation-webapp-data-sources.xml.hbs, oba/config/template_renderer/main_test.go
Adds conditional stop modification strategy wiring and renderer tests for multi-agency and single-agency feeds.
Fixtures and unit tests
bin/build_bundle_test.sh, bin/testdata/build_bundle/*
Adds unit coverage and supporting JSON, XML, and mapping fixtures for mode validation, downloads, XML generation, and end-to-end script behavior.
Integration test, CI, and docs
bin/bundle_inputs_integration_test.sh, bin/testdata/services/make_services_fixture.sh, docker-compose.ci.yml, .github/workflows/test.yaml, README.md
Adds Docker-based integration coverage, a deterministic services fixture, CI fixture-server wiring, new workflow jobs, and deployment parameter documentation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI as GitHub Actions
  participant FixtureServer as python3 http.server
  participant Docker as oba image
  participant BuildScript as build_bundle.sh
  CI->>FixtureServer: start fixture server
  CI->>Docker: run integration test
  Docker->>BuildScript: execute bundle build
  BuildScript->>FixtureServer: fetch bundle inputs and GTFS zip
  BuildScript-->>Docker: build log and artifacts
  Docker-->>CI: test result
Loading
🚥 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 accurately captures the two main changes: bundle inputs mode in build_bundle.sh and GTFS-RT stop-remap wiring.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch stop-consolidation

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (7)
bundler/Dockerfile (1)

45-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add --no-install-recommends and clean the apt cache.

Reduces image size and avoids pulling unneeded recommended packages; also keeps the layer clean.

♻️ Proposed fix
-RUN apt-get update && apt-get install -y jq unzip zip
+RUN apt-get update && apt-get install -y --no-install-recommends jq unzip zip \
+    && rm -rf /var/lib/apt/lists/*
🤖 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 `@bundler/Dockerfile` at line 45, The Dockerfile’s apt install step needs to be
tightened to avoid unnecessary packages and leftover cache. Update the existing
apt-get update && apt-get install -y jq unzip zip command to include
--no-install-recommends, and clean up apt cache in the same layer so the image
stays smaller; make the change in the Dockerfile build step that installs jq,
unzip, and zip.

Source: Linters/SAST tools

bundler/build_bundle.sh (1)

258-263: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Quote the glob so tidied filenames aren't parsed as options.

zip ../gtfs_tidied.zip * lets a filename beginning with - be treated as a flag. Use ./*.

♻️ Proposed fix
-        zip ../gtfs_tidied.zip *
+        zip ../gtfs_tidied.zip ./*
🤖 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 `@bundler/build_bundle.sh` around lines 258 - 263, The zip command in the
gtfs-out packaging block is vulnerable to filenames starting with a dash being
interpreted as options. Update the gtfs_tidied.zip creation logic in the
gtfs-out cd/zip sequence to use a safe glob like ./* instead of * so tidied
filenames are passed as paths, not flags, and keep the GTFS_ZIP_FILENAME
assignment unchanged.

Source: Linters/SAST tools

bin/build_bundle_test.sh (1)

53-59: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

SC2015 pattern (A && B || C) is a false positive here.

pass/fail end in a plain variable assignment (passed=$((...))), which always exits 0, so fail can't accidentally fire from the && branch. Functionally fine as-is; only worth a real if/then/else if you want to silence the linter noise across the file.

🤖 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 `@bin/build_bundle_test.sh` around lines 53 - 59, The shell test assertions use
the A && B || C pattern in the build bundle test script, which triggers SC2015
as a false positive. Keep the current behavior in the test helpers like pass and
fail if you want, but to satisfy the linter noise either rewrite the affected
assertions into explicit if/then/else blocks or otherwise adjust the surrounding
test logic in build_bundle_test.sh so the SC2015 warning is avoided without
changing the test semantics.

Source: Linters/SAST tools

oba/config/onebusaway-transit-data-federation-webapp-data-sources.xml.hbs (1)

80-94: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated branching mirrors the existing agencyId/agencyIds block.

The {{#if this.agencyIds.length}}...{{else if this.agencyId}} structure here duplicates the shape of the block at lines 68-78 just to extract/apply the agency id a second time. Not blocking, but if more per-agency wiring is added later this duplication will compound.

🤖 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 `@oba/config/onebusaway-transit-data-federation-webapp-data-sources.xml.hbs`
around lines 80 - 94, The stopModificationStrategy Handlebars block duplicates
the earlier agencyId/agencyIds branching and repeats the same agency-id
extraction logic. Refactor the repeated logic in one shared place, either by
introducing a reusable partial/helper or by assigning a single normalized
agencyId value before rendering ConsolidatedStopsModificationStrategy, so both
branches in the template use the same source of truth.
bin/bundle_inputs_integration_test.sh (1)

38-41: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Replace fixed sleep 1 with a readiness poll.

A fixed 1-second sleep for the http.server to become ready is a latent flakiness source on loaded/slow CI runners — if the server isn't up yet, the subsequent wget calls inside the container will fail the whole test run.

♻️ Proposed fix: poll instead of fixed sleep
-sleep 1
+for i in $(seq 1 20); do
+    curl -sf "http://127.0.0.1:${PORT}/bundle-inputs.json" >/dev/null 2>&1 && break
+    sleep 0.5
+done
🤖 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 `@bin/bundle_inputs_integration_test.sh` around lines 38 - 41, The test setup
in the bundle inputs integration script uses a fixed sleep after starting the
http.server, which can race on slower CI runners. Replace that sleep with a
readiness poll in the same setup block by waiting until the server is actually
reachable on $PORT before proceeding, using the existing SERVER_PID/PORT/SERVE
flow so the later wget-based checks in the test only run once the server is
ready.
.github/workflows/test.yaml (2)

34-35: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Set persist-credentials: false on checkout steps.

Neither checkout step needs subsequent git push/pull operations, so persisting credentials to disk is unnecessary risk exposure flagged by zizmor's artipacked audit.

🔒 Proposed fix
      - name: Checkout code
        uses: actions/checkout@v4
+       with:
+         persist-credentials: false

Apply to both the bundle_script and bundle_inputs_integration jobs' checkout steps.

Also applies to: 47-48

🤖 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 34 - 35, The checkout steps in the
bundle_script and bundle_inputs_integration jobs should disable credential
persistence. Update each actions/checkout@v4 usage to set persist-credentials to
false, since these workflows do not need later git push/pull access. Use the
checkout step in both jobs as the target location and apply the same setting
consistently.

Source: Linters/SAST tools


43-61: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add explicit permissions: block to bundle_inputs_integration.

The job has no permissions: key and thus inherits the default (broader) GITHUB_TOKEN scope, unlike the sibling bundle_script job which correctly sets contents: read.

🔒 Proposed fix
  bundle_inputs_integration:
    name: Bundle inputs integration (real builder)
    runs-on: ubuntu-latest
+   permissions:
+     contents: read
    steps:
🤖 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 43 - 61, The
bundle_inputs_integration job is missing an explicit permissions block, so it
inherits a broader GITHUB_TOKEN scope than needed. Update the
bundle_inputs_integration workflow job in test.yaml to match the sibling
bundle_script job by adding a permissions section with contents set to read,
keeping the rest of the steps unchanged.

Source: Linters/SAST tools

🤖 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 `@bundler/build_bundle.sh`:
- Around line 150-204: Escape the manifest-derived values before writing them
into bundle-context.xml in generate_bundle_context_xml, since id and
defaultAgencyId are inserted directly into XML attributes. Update the jq reads
for feeds[$i].id and feeds[$i].defaultAgencyId to produce escaped text so
special characters like &, <, and > cannot break the generated XML. Keep the
rest of the XML template logic in build_bundle.sh unchanged and ensure the
generated GtfsBundle entries remain valid for all feed values.

---

Nitpick comments:
In @.github/workflows/test.yaml:
- Around line 34-35: The checkout steps in the bundle_script and
bundle_inputs_integration jobs should disable credential persistence. Update
each actions/checkout@v4 usage to set persist-credentials to false, since these
workflows do not need later git push/pull access. Use the checkout step in both
jobs as the target location and apply the same setting consistently.
- Around line 43-61: The bundle_inputs_integration job is missing an explicit
permissions block, so it inherits a broader GITHUB_TOKEN scope than needed.
Update the bundle_inputs_integration workflow job in test.yaml to match the
sibling bundle_script job by adding a permissions section with contents set to
read, keeping the rest of the steps unchanged.

In `@bin/build_bundle_test.sh`:
- Around line 53-59: The shell test assertions use the A && B || C pattern in
the build bundle test script, which triggers SC2015 as a false positive. Keep
the current behavior in the test helpers like pass and fail if you want, but to
satisfy the linter noise either rewrite the affected assertions into explicit
if/then/else blocks or otherwise adjust the surrounding test logic in
build_bundle_test.sh so the SC2015 warning is avoided without changing the test
semantics.

In `@bin/bundle_inputs_integration_test.sh`:
- Around line 38-41: The test setup in the bundle inputs integration script uses
a fixed sleep after starting the http.server, which can race on slower CI
runners. Replace that sleep with a readiness poll in the same setup block by
waiting until the server is actually reachable on $PORT before proceeding, using
the existing SERVER_PID/PORT/SERVE flow so the later wget-based checks in the
test only run once the server is ready.

In `@bundler/build_bundle.sh`:
- Around line 258-263: The zip command in the gtfs-out packaging block is
vulnerable to filenames starting with a dash being interpreted as options.
Update the gtfs_tidied.zip creation logic in the gtfs-out cd/zip sequence to use
a safe glob like ./* instead of * so tidied filenames are passed as paths, not
flags, and keep the GTFS_ZIP_FILENAME assignment unchanged.

In `@bundler/Dockerfile`:
- Line 45: The Dockerfile’s apt install step needs to be tightened to avoid
unnecessary packages and leftover cache. Update the existing apt-get update &&
apt-get install -y jq unzip zip command to include --no-install-recommends, and
clean up apt cache in the same layer so the image stays smaller; make the change
in the Dockerfile build step that installs jq, unzip, and zip.

In `@oba/config/onebusaway-transit-data-federation-webapp-data-sources.xml.hbs`:
- Around line 80-94: The stopModificationStrategy Handlebars block duplicates
the earlier agencyId/agencyIds branching and repeats the same agency-id
extraction logic. Refactor the repeated logic in one shared place, either by
introducing a reusable partial/helper or by assigning a single normalized
agencyId value before rendering ConsolidatedStopsModificationStrategy, so both
branches in the template use the same source of truth.
🪄 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: 48b29d61-dfea-4707-9bf2-9103de08086e

📥 Commits

Reviewing files that changed from the base of the PR and between ca58d6a and 7298089.

📒 Files selected for processing (16)
  • .github/workflows/test.yaml
  • README.md
  • bin/build_bundle_test.sh
  • bin/bundle_inputs_integration_test.sh
  • bin/testdata/build_bundle/bundle-inputs-no-mapping.json
  • bin/testdata/build_bundle/bundle-inputs.json
  • bin/testdata/build_bundle/golden-context-no-mapping.xml
  • bin/testdata/build_bundle/golden-context-with-mapping.xml
  • bin/testdata/build_bundle/make_fixtures.sh
  • bin/testdata/build_bundle/mapping-keeper-missing.txt
  • bin/testdata/build_bundle/mapping-valid.txt
  • bundler/Dockerfile
  • bundler/build_bundle.sh
  • oba/build_bundle.sh
  • oba/config/onebusaway-transit-data-federation-webapp-data-sources.xml.hbs
  • oba/config/template_renderer/main_test.go

Comment thread bundler/build_bundle.sh
The 'Services with Bundler' job was failing on stops-for-location returning
0 stops. Root cause is environmental, not this PR: the job built from the
live Unitrans feed, and the current feed leaves ~42% of its stops out of
OBA's runtime geospatial STRtree (near-coincident directional stop pairs).
validate.sh picks 'first route's first stop' dynamically, so it fails at
random depending on which stop it lands on. The single-mode build path,
OBA JAR, and gtfstidy output are all unchanged by this PR (verified), so
main would fail the same way today.

Make the job deterministic by building from a small committed GTFS fixture
(7 well-separated stops, all indexed) served on the compose network via a
new docker-compose.ci.yml override, instead of the live feed.

Also address CodeRabbit review feedback:
- Escape manifest-derived ids/agencyIds before writing bundle-context.xml
  (new xml_attr_escape helper) + a unit test; both build_bundle.sh copies
  stay byte-identical.
- zip ../gtfs_tidied.zip ./* (glob safety).
- bundler/Dockerfile: --no-install-recommends + clean apt lists.
- bundle_inputs_integration job: add contents:read permissions;
  persist-credentials:false on the two new jobs' checkouts.
- Integration test: poll for the fixture server instead of a fixed sleep.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
.github/workflows/test.yaml (1)

45-67: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

No job-level timeout guard other than the integration step.

bundle_inputs_integration sets a 20-minute timeout-minutes only on the "Run integration test" step; the "Build oba image" step has no timeout and could hang on a stuck buildx cache/network issue, consuming the full default GitHub Actions job timeout (6h).

🤖 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 45 - 67, The
bundle_inputs_integration job only times out the Run integration test step, so a
hung Build oba image step can still run until the full job timeout. Add an
explicit timeout guard to the job or to the docker buildx build step in
bundle_inputs_integration so the build cannot stall indefinitely, and keep the
existing Run integration test timeout in place.
bin/testdata/services/make_services_fixture.sh (1)

63-63: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Zip won't overwrite stale entries on regeneration.

zip updates an existing archive rather than replacing it; if services-gtfs.zip already exists (e.g. after editing which .txt files are generated), stale entries from a prior run can persist alongside new ones. Since the comment header instructs re-running this script to regenerate the fixture, consider removing the old archive first.

Proposed fix
-(cd "$WORK" && zip -q "$OUT_DIR/services-gtfs.zip" ./*.txt)
+rm -f "$OUT_DIR/services-gtfs.zip"
+(cd "$WORK" && zip -q "$OUT_DIR/services-gtfs.zip" ./*.txt)
🤖 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 `@bin/testdata/services/make_services_fixture.sh` at line 63, The services-gtfs
archive generation step can leave stale entries behind because zip updates an
existing file instead of replacing it. In make_services_fixture.sh, remove any
existing services-gtfs.zip before the zip command runs, so rerunning the fixture
regeneration from the same WORK directory always produces a clean archive. Keep
the fix localized to the archive creation block that uses WORK, OUT_DIR, and
services-gtfs.zip.
🤖 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.

Nitpick comments:
In @.github/workflows/test.yaml:
- Around line 45-67: The bundle_inputs_integration job only times out the Run
integration test step, so a hung Build oba image step can still run until the
full job timeout. Add an explicit timeout guard to the job or to the docker
buildx build step in bundle_inputs_integration so the build cannot stall
indefinitely, and keep the existing Run integration test timeout in place.

In `@bin/testdata/services/make_services_fixture.sh`:
- Line 63: The services-gtfs archive generation step can leave stale entries
behind because zip updates an existing file instead of replacing it. In
make_services_fixture.sh, remove any existing services-gtfs.zip before the zip
command runs, so rerunning the fixture regeneration from the same WORK directory
always produces a clean archive. Keep the fix localized to the archive creation
block that uses WORK, OUT_DIR, and services-gtfs.zip.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 403b9a0a-df53-4c0d-ba32-75c6a6ee5c28

📥 Commits

Reviewing files that changed from the base of the PR and between 7298089 and 1b92aaa.

⛔ Files ignored due to path filters (1)
  • bin/testdata/services/services-gtfs.zip is excluded by !**/*.zip
📒 Files selected for processing (8)
  • .github/workflows/test.yaml
  • bin/build_bundle_test.sh
  • bin/bundle_inputs_integration_test.sh
  • bin/testdata/services/make_services_fixture.sh
  • bundler/Dockerfile
  • bundler/build_bundle.sh
  • docker-compose.ci.yml
  • oba/build_bundle.sh
🚧 Files skipped from review as they are similar to previous changes (5)
  • bundler/Dockerfile
  • bin/build_bundle_test.sh
  • bin/bundle_inputs_integration_test.sh
  • bundler/build_bundle.sh
  • oba/build_bundle.sh

The bash ${//} version produced '<lt;' instead of '&lt;' on Ubuntu's bash
5.2, where a literal & in the replacement means the matched text. Use sed,
which is version-independent. Verified 49/49 in an ubuntu:24.04 container
(bash 5.2.21, GNU sed 4.9).
@aaronbrethorst
aaronbrethorst merged commit 2c19c6a into main Jul 6, 2026
8 checks passed
@aaronbrethorst
aaronbrethorst deleted the stop-consolidation branch July 6, 2026 06:55
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