Redesign serverStatus around structured monitor stats; drop holding-time UI #73
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [develop, main, master] | |
| pull_request: | |
| branches: [develop, main, master] | |
| jobs: | |
| build: | |
| name: Build and test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: 21 | |
| cache: maven | |
| # `mvn verify` packages and runs tests across every module. | |
| # Integration tests stay excluded by the default skip-integration-tests profile. | |
| # | |
| # Picks up the Phase 0 migration-safety tests: | |
| # * transitclockApi/src/test/ — GTFS-RT golden fixtures (0.1) | |
| # and JAX-RS resource smoke (0.2). | |
| # * transitclockWebapp/src/test/ — JSP taglib URI audit (0.4). | |
| - name: Build and run unit tests | |
| run: mvn -B -ntp verify | |
| # Pipeline tests live in the transitclockPipelineTests module and are | |
| # opt-in via the include-pipeline-tests profile. They boot a real Core | |
| # against an in-memory HSQL database populated with a small WMATA GTFS | |
| # fixture and exercise matcher/generator behavior end-to-end. Separate | |
| # step so failures are attributed distinctly from unit-test failures. | |
| # | |
| # Picks up the Phase 0 migration-safety tests: | |
| # * pipelinetests/persistence/ — Hibernate Criteria pinning + entity | |
| # round-trip (0.3). | |
| # * pipelinetests/rmi/ — RMI server boot + client smoke (0.5). | |
| - name: Run pipeline tests | |
| run: mvn -B -ntp -pl transitclockPipelineTests -am -P include-pipeline-tests test | |
| # Integration tests live in transitclockIntegration and are the | |
| # heaviest tier — full AVL-CSV replays through a booted Core. Opt-in | |
| # via include-integration-tests. Kept as its own step so a failure | |
| # here is attributed distinctly from the unit and pipeline tiers. | |
| - name: Run integration tests | |
| run: mvn -B -ntp -pl transitclockIntegration -am -P include-integration-tests test | |
| # Builds the tomcat-runtime image, boots it, and pokes the JAX-RS | |
| # servlet to exercise the layers that mvn verify cannot reach: | |
| # | |
| # 1. JVM class-loader. The WARs are compiled with --release 21, so | |
| # a Tomcat base image on JDK <21 produces UnsupportedClassVersionError | |
| # at servlet load. | |
| # 2. Servlet namespace. Phase B migrated the WARs to jakarta.servlet, | |
| # so Tomcat 9 (javax.servlet) won't deploy them and Tomcat 11 | |
| # (jakarta.servlet) is required. Catches a regression of either | |
| # a WAR module that didn't get the namespace migration or a | |
| # Dockerfile bump back to Tomcat 9. | |
| # 3. Jersey JAX-RS init. Jersey's annotation discovery is lazy — it | |
| # fires on the first request to the servlet, not at deploy time. | |
| # A Jersey/ASM combo that can't read the WAR's class file level | |
| # throws "Unsupported class file major version N" out of the ASM | |
| # ClassReader on first request; this step also flags any | |
| # Servlet.init() failure on org.transitclock.api.ApiApplication. | |
| # | |
| # mvn verify never boots Tomcat, and the JaxRsResourceSmokeBase tests | |
| # register resources directly on a Jersey ResourceConfig (no | |
| # classpath scan), so neither path catches these. This step fails | |
| # fast on all three. | |
| - name: Smoke-test docker tomcat-runtime image | |
| run: | | |
| set -euo pipefail | |
| docker build -f docker/Dockerfile --target tomcat-runtime -t transitclock-tomcat-smoke . | |
| # Boot Tomcat with no Core/RMI backend (we only need Jersey to | |
| # initialize the JAX-RS application; downstream RMI errors are | |
| # expected and ignored). Bind 8080 to the host so curl can hit it. | |
| cid=$(docker run -d --rm -p 8080:8080 -e TRANSITCLOCK_APIKEY=ci-smoke transitclock-tomcat-smoke catalina.sh run) | |
| trap 'docker logs "$cid" 2>&1 | tail -100; docker stop "$cid" >/dev/null 2>&1 || true' EXIT | |
| fail() { echo "FAIL: $1"; exit 1; } | |
| # Phase 1: wait for both WARs to deploy (or for a class-version | |
| # error to appear). 60s budget; both signals normally arrive in <30s. | |
| for i in $(seq 1 60); do | |
| logs=$(docker logs "$cid" 2>&1 || true) | |
| if echo "$logs" | grep -q "UnsupportedClassVersionError"; then | |
| fail "tomcat cannot load WAR classes (class-file/runtime mismatch)" | |
| fi | |
| api_done=$(echo "$logs" | grep -cE "Deployment of web application archive .*api\.war.* has finished" || true) | |
| web_done=$(echo "$logs" | grep -cE "Deployment of web application archive .*web\.war.* has finished" || true) | |
| if [ "$api_done" -gt 0 ] && [ "$web_done" -gt 0 ]; then | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| [ "${api_done:-0}" -gt 0 ] || fail "api.war did not finish deploying within 60s" | |
| [ "${web_done:-0}" -gt 0 ] || fail "web.war did not finish deploying within 60s" | |
| echo "OK: both WARs deployed without class-version errors" | |
| # Phase 2: hit the JAX-RS servlet so Jersey actually initializes | |
| # its ResourceConfig and the ASM annotation scanner walks the | |
| # WAR classes. Any HTTP response (200/4xx/500 with a | |
| # missing-config message) is fine — what we want to catch is | |
| # Jersey itself dying with "Unsupported class file major version | |
| # N" out of jersey.repackaged.org.objectweb.asm.ClassReader. | |
| # Tomcat reports that class as a Servlet.init() exception with | |
| # the trace inlined into the HTTP response *body*, not into | |
| # stderr, so grep the response — checking docker logs alone | |
| # would miss it. | |
| resp_body=/tmp/smoke-resp.html | |
| for i in $(seq 1 30); do | |
| if curl -sS -o "$resp_body" --max-time 5 "http://localhost:8080/api/v1/key/ci-smoke/agency/1/command/routes"; then | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| [ -s "$resp_body" ] || fail "Jersey servlet did not respond at all within 30s" | |
| if grep -q "Unsupported class file major version" "$resp_body" \ | |
| || docker logs "$cid" 2>&1 | grep -q "Unsupported class file major version"; then | |
| echo "--- offending response body ---" | |
| head -c 4000 "$resp_body" | |
| fail "Jersey ASM scanner cannot read WAR classes (Jersey/ASM vs class-file mismatch)" | |
| fi | |
| if grep -q "Servlet.init() for servlet \[org.transitclock.api.ApiApplication\] threw" "$resp_body"; then | |
| echo "--- offending response body ---" | |
| head -c 4000 "$resp_body" | |
| fail "Jersey JAX-RS application failed to initialize (ApiApplication.init threw)" | |
| fi | |
| echo "OK: Jersey JAX-RS servlet initialized without ASM class-file errors" | |
| - name: Upload JaCoCo aggregate report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: jacoco-aggregate-report | |
| path: coverage-report/target/site/jacoco-aggregate/ | |
| if-no-files-found: ignore | |
| - name: Upload surefire reports | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: surefire-reports | |
| path: '**/target/surefire-reports/' | |
| if-no-files-found: ignore |